diff --git a/codegen/configs/policy_client_v1.yaml b/codegen/configs/policy_client_v1.yaml new file mode 100644 index 0000000000..5bd811ccf1 --- /dev/null +++ b/codegen/configs/policy_client_v1.yaml @@ -0,0 +1,11 @@ +package: client +generate: + echo-server: false + client: true + models: true + strict-server: true +output-options: + skip-prune: true + exclude-schemas: + - PresentationDefinition + - PresentationSubmission diff --git a/docs/_static/policy/v1.yaml b/docs/_static/policy/v1.yaml index 3c8bd90878..e221b36cef 100644 --- a/docs/_static/policy/v1.yaml +++ b/docs/_static/policy/v1.yaml @@ -5,10 +5,10 @@ info: servers: - url: "http://localhost:1323" paths: - /{did}/presentation_definition: + /presentation_definition: parameters: - - name: did - in: path + - name: authorizer + in: query description: URLEncoded DID. required: true example: did:web:example.com:1 @@ -52,7 +52,7 @@ paths: When an access token is used to request a resource, the resource server needs to know if the access token grants access to the requested resource. The resource server will send a request to the policy backend to check if the access token grants access to the requested resource. All cryptographic and presentation exchange validations have already been done by the caller. - operationId: "authorized" + operationId: "checkAuthorized" tags: - policy requestBody: @@ -104,7 +104,7 @@ components: type: string presentation_submission: description: The presentation submission that was used to request the access token. - type: object + $ref: '#/components/schemas/PresentationSubmission' vps: description: | The verifiable presentations that were used to request the access token. @@ -127,3 +127,8 @@ components: A presentation definition is a JSON object that describes the desired verifiable credentials and presentation formats. Specified at https://identity.foundation/presentation-exchange/spec/v2.0.0/ A JSON schema is available at https://identity.foundation/presentation-exchange/#json-schema + PresentationSubmission: + description: | + A presentation submission is a JSON object that maps requirements from the Presentation Definition to the verifiable presentations that were used to request an access token. + Specified at https://identity.foundation/presentation-exchange/spec/v2.0.0/ + A JSON schema is available at https://identity.foundation/presentation-exchange/#json-schema diff --git a/makefile b/makefile index 6f6efae5be..31b5d65065 100644 --- a/makefile +++ b/makefile @@ -68,6 +68,7 @@ gen-api: oapi-codegen --config codegen/configs/auth_iam.yaml docs/_static/auth/iam.yaml | gofmt > auth/api/iam/generated.go oapi-codegen --config codegen/configs/didman_v1.yaml docs/_static/didman/v1.yaml | gofmt > didman/api/v1/generated.go oapi-codegen --config codegen/configs/crypto_store_client.yaml https://raw.githubusercontent.com/nuts-foundation/secret-store-api/main/nuts-storage-api-v1.yaml | gofmt > crypto/storage/external/generated.go + oapi-codegen --config codegen/configs/policy_client_v1.yaml docs/_static/policy/v1.yaml | gofmt > policy/api/v1/client/generated.go gen-protobuf: protoc --go_out=paths=source_relative:network -I network network/transport/v2/protocol.proto diff --git a/policy/api/v1/client/client.go b/policy/api/v1/client/client.go new file mode 100644 index 0000000000..1e8b21b1d8 --- /dev/null +++ b/policy/api/v1/client/client.go @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package client + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "github.com/nuts-foundation/go-did/did" + "io" + "net/http" + "net/url" + "time" + + "github.com/nuts-foundation/nuts-node/core" + "github.com/nuts-foundation/nuts-node/vcr/pe" +) + +// HTTPClient holds the server address and other basic settings for the http client +type HTTPClient struct { + strictMode bool + httpClient core.HTTPRequestDoer +} + +// NewHTTPClient creates a new api client. +func NewHTTPClient(strictMode bool, timeout time.Duration, tlsConfig *tls.Config) HTTPClient { + return HTTPClient{ + strictMode: strictMode, + httpClient: core.NewStrictHTTPClient(strictMode, timeout, tlsConfig), + } +} + +// PresentationDefinition retrieves the presentation definition from the presentation definition endpoint for the given scope and . +func (hb HTTPClient) PresentationDefinition(ctx context.Context, policyEndpoint string, authorizer did.DID, scopes string) (*pe.PresentationDefinition, error) { + presentationDefinitionURL, err := core.ParsePublicURL(policyEndpoint, hb.strictMode) + if err != nil { + return nil, err + } + presentationDefinitionURL.Path = fmt.Sprintf("%s/presentation_definition", presentationDefinitionURL.Path) + presentationDefinitionURL.RawQuery = url.Values{"scope": []string{scopes}, "authorizer": []string{authorizer.String()}}.Encode() + + // create a GET request with query params + request, err := http.NewRequestWithContext(ctx, http.MethodGet, presentationDefinitionURL.String(), nil) + if err != nil { + return nil, err + } + response, err := hb.httpClient.Do(request.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to call endpoint: %w", err) + } + if httpErr := core.TestResponseCode(http.StatusOK, response); httpErr != nil { + return nil, httpErr + } + + var presentationDefinition pe.PresentationDefinition + var data []byte + + if data, err = io.ReadAll(response.Body); err != nil { + return nil, fmt.Errorf("unable to read response: %w", err) + } + if err = json.Unmarshal(data, &presentationDefinition); err != nil { + return nil, fmt.Errorf("unable to unmarshal response: %w", err) + } + + return &presentationDefinition, nil +} diff --git a/policy/api/v1/client/client_test.go b/policy/api/v1/client/client_test.go new file mode 100644 index 0000000000..94749da224 --- /dev/null +++ b/policy/api/v1/client/client_test.go @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package client + +import ( + "context" + "encoding/json" + "github.com/nuts-foundation/go-did/did" + http2 "github.com/nuts-foundation/nuts-node/test/http" + "github.com/nuts-foundation/nuts-node/vcr/pe" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHTTPClient_PresentationDefinition(t *testing.T) { + ctx := context.Background() + authorizer := did.MustParseDID("did:web:example.com") + definition := pe.PresentationDefinition{ + Id: "123", + } + + t.Run("ok", func(t *testing.T) { + var capturedRequest *http.Request + handler := func(writer http.ResponseWriter, request *http.Request) { + switch request.URL.Path { + case "/presentation_definition": + capturedRequest = request + writer.WriteHeader(http.StatusOK) + bytes, _ := json.Marshal(definition) + writer.Write(bytes) + } + writer.WriteHeader(http.StatusNotFound) + } + tlsServer, client := testServerAndClient(t, http.HandlerFunc(handler)) + + response, err := client.PresentationDefinition(ctx, tlsServer.URL, authorizer, "test") + + require.NoError(t, err) + require.NotNil(t, definition) + assert.Equal(t, definition, *response) + require.NotNil(t, capturedRequest) + assert.Equal(t, "GET", capturedRequest.Method) + assert.Equal(t, "/presentation_definition", capturedRequest.URL.Path) + // check query params + require.NotNil(t, capturedRequest.URL.Query().Get("scope")) + assert.Equal(t, "test", capturedRequest.URL.Query().Get("scope")) + require.NotNil(t, capturedRequest.URL.Query().Get("authorizer")) + assert.Equal(t, authorizer.String(), capturedRequest.URL.Query().Get("authorizer")) + }) + t.Run("error - not found", func(t *testing.T) { + handler := http2.Handler{StatusCode: http.StatusNotFound} + tlsServer, client := testServerAndClient(t, &handler) + + response, err := client.PresentationDefinition(ctx, tlsServer.URL, authorizer, "test") + + require.Error(t, err) + assert.EqualError(t, err, "server returned HTTP 404 (expected: 200)") + assert.Nil(t, response) + }) + t.Run("error - invalid URL", func(t *testing.T) { + handler := http2.Handler{StatusCode: http.StatusNotFound} + _, client := testServerAndClient(t, &handler) + + response, err := client.PresentationDefinition(ctx, ":", authorizer, "test") + + require.Error(t, err) + assert.EqualError(t, err, "parse \":\": missing protocol scheme") + assert.Nil(t, response) + }) + t.Run("error - invalid response", func(t *testing.T) { + handler := http2.Handler{StatusCode: http.StatusOK, ResponseData: "}"} + tlsServer, client := testServerAndClient(t, &handler) + + response, err := client.PresentationDefinition(ctx, tlsServer.URL, authorizer, "test") + + require.Error(t, err) + assert.EqualError(t, err, "unable to unmarshal response: invalid character '}' looking for beginning of value") + assert.Nil(t, response) + }) +} + +func testServerAndClient(t *testing.T, handler http.Handler) (*httptest.Server, *HTTPClient) { + tlsServer := http2.TestTLSServer(t, handler) + return tlsServer, &HTTPClient{ + httpClient: tlsServer.Client(), + } +} diff --git a/policy/api/v1/client/generated.go b/policy/api/v1/client/generated.go new file mode 100644 index 0000000000..ad20e5c8cd --- /dev/null +++ b/policy/api/v1/client/generated.go @@ -0,0 +1,455 @@ +// Package client provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0 DO NOT EDIT. +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" +) + +// AuthorizedRequest The request contains all params involved with the request. +// It might be the case that the caller mapped credential fields to additional params. +type AuthorizedRequest struct { + // Audience The audience of the access token. This is the identifier (DID) of the authorizer and issuer of the access token. + Audience string `json:"audience"` + + // ClientId The client ID of the client that requested the resource (DID). + ClientId string `json:"client_id"` + + // PresentationSubmission A presentation submission is a JSON object that maps requirements from the Presentation Definition to the verifiable presentations that were used to request an access token. + // Specified at https://identity.foundation/presentation-exchange/spec/v2.0.0/ + // A JSON schema is available at https://identity.foundation/presentation-exchange/#json-schema + PresentationSubmission PresentationSubmission `json:"presentation_submission"` + + // RequestMethod The method of the resource request. + RequestMethod string `json:"request_method"` + + // RequestUrl The URL of the resource request. + RequestUrl string `json:"request_url"` + + // Scope The scope used in the authorization request. + Scope string `json:"scope"` + + // Vps The verifiable presentations that were used to request the access token. + // The verifiable presentations could be in JWT format or in JSON format. + Vps []interface{} `json:"vps"` +} + +// AuthorizedResponse The response indicates if the access token grants access to the requested resource. +// If the access token grants access, the response will be 200 with a boolean value set to true. +// If the access token does not grant access, the response will be 200 with a boolean value set to false. +type AuthorizedResponse struct { + // Authorized Indicates if the access token grants access to the requested resource. + Authorized bool `json:"authorized"` +} + +// PresentationDefinitionParams defines parameters for PresentationDefinition. +type PresentationDefinitionParams struct { + // Authorizer URLEncoded DID. + Authorizer string `form:"authorizer" json:"authorizer"` + + // Scope This is the scope used in the OpenID4VP authorization request. + // It is a space separated list of scopes. + Scope string `form:"scope" json:"scope"` +} + +// CheckAuthorizedJSONRequestBody defines body for CheckAuthorized for application/json ContentType. +type CheckAuthorizedJSONRequestBody = AuthorizedRequest + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // CheckAuthorizedWithBody request with any body + CheckAuthorizedWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + CheckAuthorized(ctx context.Context, body CheckAuthorizedJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PresentationDefinition request + PresentationDefinition(ctx context.Context, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) CheckAuthorizedWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCheckAuthorizedRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CheckAuthorized(ctx context.Context, body CheckAuthorizedJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCheckAuthorizedRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PresentationDefinition(ctx context.Context, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPresentationDefinitionRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewCheckAuthorizedRequest calls the generic CheckAuthorized builder with application/json body +func NewCheckAuthorizedRequest(server string, body CheckAuthorizedJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCheckAuthorizedRequestWithBody(server, "application/json", bodyReader) +} + +// NewCheckAuthorizedRequestWithBody generates requests for CheckAuthorized with any type of body +func NewCheckAuthorizedRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/authorized") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPresentationDefinitionRequest generates requests for PresentationDefinition +func NewPresentationDefinitionRequest(server string, params *PresentationDefinitionParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/presentation_definition") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "authorizer", runtime.ParamLocationQuery, params.Authorizer); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "scope", runtime.ParamLocationQuery, params.Scope); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // CheckAuthorizedWithBodyWithResponse request with any body + CheckAuthorizedWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CheckAuthorizedResponse, error) + + CheckAuthorizedWithResponse(ctx context.Context, body CheckAuthorizedJSONRequestBody, reqEditors ...RequestEditorFn) (*CheckAuthorizedResponse, error) + + // PresentationDefinitionWithResponse request + PresentationDefinitionWithResponse(ctx context.Context, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*PresentationDefinitionResponse, error) +} + +type CheckAuthorizedResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *AuthorizedResponse +} + +// Status returns HTTPResponse.Status +func (r CheckAuthorizedResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CheckAuthorizedResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PresentationDefinitionResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *PresentationDefinition +} + +// Status returns HTTPResponse.Status +func (r PresentationDefinitionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PresentationDefinitionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// CheckAuthorizedWithBodyWithResponse request with arbitrary body returning *CheckAuthorizedResponse +func (c *ClientWithResponses) CheckAuthorizedWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CheckAuthorizedResponse, error) { + rsp, err := c.CheckAuthorizedWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCheckAuthorizedResponse(rsp) +} + +func (c *ClientWithResponses) CheckAuthorizedWithResponse(ctx context.Context, body CheckAuthorizedJSONRequestBody, reqEditors ...RequestEditorFn) (*CheckAuthorizedResponse, error) { + rsp, err := c.CheckAuthorized(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCheckAuthorizedResponse(rsp) +} + +// PresentationDefinitionWithResponse request returning *PresentationDefinitionResponse +func (c *ClientWithResponses) PresentationDefinitionWithResponse(ctx context.Context, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*PresentationDefinitionResponse, error) { + rsp, err := c.PresentationDefinition(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParsePresentationDefinitionResponse(rsp) +} + +// ParseCheckAuthorizedResponse parses an HTTP response from a CheckAuthorizedWithResponse call +func ParseCheckAuthorizedResponse(rsp *http.Response) (*CheckAuthorizedResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CheckAuthorizedResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest AuthorizedResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParsePresentationDefinitionResponse parses an HTTP response from a PresentationDefinitionWithResponse call +func ParsePresentationDefinitionResponse(rsp *http.Response) (*PresentationDefinitionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PresentationDefinitionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PresentationDefinition + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} diff --git a/policy/api/v1/client/types.go b/policy/api/v1/client/types.go new file mode 100644 index 0000000000..b9aad6779b --- /dev/null +++ b/policy/api/v1/client/types.go @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package client + +import "github.com/nuts-foundation/nuts-node/vcr/pe" + +// PresentationDefinition is a type alias for the PresentationDefinition from the nuts-node/vcr/pe package. +type PresentationDefinition = pe.PresentationDefinition + +// PresentationSubmission is a type alias for the PresentationSubmission from the nuts-node/vcr/pe package. +type PresentationSubmission = pe.PresentationSubmission