diff --git a/acceptance/acceptance_suite_test.go b/acceptance/acceptance_suite_test.go index ce72c02..addbe83 100644 --- a/acceptance/acceptance_suite_test.go +++ b/acceptance/acceptance_suite_test.go @@ -2,9 +2,11 @@ package acceptance_test import ( "bytes" + "encoding/json" "log" "net" "net/http" + "net/url" "os" "source-score/pkg/api" "source-score/pkg/helpers" @@ -32,10 +34,14 @@ const ( ) var ( - baseUrl string - commonHeaders = map[string]string{"X-API-Key": "demo-api-key"} - client = &http.Client{Timeout: 10 * time.Second} - serverPort = os.Getenv("PORT") + baseUrl string + token string + + commonHeaders = map[string]string{ + "Client-ID": "ac-tests", + } + client = &http.Client{Timeout: 10 * time.Second} + serverPort = os.Getenv("PORT") sourceInput1 = api.SourceInput{ Name: "Sample Source 1", Summary: "Sample summary", @@ -130,6 +136,25 @@ func TestSourceScore(t *testing.T) { baseUrl = "http://" + helpers.Localhost + ":" + serverPort + var _ = BeforeSuite(func() { + endpoint, err := url.JoinPath(baseUrl, "/auth/token") + Expect(err).To(BeNil()) + + resp, err := doRequest(http.MethodPost, endpoint, nil) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + defer resp.Body.Close() + var tokenResp map[string]string + err = json.NewDecoder(resp.Body).Decode(&tokenResp) + Expect(err).To(BeNil()) + + token = tokenResp["token"] + Expect(token).ToNot(Equal("")) + + commonHeaders["Authorization"] = "Bearer " + token + }) + RegisterFailHandler(Fail) RunSpecs(t, "SourceScore Acceptance Test Suite") } diff --git a/acceptance/auth_test.go b/acceptance/auth_test.go new file mode 100644 index 0000000..901edae --- /dev/null +++ b/acceptance/auth_test.go @@ -0,0 +1,150 @@ +package acceptance_test + +import ( + "encoding/json" + "net/http" + "net/url" + "source-score/pkg/middleware" + "time" + + "github.com/golang-jwt/jwt/v5" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var ( + protectedEndpoint string + err error +) + +var _ = Describe("Auth middleware tests", Ordered, func() { + protectedEndpoint, err = url.JoinPath(baseUrl, "/api/v1/claims") + Expect(err).To(BeNil()) + + Context("Validation tests", func() { + It("should reject requests without the client id header", func() { + resp, err := doRequestWithHeaders(http.MethodGet, protectedEndpoint, map[string]string{ + "Authorization": commonHeaders["Authorization"], + }) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["msg"]).To(ContainSubstring("Client-ID is required")) + }) + + It("should reject requests without the authorization header", func() { + resp, err := doRequestWithHeaders(http.MethodGet, protectedEndpoint, map[string]string{ + middleware.ClientIDHeader: commonHeaders[middleware.ClientIDHeader], + }) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["error"]).To(ContainSubstring("missing token")) + }) + + It("should reject requests with an invalid jwt token", func() { + resp, err := doRequestWithAuthToken(http.MethodGet, protectedEndpoint, "invalid.jwt.token") + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["error"]).To(ContainSubstring("invalid or expired token")) + }) + + It("should reject requests with an expired jwt token", func() { + token := signAcceptanceToken(jwt.RegisteredClaims{ + Audience: []string{commonHeaders[middleware.ClientIDHeader]}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now().Add(-2 * time.Hour)), + Issuer: middleware.TokenIssuer, + }) + + resp, err := doRequestWithAuthToken(http.MethodGet, protectedEndpoint, token) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["error"]).To(ContainSubstring("invalid or expired token")) + }) + + It("should reject requests when jwt audience does not match the client id header", func() { + token := signAcceptanceToken(jwt.RegisteredClaims{ + Audience: []string{"another-client"}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + Issuer: middleware.TokenIssuer, + }) + + resp, err := doRequestWithAuthToken(http.MethodGet, protectedEndpoint, token) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["error"]).To(ContainSubstring("invalid or expired token")) + }) + + It("should reject requests when jwt issuer does not match the configured token issuer", func() { + token := signAcceptanceToken(jwt.RegisteredClaims{ + Audience: []string{commonHeaders[middleware.ClientIDHeader]}, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + Issuer: "another-issuer", + }) + + resp, err := doRequestWithAuthToken(http.MethodGet, protectedEndpoint, token) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + + defer resp.Body.Close() + var respBody map[string]string + err = json.NewDecoder(resp.Body).Decode(&respBody) + Expect(err).To(BeNil()) + Expect(respBody["error"]).To(ContainSubstring("invalid or expired token")) + }) + }) +}) + +func doRequestWithAuthToken(method, endpoint, authToken string) (*http.Response, error) { + return doRequestWithHeaders(method, endpoint, map[string]string{ + middleware.ClientIDHeader: commonHeaders[middleware.ClientIDHeader], + "Authorization": "Bearer " + authToken, + }) +} + +func doRequestWithHeaders(method, endpoint string, headers map[string]string) (*http.Response, error) { + req, err := http.NewRequest(method, endpoint, nil) + if err != nil { + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + return client.Do(req) +} + +func signAcceptanceToken(claims jwt.RegisteredClaims) string { + // signing secret is the same as that hardcoded for app container in docker compose + signedToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte("default-secret-string")) + Expect(err).To(BeNil()) + + return signedToken +} diff --git a/acceptance/compose.yaml b/acceptance/compose.yaml index a86a8ac..a4af27b 100644 --- a/acceptance/compose.yaml +++ b/acceptance/compose.yaml @@ -24,8 +24,8 @@ services: - APP_USER_PASSWORD=sourcescore - PG_HOST=database - SUPER_USER_PASSWORD=sourcescore - - API_KEY=demo-api-key - RATE_LIMIT_DISABLED=true + - JWT_SECRET=default-secret-string ports: - "8080:8080" adminer: diff --git a/api/source-score.yaml b/api/source-score.yaml index e690124..bbca590 100644 --- a/api/source-score.yaml +++ b/api/source-score.yaml @@ -5,13 +5,45 @@ info: version: 0.1.0 servers: - - url: / - description: Current host +- url: / + description: Current host security: - - ApiKeyAuth: [] +- BearerAuth: [] paths: + /auth/token: + post: + summary: Obtain API token + description: Authenticates a user and returns an API token for subsequent requests + tags: + - authentication + security: [] + operationId: getAuthToken + parameters: + - $ref: '#/components/parameters/ClientIDHeader' + responses: + 200: + description: Authentication successful, token returned + content: + application/json: + schema: + type: object + properties: + token: + type: string + description: API token to be used in the Authorization header for authenticated requests + 401: + description: Authentication failed, invalid client ID + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error message + /api/v1/sources: get: summary: Get all sources @@ -19,6 +51,8 @@ paths: tags: - sources operationId: getSources + parameters: + - $ref: '#/components/parameters/ClientIDHeader' responses: 200: description: List of sources retrieved successfully @@ -36,6 +70,8 @@ paths: tags: - sources operationId: updateAllScores + parameters: + - $ref: '#/components/parameters/ClientIDHeader' responses: 202: description: Score update process initiated successfully @@ -49,6 +85,8 @@ paths: tags: - source operationId: postSource + parameters: + - $ref: '#/components/parameters/ClientIDHeader' requestBody: required: true content: @@ -75,6 +113,7 @@ paths: - source operationId: getSource parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -95,9 +134,10 @@ paths: summary: Update source fields description: Partially updates a source. Only provided fields will be updated. Empty values are not allowed. tags: - - source + - source operationId: patchSource parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -125,6 +165,7 @@ paths: - source operationId: deleteSource parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -145,6 +186,7 @@ paths: - claims operationId: getClaimsBySourceDigest parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -169,6 +211,7 @@ paths: - claims operationId: getClaims parameters: + - $ref: '#/components/parameters/ClientIDHeader' - name: checked in: query schema: @@ -193,6 +236,8 @@ paths: tags: - claims operationId: verifyAllClaims + parameters: + - $ref: '#/components/parameters/ClientIDHeader' responses: 202: description: Claim verification process initiated successfully @@ -206,6 +251,8 @@ paths: tags: - claim operationId: postClaim + parameters: + - $ref: '#/components/parameters/ClientIDHeader' requestBody: required: true content: @@ -228,6 +275,7 @@ paths: - claim operationId: getClaim parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -251,6 +299,7 @@ paths: - claim operationId: deleteClaim parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -270,6 +319,7 @@ paths: - claim operationId: patchClaim parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -297,6 +347,7 @@ paths: - claim operationId: verifyClaim parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -325,6 +376,7 @@ paths: - proofs operationId: getProofsByClaimDigest parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -348,6 +400,8 @@ paths: tags: - proofs operationId: getProofs + parameters: + - $ref: '#/components/parameters/ClientIDHeader' responses: 200: description: List of proofs retrieved successfully @@ -365,6 +419,8 @@ paths: tags: - proof operationId: postProof + parameters: + - $ref: '#/components/parameters/ClientIDHeader' requestBody: required: true content: @@ -387,6 +443,7 @@ paths: - proof operationId: getProof parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -410,6 +467,7 @@ paths: - proof operationId: deleteProof parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -426,9 +484,10 @@ paths: summary: Update proof reviewer description: Updates the reviewer identifier for a proof. Empty values and spaces are not allowed. tags: - - proof + - proof operationId: patchProof parameters: + - $ref: '#/components/parameters/ClientIDHeader' - in: path name: uriDigest required: true @@ -451,10 +510,19 @@ paths: components: securitySchemes: - ApiKeyAuth: - type: apiKey + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + parameters: + ClientIDHeader: + name: Client-ID in: header - name: X-API-Key + required: true + schema: + type: string + description: Client identifier for authentication schemas: SourceInput: diff --git a/cmd/app/main.go b/cmd/app/main.go index 7bbae61..5df0353 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -79,7 +79,7 @@ func main() { server.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://satyalens.github.io"}, AllowMethods: []string{"GET", "OPTIONS"}, - AllowHeaders: []string{"Content-Type", "X-API-Key"}, + AllowHeaders: []string{"Content-Type", "Authorization", "Client-ID"}, AllowCredentials: true, })) @@ -99,12 +99,17 @@ func main() { serverOpts.Middlewares = append(serverOpts.Middlewares, api.MiddlewareFunc(middleware.RateLimiterMiddleware(10, 20))) } - // Secure with API key if the env var is set - if key, ok := os.LookupEnv("API_KEY"); ok { - slog.Info("API Key found, securing the API") - // server.Use(middleware.APIKeyMiddleware(key)) - serverOpts.Middlewares = append(serverOpts.Middlewares, api.MiddlewareFunc(middleware.APIKeyMiddleware(key))) - } + authTokenMiddleware := middleware.AuthTokenMiddleware(conf.Cfg.JwtSecret) + serverOpts.Middlewares = append( + serverOpts.Middlewares, + func(c *gin.Context) { + if c.FullPath() == "/auth/token" { + c.Next() + } else { + authTokenMiddleware(c) + } + }, + ) api.RegisterHandlersWithOptions( server, diff --git a/go.mod b/go.mod index 1f2a9ce..cc7b499 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.25.0 require ( github.com/gin-gonic/gin v1.12.0 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/golangci/golangci-lint/v2 v2.10.1 github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 github.com/oapi-codegen/runtime v1.3.0 diff --git a/go.sum b/go.sum index 5e2e436..f7b5330 100644 --- a/go.sum +++ b/go.sum @@ -283,6 +283,8 @@ github.com/godoc-lint/godoc-lint v0.11.2/go.mod h1:iVpGdL1JCikNH2gGeAn3Hh+AgN5Gx github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= diff --git a/pkg/api/server.gen.go b/pkg/api/server.gen.go index 1a08df9..7477098 100644 --- a/pkg/api/server.gen.go +++ b/pkg/api/server.gen.go @@ -12,7 +12,7 @@ import ( ) const ( - ApiKeyAuthScopes = "ApiKeyAuth.Scopes" + BearerAuthScopes = "BearerAuth.Scopes" ) // Claim Complete claim entity with verification status @@ -177,9 +177,135 @@ type SourcePatchInput struct { Tags *string `json:"tags" validate:"omitnil,nospace,nonempty"` } +// ClientIDHeader defines model for ClientIDHeader. +type ClientIDHeader = string + +// PostClaimParams defines parameters for PostClaim. +type PostClaimParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// DeleteClaimParams defines parameters for DeleteClaim. +type DeleteClaimParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetClaimParams defines parameters for GetClaim. +type GetClaimParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// PatchClaimParams defines parameters for PatchClaim. +type PatchClaimParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// VerifyClaimParams defines parameters for VerifyClaim. +type VerifyClaimParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetProofsByClaimDigestParams defines parameters for GetProofsByClaimDigest. +type GetProofsByClaimDigestParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + // GetClaimsParams defines parameters for GetClaims. type GetClaimsParams struct { Checked *bool `form:"checked,omitempty" json:"checked,omitempty"` + + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// VerifyAllClaimsParams defines parameters for VerifyAllClaims. +type VerifyAllClaimsParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// PostProofParams defines parameters for PostProof. +type PostProofParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// DeleteProofParams defines parameters for DeleteProof. +type DeleteProofParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetProofParams defines parameters for GetProof. +type GetProofParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// PatchProofParams defines parameters for PatchProof. +type PatchProofParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetProofsParams defines parameters for GetProofs. +type GetProofsParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// PostSourceParams defines parameters for PostSource. +type PostSourceParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// DeleteSourceParams defines parameters for DeleteSource. +type DeleteSourceParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetSourceParams defines parameters for GetSource. +type GetSourceParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// PatchSourceParams defines parameters for PatchSource. +type PatchSourceParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetClaimsBySourceDigestParams defines parameters for GetClaimsBySourceDigest. +type GetClaimsBySourceDigestParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetSourcesParams defines parameters for GetSources. +type GetSourcesParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// UpdateAllScoresParams defines parameters for UpdateAllScores. +type UpdateAllScoresParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` +} + +// GetAuthTokenParams defines parameters for GetAuthToken. +type GetAuthTokenParams struct { + // ClientID Client identifier for authentication + ClientID ClientIDHeader `json:"Client-ID"` } // PostClaimJSONRequestBody defines body for PostClaim for application/json ContentType. @@ -207,64 +333,67 @@ type PatchSourceJSONRequestBody = SourcePatchInput type ServerInterface interface { // Create a new claim // (POST /api/v1/claim) - PostClaim(c *gin.Context) + PostClaim(c *gin.Context, params PostClaimParams) // Delete a claim // (DELETE /api/v1/claim/{uriDigest}) - DeleteClaim(c *gin.Context, uriDigest string) + DeleteClaim(c *gin.Context, uriDigest string, params DeleteClaimParams) // Get claim by URI digest // (GET /api/v1/claim/{uriDigest}) - GetClaim(c *gin.Context, uriDigest string) + GetClaim(c *gin.Context, uriDigest string, params GetClaimParams) // Update claim fields // (PATCH /api/v1/claim/{uriDigest}) - PatchClaim(c *gin.Context, uriDigest string) + PatchClaim(c *gin.Context, uriDigest string, params PatchClaimParams) // Verify a single claim // (POST /api/v1/claim/{uriDigest}) - VerifyClaim(c *gin.Context, uriDigest string) + VerifyClaim(c *gin.Context, uriDigest string, params VerifyClaimParams) // Get all the proofs provided for a claim // (GET /api/v1/claim/{uriDigest}/proofs) - GetProofsByClaimDigest(c *gin.Context, uriDigest string) + GetProofsByClaimDigest(c *gin.Context, uriDigest string, params GetProofsByClaimDigestParams) // Get all claims // (GET /api/v1/claims) GetClaims(c *gin.Context, params GetClaimsParams) // Verify all claims // (POST /api/v1/claims/verify) - VerifyAllClaims(c *gin.Context) + VerifyAllClaims(c *gin.Context, params VerifyAllClaimsParams) // Create a new proof // (POST /api/v1/proof) - PostProof(c *gin.Context) + PostProof(c *gin.Context, params PostProofParams) // Delete a proof // (DELETE /api/v1/proof/{uriDigest}) - DeleteProof(c *gin.Context, uriDigest string) + DeleteProof(c *gin.Context, uriDigest string, params DeleteProofParams) // Get proof by URI digest // (GET /api/v1/proof/{uriDigest}) - GetProof(c *gin.Context, uriDigest string) + GetProof(c *gin.Context, uriDigest string, params GetProofParams) // Update proof reviewer // (PATCH /api/v1/proof/{uriDigest}) - PatchProof(c *gin.Context, uriDigest string) + PatchProof(c *gin.Context, uriDigest string, params PatchProofParams) // Get all proofs // (GET /api/v1/proofs) - GetProofs(c *gin.Context) + GetProofs(c *gin.Context, params GetProofsParams) // Create a new source // (POST /api/v1/source) - PostSource(c *gin.Context) + PostSource(c *gin.Context, params PostSourceParams) // Delete a source // (DELETE /api/v1/source/{uriDigest}) - DeleteSource(c *gin.Context, uriDigest string) + DeleteSource(c *gin.Context, uriDigest string, params DeleteSourceParams) // Get source by URI digest // (GET /api/v1/source/{uriDigest}) - GetSource(c *gin.Context, uriDigest string) + GetSource(c *gin.Context, uriDigest string, params GetSourceParams) // Update source fields // (PATCH /api/v1/source/{uriDigest}) - PatchSource(c *gin.Context, uriDigest string) + PatchSource(c *gin.Context, uriDigest string, params PatchSourceParams) // Get all the claims made by a source // (GET /api/v1/source/{uriDigest}/claims) - GetClaimsBySourceDigest(c *gin.Context, uriDigest string) + GetClaimsBySourceDigest(c *gin.Context, uriDigest string, params GetClaimsBySourceDigestParams) // Get all sources // (GET /api/v1/sources) - GetSources(c *gin.Context) + GetSources(c *gin.Context, params GetSourcesParams) // Update all source scores // (POST /api/v1/sources/scores) - UpdateAllScores(c *gin.Context) + UpdateAllScores(c *gin.Context, params UpdateAllScoresParams) + // Obtain API token + // (POST /auth/token) + GetAuthToken(c *gin.Context, params GetAuthTokenParams) } // ServerInterfaceWrapper converts contexts to parameters. @@ -279,7 +408,36 @@ type MiddlewareFunc func(c *gin.Context) // PostClaim operation middleware func (siw *ServerInterfaceWrapper) PostClaim(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PostClaimParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -288,7 +446,7 @@ func (siw *ServerInterfaceWrapper) PostClaim(c *gin.Context) { } } - siw.Handler.PostClaim(c) + siw.Handler.PostClaim(c, params) } // DeleteClaim operation middleware @@ -305,7 +463,34 @@ func (siw *ServerInterfaceWrapper) DeleteClaim(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteClaimParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -314,7 +499,7 @@ func (siw *ServerInterfaceWrapper) DeleteClaim(c *gin.Context) { } } - siw.Handler.DeleteClaim(c, uriDigest) + siw.Handler.DeleteClaim(c, uriDigest, params) } // GetClaim operation middleware @@ -331,7 +516,34 @@ func (siw *ServerInterfaceWrapper) GetClaim(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetClaimParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -340,7 +552,7 @@ func (siw *ServerInterfaceWrapper) GetClaim(c *gin.Context) { } } - siw.Handler.GetClaim(c, uriDigest) + siw.Handler.GetClaim(c, uriDigest, params) } // PatchClaim operation middleware @@ -357,7 +569,34 @@ func (siw *ServerInterfaceWrapper) PatchClaim(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PatchClaimParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -366,7 +605,7 @@ func (siw *ServerInterfaceWrapper) PatchClaim(c *gin.Context) { } } - siw.Handler.PatchClaim(c, uriDigest) + siw.Handler.PatchClaim(c, uriDigest, params) } // VerifyClaim operation middleware @@ -383,7 +622,34 @@ func (siw *ServerInterfaceWrapper) VerifyClaim(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params VerifyClaimParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -392,7 +658,7 @@ func (siw *ServerInterfaceWrapper) VerifyClaim(c *gin.Context) { } } - siw.Handler.VerifyClaim(c, uriDigest) + siw.Handler.VerifyClaim(c, uriDigest, params) } // GetProofsByClaimDigest operation middleware @@ -409,7 +675,34 @@ func (siw *ServerInterfaceWrapper) GetProofsByClaimDigest(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetProofsByClaimDigestParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -418,7 +711,7 @@ func (siw *ServerInterfaceWrapper) GetProofsByClaimDigest(c *gin.Context) { } } - siw.Handler.GetProofsByClaimDigest(c, uriDigest) + siw.Handler.GetProofsByClaimDigest(c, uriDigest, params) } // GetClaims operation middleware @@ -426,7 +719,7 @@ func (siw *ServerInterfaceWrapper) GetClaims(c *gin.Context) { var err error - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) // Parameter object where we will unmarshal all parameters from the context var params GetClaimsParams @@ -439,6 +732,30 @@ func (siw *ServerInterfaceWrapper) GetClaims(c *gin.Context) { return } + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } + for _, middleware := range siw.HandlerMiddlewares { middleware(c) if c.IsAborted() { @@ -452,7 +769,36 @@ func (siw *ServerInterfaceWrapper) GetClaims(c *gin.Context) { // VerifyAllClaims operation middleware func (siw *ServerInterfaceWrapper) VerifyAllClaims(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params VerifyAllClaimsParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -461,13 +807,42 @@ func (siw *ServerInterfaceWrapper) VerifyAllClaims(c *gin.Context) { } } - siw.Handler.VerifyAllClaims(c) + siw.Handler.VerifyAllClaims(c, params) } // PostProof operation middleware func (siw *ServerInterfaceWrapper) PostProof(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PostProofParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -476,7 +851,7 @@ func (siw *ServerInterfaceWrapper) PostProof(c *gin.Context) { } } - siw.Handler.PostProof(c) + siw.Handler.PostProof(c, params) } // DeleteProof operation middleware @@ -493,7 +868,34 @@ func (siw *ServerInterfaceWrapper) DeleteProof(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteProofParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -502,7 +904,7 @@ func (siw *ServerInterfaceWrapper) DeleteProof(c *gin.Context) { } } - siw.Handler.DeleteProof(c, uriDigest) + siw.Handler.DeleteProof(c, uriDigest, params) } // GetProof operation middleware @@ -519,7 +921,34 @@ func (siw *ServerInterfaceWrapper) GetProof(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetProofParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -528,7 +957,7 @@ func (siw *ServerInterfaceWrapper) GetProof(c *gin.Context) { } } - siw.Handler.GetProof(c, uriDigest) + siw.Handler.GetProof(c, uriDigest, params) } // PatchProof operation middleware @@ -545,7 +974,34 @@ func (siw *ServerInterfaceWrapper) PatchProof(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PatchProofParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -554,13 +1010,42 @@ func (siw *ServerInterfaceWrapper) PatchProof(c *gin.Context) { } } - siw.Handler.PatchProof(c, uriDigest) + siw.Handler.PatchProof(c, uriDigest, params) } // GetProofs operation middleware func (siw *ServerInterfaceWrapper) GetProofs(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetProofsParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -569,13 +1054,42 @@ func (siw *ServerInterfaceWrapper) GetProofs(c *gin.Context) { } } - siw.Handler.GetProofs(c) + siw.Handler.GetProofs(c, params) } // PostSource operation middleware func (siw *ServerInterfaceWrapper) PostSource(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PostSourceParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -584,7 +1098,7 @@ func (siw *ServerInterfaceWrapper) PostSource(c *gin.Context) { } } - siw.Handler.PostSource(c) + siw.Handler.PostSource(c, params) } // DeleteSource operation middleware @@ -601,7 +1115,34 @@ func (siw *ServerInterfaceWrapper) DeleteSource(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteSourceParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -610,7 +1151,7 @@ func (siw *ServerInterfaceWrapper) DeleteSource(c *gin.Context) { } } - siw.Handler.DeleteSource(c, uriDigest) + siw.Handler.DeleteSource(c, uriDigest, params) } // GetSource operation middleware @@ -627,7 +1168,34 @@ func (siw *ServerInterfaceWrapper) GetSource(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetSourceParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -636,7 +1204,7 @@ func (siw *ServerInterfaceWrapper) GetSource(c *gin.Context) { } } - siw.Handler.GetSource(c, uriDigest) + siw.Handler.GetSource(c, uriDigest, params) } // PatchSource operation middleware @@ -653,7 +1221,34 @@ func (siw *ServerInterfaceWrapper) PatchSource(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PatchSourceParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -662,7 +1257,7 @@ func (siw *ServerInterfaceWrapper) PatchSource(c *gin.Context) { } } - siw.Handler.PatchSource(c, uriDigest) + siw.Handler.PatchSource(c, uriDigest, params) } // GetClaimsBySourceDigest operation middleware @@ -679,7 +1274,34 @@ func (siw *ServerInterfaceWrapper) GetClaimsBySourceDigest(c *gin.Context) { return } - c.Set(ApiKeyAuthScopes, []string{}) + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetClaimsBySourceDigestParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -688,13 +1310,42 @@ func (siw *ServerInterfaceWrapper) GetClaimsBySourceDigest(c *gin.Context) { } } - siw.Handler.GetClaimsBySourceDigest(c, uriDigest) + siw.Handler.GetClaimsBySourceDigest(c, uriDigest, params) } // GetSources operation middleware func (siw *ServerInterfaceWrapper) GetSources(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetSourcesParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -703,13 +1354,84 @@ func (siw *ServerInterfaceWrapper) GetSources(c *gin.Context) { } } - siw.Handler.GetSources(c) + siw.Handler.GetSources(c, params) } // UpdateAllScores operation middleware func (siw *ServerInterfaceWrapper) UpdateAllScores(c *gin.Context) { - c.Set(ApiKeyAuthScopes, []string{}) + var err error + + c.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params UpdateAllScoresParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.UpdateAllScores(c, params) +} + +// GetAuthToken operation middleware +func (siw *ServerInterfaceWrapper) GetAuthToken(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetAuthTokenParams + + headers := c.Request.Header + + // ------------- Required header parameter "Client-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Client-ID")]; found { + var ClientID ClientIDHeader + n := len(valueList) + if n != 1 { + siw.ErrorHandler(c, fmt.Errorf("Expected one value for Client-ID, got %d", n), http.StatusBadRequest) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Client-ID", valueList[0], &ClientID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter Client-ID: %w", err), http.StatusBadRequest) + return + } + + params.ClientID = ClientID + + } else { + siw.ErrorHandler(c, fmt.Errorf("Header parameter Client-ID is required, but not found"), http.StatusBadRequest) + return + } for _, middleware := range siw.HandlerMiddlewares { middleware(c) @@ -718,7 +1440,7 @@ func (siw *ServerInterfaceWrapper) UpdateAllScores(c *gin.Context) { } } - siw.Handler.UpdateAllScores(c) + siw.Handler.GetAuthToken(c, params) } // GinServerOptions provides options for the Gin server. @@ -768,4 +1490,5 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options router.GET(options.BaseURL+"/api/v1/source/:uriDigest/claims", wrapper.GetClaimsBySourceDigest) router.GET(options.BaseURL+"/api/v1/sources", wrapper.GetSources) router.POST(options.BaseURL+"/api/v1/sources/scores", wrapper.UpdateAllScores) + router.POST(options.BaseURL+"/auth/token", wrapper.GetAuthToken) } diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index 9e203fb..0a3080f 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -18,6 +18,7 @@ type conf struct { PgHost string `env:"PG_HOST" yaml:"PG_HOST" env-required:"true"` Port string `env:"PORT" yaml:"PORT" env-default:"8080"` SuperUserPassword string `env:"SUPER_USER_PASSWORD" yaml:"SUPER_USER_PASSWORD" env-required:"true"` + JwtSecret string `env:"JWT_SECRET" yaml:"JWT_SECRET" env-required:"true"` } var ( diff --git a/pkg/conf/conf.yaml b/pkg/conf/conf.yaml index d0c127d..cc54af1 100644 --- a/pkg/conf/conf.yaml +++ b/pkg/conf/conf.yaml @@ -1,4 +1,5 @@ APP_USER_PASSWORD: env-pwd PG_HOST: env-host PORT: 8999 -SUPER_USER_PASSWORD: user-pwd \ No newline at end of file +SUPER_USER_PASSWORD: user-pwd +JWT_SECRET: test-secret \ No newline at end of file diff --git a/pkg/conf/conf_test.go b/pkg/conf/conf_test.go index 1ce7874..66a0c04 100644 --- a/pkg/conf/conf_test.go +++ b/pkg/conf/conf_test.go @@ -9,11 +9,12 @@ import ( ) const ( - SamplePort = "8099" - SamplePwd = "sample-pwd" - SampleHost = "sample-host" - SampleSUPwd = "super-pwd" + SamplePort = "8099" + SamplePwd = "sample-pwd" + SampleHost = "sample-host" + SampleSUPwd = "super-pwd" SampleDbName = "test-db" + SampleSecret = "env-secret" ) var _ = Describe("Conf Tests", func() { @@ -22,6 +23,7 @@ var _ = Describe("Conf Tests", func() { os.Setenv("PG_HOST", SampleHost) os.Setenv("PORT", SamplePort) os.Setenv("SUPER_USER_PASSWORD", SampleSUPwd) + os.Setenv("JWT_SECRET", SampleSecret) It("should load the environment variables into the config", func() { os.Unsetenv("DOTENV_PATH") @@ -31,6 +33,7 @@ var _ = Describe("Conf Tests", func() { Expect(conf.Cfg.PgHost).To(BeEquivalentTo(SampleHost)) Expect(conf.Cfg.Port).To(BeEquivalentTo(SamplePort)) Expect(conf.Cfg.SuperUserPassword).To(BeEquivalentTo(SampleSUPwd)) + Expect(conf.Cfg.JwtSecret).To(Equal(SampleSecret)) }) }) @@ -43,6 +46,7 @@ var _ = Describe("Conf Tests", func() { Expect(conf.Cfg.PgHost).To(BeEquivalentTo("env-host")) Expect(conf.Cfg.Port).To(BeEquivalentTo("8999")) Expect(conf.Cfg.SuperUserPassword).To(BeEquivalentTo("user-pwd")) + Expect(conf.Cfg.JwtSecret).To(Equal("test-secret")) }) }) diff --git a/pkg/domain/claim/claim_repository.go b/pkg/domain/claim/claim_repository.go index 54775f0..c770435 100644 --- a/pkg/domain/claim/claim_repository.go +++ b/pkg/domain/claim/claim_repository.go @@ -14,7 +14,7 @@ import ( //go:generate go tool counterfeiter . ClaimRepository type ClaimRepository interface { - GetClaims(ctx context.Context, claimFilter *api.GetClaimsParams) ([]api.Claim, error) + GetClaims(ctx context.Context, claimFilter *ClaimFilter) ([]api.Claim, error) PostClaim(ctx context.Context, claimInput *api.ClaimInput) (string, error) GetClaimByUriDigest(ctx context.Context, uriDigest string) (*api.Claim, error) DeleteClaimByUriDigest(ctx context.Context, claim *api.Claim) error @@ -34,10 +34,9 @@ func NewClaimRepository(ctx context.Context, client *pgsql.Client) ClaimReposito } // GetClaims returns all claims from the DB -func (cr *claimRepository) GetClaims(ctx context.Context, claimFilter *api.GetClaimsParams) ([]api.Claim, error) { +func (cr *claimRepository) GetClaims(ctx context.Context, claimFilter *ClaimFilter) ([]api.Claim, error) { var claims []api.Claim result := cr.client.FindAll(ctx, &claims, claimFilter) - if result.Error != nil { return nil, result.Error } diff --git a/pkg/domain/claim/claim_repository_test.go b/pkg/domain/claim/claim_repository_test.go index 4e88bb2..1234b2c 100644 --- a/pkg/domain/claim/claim_repository_test.go +++ b/pkg/domain/claim/claim_repository_test.go @@ -6,6 +6,7 @@ import ( "time" "source-score/pkg/api" + "source-score/pkg/domain/claim" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -116,7 +117,7 @@ var _ = Describe("Claim repository layer unit tests", func() { It("Should return checked claims when ClaimFilter Checked is true", func() { checked := true - claims, err := claimRepo.GetClaims(context.TODO(), &api.GetClaimsParams{ + claims, err := claimRepo.GetClaims(context.TODO(), &claim.ClaimFilter{ Checked: &checked, }) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/domain/claim/claim_service.go b/pkg/domain/claim/claim_service.go index 089034e..c36d70d 100644 --- a/pkg/domain/claim/claim_service.go +++ b/pkg/domain/claim/claim_service.go @@ -18,7 +18,7 @@ import ( //go:generate go tool counterfeiter . ClaimService type ClaimService interface { - GetClaims(ctx context.Context, claimFilter *api.GetClaimsParams) ([]api.Claim, error) + GetClaims(ctx context.Context, claimFilter *ClaimFilter) ([]api.Claim, error) PostClaim(ctx context.Context, claimInput *api.ClaimInput) (string, error) GetClaimByUriDigest(ctx context.Context, uriDigest string) (*api.Claim, error) DeleteClaimByUriDigest(ctx context.Context, uriDigest string) error @@ -28,6 +28,10 @@ type ClaimService interface { GetClaimsBySourceDigest(ctx context.Context, sourceDigest string) ([]api.Claim, error) } +type ClaimFilter struct { + Checked *bool +} + type claimService struct { claimRepo ClaimRepository proofSvc proof.ProofService @@ -53,7 +57,7 @@ func NewClaimService(ctx context.Context, claimRepo ClaimRepository, proofSvc pr } } -func (svc *claimService) GetClaims(ctx context.Context, claimFilter *api.GetClaimsParams) ([]api.Claim, error) { +func (svc *claimService) GetClaims(ctx context.Context, claimFilter *ClaimFilter) ([]api.Claim, error) { return svc.claimRepo.GetClaims(ctx, claimFilter) } diff --git a/pkg/domain/claim/claim_service_test.go b/pkg/domain/claim/claim_service_test.go index 1fdc22c..07e6655 100644 --- a/pkg/domain/claim/claim_service_test.go +++ b/pkg/domain/claim/claim_service_test.go @@ -75,7 +75,7 @@ var _ = Describe("Claim model service layer unit tests", Ordered, func() { checkedClaim := sampleClaim2 checkedClaim.Checked = true expected := []api.Claim{checkedClaim} - filter := &api.GetClaimsParams{Checked: &checked} + filter := &claim.ClaimFilter{Checked: &checked} callsBefore := fakeClaimRepo.GetClaimsCallCount() fakeClaimRepo.GetClaimsReturnsOnCall(callsBefore, expected, nil) diff --git a/pkg/domain/claim/claimfakes/fake_claim_repository.go b/pkg/domain/claim/claimfakes/fake_claim_repository.go index 605e4b0..b23b0e4 100644 --- a/pkg/domain/claim/claimfakes/fake_claim_repository.go +++ b/pkg/domain/claim/claimfakes/fake_claim_repository.go @@ -48,11 +48,11 @@ type FakeClaimRepository struct { result1 *api.Claim result2 error } - GetClaimsStub func(context.Context, *api.GetClaimsParams) ([]api.Claim, error) + GetClaimsStub func(context.Context, *claim.ClaimFilter) ([]api.Claim, error) getClaimsMutex sync.RWMutex getClaimsArgsForCall []struct { arg1 context.Context - arg2 *api.GetClaimsParams + arg2 *claim.ClaimFilter } getClaimsReturns struct { result1 []api.Claim @@ -323,12 +323,12 @@ func (fake *FakeClaimRepository) GetClaimByUriDigestReturnsOnCall(i int, result1 }{result1, result2} } -func (fake *FakeClaimRepository) GetClaims(arg1 context.Context, arg2 *api.GetClaimsParams) ([]api.Claim, error) { +func (fake *FakeClaimRepository) GetClaims(arg1 context.Context, arg2 *claim.ClaimFilter) ([]api.Claim, error) { fake.getClaimsMutex.Lock() ret, specificReturn := fake.getClaimsReturnsOnCall[len(fake.getClaimsArgsForCall)] fake.getClaimsArgsForCall = append(fake.getClaimsArgsForCall, struct { arg1 context.Context - arg2 *api.GetClaimsParams + arg2 *claim.ClaimFilter }{arg1, arg2}) stub := fake.GetClaimsStub fakeReturns := fake.getClaimsReturns @@ -349,13 +349,13 @@ func (fake *FakeClaimRepository) GetClaimsCallCount() int { return len(fake.getClaimsArgsForCall) } -func (fake *FakeClaimRepository) GetClaimsCalls(stub func(context.Context, *api.GetClaimsParams) ([]api.Claim, error)) { +func (fake *FakeClaimRepository) GetClaimsCalls(stub func(context.Context, *claim.ClaimFilter) ([]api.Claim, error)) { fake.getClaimsMutex.Lock() defer fake.getClaimsMutex.Unlock() fake.GetClaimsStub = stub } -func (fake *FakeClaimRepository) GetClaimsArgsForCall(i int) (context.Context, *api.GetClaimsParams) { +func (fake *FakeClaimRepository) GetClaimsArgsForCall(i int) (context.Context, *claim.ClaimFilter) { fake.getClaimsMutex.RLock() defer fake.getClaimsMutex.RUnlock() argsForCall := fake.getClaimsArgsForCall[i] diff --git a/pkg/domain/claim/claimfakes/fake_claim_service.go b/pkg/domain/claim/claimfakes/fake_claim_service.go index 80bf436..424701c 100644 --- a/pkg/domain/claim/claimfakes/fake_claim_service.go +++ b/pkg/domain/claim/claimfakes/fake_claim_service.go @@ -35,11 +35,11 @@ type FakeClaimService struct { result1 *api.Claim result2 error } - GetClaimsStub func(context.Context, *api.GetClaimsParams) ([]api.Claim, error) + GetClaimsStub func(context.Context, *claim.ClaimFilter) ([]api.Claim, error) getClaimsMutex sync.RWMutex getClaimsArgsForCall []struct { arg1 context.Context - arg2 *api.GetClaimsParams + arg2 *claim.ClaimFilter } getClaimsReturns struct { result1 []api.Claim @@ -245,12 +245,12 @@ func (fake *FakeClaimService) GetClaimByUriDigestReturnsOnCall(i int, result1 *a }{result1, result2} } -func (fake *FakeClaimService) GetClaims(arg1 context.Context, arg2 *api.GetClaimsParams) ([]api.Claim, error) { +func (fake *FakeClaimService) GetClaims(arg1 context.Context, arg2 *claim.ClaimFilter) ([]api.Claim, error) { fake.getClaimsMutex.Lock() ret, specificReturn := fake.getClaimsReturnsOnCall[len(fake.getClaimsArgsForCall)] fake.getClaimsArgsForCall = append(fake.getClaimsArgsForCall, struct { arg1 context.Context - arg2 *api.GetClaimsParams + arg2 *claim.ClaimFilter }{arg1, arg2}) stub := fake.GetClaimsStub fakeReturns := fake.getClaimsReturns @@ -271,13 +271,13 @@ func (fake *FakeClaimService) GetClaimsCallCount() int { return len(fake.getClaimsArgsForCall) } -func (fake *FakeClaimService) GetClaimsCalls(stub func(context.Context, *api.GetClaimsParams) ([]api.Claim, error)) { +func (fake *FakeClaimService) GetClaimsCalls(stub func(context.Context, *claim.ClaimFilter) ([]api.Claim, error)) { fake.getClaimsMutex.Lock() defer fake.getClaimsMutex.Unlock() fake.GetClaimsStub = stub } -func (fake *FakeClaimService) GetClaimsArgsForCall(i int) (context.Context, *api.GetClaimsParams) { +func (fake *FakeClaimService) GetClaimsArgsForCall(i int) (context.Context, *claim.ClaimFilter) { fake.getClaimsMutex.RLock() defer fake.getClaimsMutex.RUnlock() argsForCall := fake.getClaimsArgsForCall[i] diff --git a/pkg/handlers/auth.go b/pkg/handlers/auth.go new file mode 100644 index 0000000..1c52273 --- /dev/null +++ b/pkg/handlers/auth.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "fmt" + "log/slog" + "net/http" + "source-score/pkg/middleware" + "time" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" +) + +type AuthHandler struct { + jwtSecret string +} + +func NewAuthHandler(secret string) *AuthHandler { + return &AuthHandler{jwtSecret: secret} +} + +func (ah *AuthHandler) GetAuthToken(c *gin.Context) { + clientID := c.GetHeader(middleware.ClientIDHeader) + if clientID == "" { + c.JSON( + http.StatusBadRequest, + gin.H{"error": fmt.Sprintf("%s header missing", middleware.ClientIDHeader)}, + ) + return + } + + token, err := jwt.NewWithClaims( + jwt.SigningMethodHS256, + jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(4 * time.Hour)), + Audience: []string{clientID}, + IssuedAt: jwt.NewNumericDate(time.Now()), + Issuer: middleware.TokenIssuer, + }, + ).SignedString([]byte(ah.jwtSecret)) + if err != nil { + slog.Error("failed to generate auth token", "error", err) + c.JSON( + http.StatusInternalServerError, + gin.H{"error": "failed to generate auth token"}, + ) + return + } + + c.JSON(http.StatusOK, gin.H{"token": token}) +} diff --git a/pkg/handlers/claim.go b/pkg/handlers/claim.go index 92f3879..02dbe0a 100644 --- a/pkg/handlers/claim.go +++ b/pkg/handlers/claim.go @@ -27,7 +27,11 @@ func NewClaimHandler(ctx context.Context, claimSvc claim.ClaimService) *ClaimHan } func (ch *ClaimHandler) GetClaims(ctx *gin.Context, params api.GetClaimsParams) { - claims, err := ch.claimSvc.GetClaims(ctx, ¶ms) + claimFilter := &claim.ClaimFilter{ + Checked: params.Checked, + } + + claims, err := ch.claimSvc.GetClaims(ctx, claimFilter) if err != nil { slog.Error("failed to get claims", "error", err) ctx.JSON( diff --git a/pkg/handlers/swagger.go b/pkg/handlers/swagger.go index 3c370aa..45e0f8c 100644 --- a/pkg/handlers/swagger.go +++ b/pkg/handlers/swagger.go @@ -64,10 +64,7 @@ func (h *SwaggerHandler) ServeUI(c *gin.Context) { plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], - layout: "StandaloneLayout", - onComplete: function () { - ui.preauthorizeApiKey("ApiKeyAuth", "demo-api-key"); - } + layout: "StandaloneLayout" }); }; diff --git a/pkg/http/router.go b/pkg/http/router.go index e432c8e..b4b6bf4 100644 --- a/pkg/http/router.go +++ b/pkg/http/router.go @@ -3,6 +3,7 @@ package http import ( "context" "source-score/pkg/api" + "source-score/pkg/conf" "source-score/pkg/domain/claim" "source-score/pkg/domain/proof" "source-score/pkg/domain/source" @@ -16,6 +17,7 @@ type router struct { srcHandler *handlers.SourceHandler claimHandler *handlers.ClaimHandler proofHandler *handlers.ProofHandler + authHandler *handlers.AuthHandler } func NewRouter( @@ -29,26 +31,27 @@ func NewRouter( srcHandler: handlers.NewSourceHandler(ctx, sourceSvc), claimHandler: handlers.NewClaimHandler(ctx, claimSvc), proofHandler: handlers.NewProofHandler(ctx, proofSvc), + authHandler: handlers.NewAuthHandler(conf.Cfg.JwtSecret), } } -func (r *router) PostSource(ctx *gin.Context) { +func (r *router) PostSource(ctx *gin.Context, params api.PostSourceParams) { r.srcHandler.PostSource(ctx) } -func (r *router) DeleteSource(ctx *gin.Context, uriDigest string) { +func (r *router) DeleteSource(ctx *gin.Context, uriDigest string, params api.DeleteSourceParams) { r.srcHandler.DeleteSourceByUriDigest(ctx, uriDigest) } -func (r *router) GetSource(ctx *gin.Context, uriDigest string) { +func (r *router) GetSource(ctx *gin.Context, uriDigest string, params api.GetSourceParams) { r.srcHandler.GetSourceByUriDigest(ctx, uriDigest) } -func (r *router) GetSources(ctx *gin.Context) { +func (r *router) GetSources(ctx *gin.Context, params api.GetSourcesParams) { r.srcHandler.GetSources(ctx) } -func (r *router) PatchSource(ctx *gin.Context, uriDigest string) { +func (r *router) PatchSource(ctx *gin.Context, uriDigest string, params api.PatchSourceParams) { r.srcHandler.PatchSourceByUriDigest(ctx, uriDigest) } @@ -56,59 +59,63 @@ func (r *router) GetClaims(ctx *gin.Context, params api.GetClaimsParams) { r.claimHandler.GetClaims(ctx, params) } -func (r *router) PostClaim(ctx *gin.Context) { +func (r *router) PostClaim(ctx *gin.Context, params api.PostClaimParams) { r.claimHandler.PostClaim(ctx) } -func (r *router) GetClaim(ctx *gin.Context, uriDigest string) { +func (r *router) GetClaim(ctx *gin.Context, uriDigest string, params api.GetClaimParams) { r.claimHandler.GetClaimByUriDigest(ctx, uriDigest) } -func (r *router) DeleteClaim(ctx *gin.Context, uriDigest string) { +func (r *router) DeleteClaim(ctx *gin.Context, uriDigest string, params api.DeleteClaimParams) { r.claimHandler.DeleteClaimByUriDigest(ctx, uriDigest) } -func (r *router) PatchClaim(ctx *gin.Context, claimDigest string) { +func (r *router) PatchClaim(ctx *gin.Context, claimDigest string, params api.PatchClaimParams) { r.claimHandler.PatchClaimByUriDigest(ctx, claimDigest) } -func (r *router) VerifyAllClaims(ctx *gin.Context) { +func (r *router) VerifyAllClaims(ctx *gin.Context, params api.VerifyAllClaimsParams) { r.claimHandler.VerifyAllClaims(ctx) } -func (r *router) VerifyClaim(ctx *gin.Context, claimDigest string) { +func (r *router) VerifyClaim(ctx *gin.Context, claimDigest string, params api.VerifyClaimParams) { // TODO: remove if individual claim verification is not required // r.claimHandler.ValidateClaimByUriDigest(ctx, claimDigest) } -func (r *router) PostProof(ctx *gin.Context) { +func (r *router) PostProof(ctx *gin.Context, params api.PostProofParams) { r.proofHandler.PostProof(ctx) } -func (r *router) DeleteProof(ctx *gin.Context, uriDigest string) { +func (r *router) DeleteProof(ctx *gin.Context, uriDigest string, params api.DeleteProofParams) { r.proofHandler.DeleteProofByUriDigest(ctx, uriDigest) } -func (r *router) GetProof(ctx *gin.Context, uriDigest string) { +func (r *router) GetProof(ctx *gin.Context, uriDigest string, params api.GetProofParams) { r.proofHandler.GetProofByUriDigest(ctx, uriDigest) } -func (r *router) GetProofs(ctx *gin.Context) { +func (r *router) GetProofs(ctx *gin.Context, params api.GetProofsParams) { r.proofHandler.GetProofs(ctx) } -func (r *router) PatchProof(ctx *gin.Context, uriDigest string) { +func (r *router) PatchProof(ctx *gin.Context, uriDigest string, params api.PatchProofParams) { r.proofHandler.PatchProofByUriDigest(ctx, uriDigest) } -func (r *router) UpdateAllScores(ctx *gin.Context) { +func (r *router) UpdateAllScores(ctx *gin.Context, params api.UpdateAllScoresParams) { r.srcHandler.UpdateAllScores(ctx) } -func (r *router) GetClaimsBySourceDigest(ctx *gin.Context, sourceDigest string) { +func (r *router) GetClaimsBySourceDigest(ctx *gin.Context, sourceDigest string, params api.GetClaimsBySourceDigestParams) { r.claimHandler.GetClaimsBySourceDigest(ctx, sourceDigest) } -func (r *router) GetProofsByClaimDigest(ctx *gin.Context, claimDigest string) { +func (r *router) GetProofsByClaimDigest(ctx *gin.Context, claimDigest string, params api.GetProofsByClaimDigestParams) { r.proofHandler.GetProofsByClaimDigest(ctx, claimDigest) } + +func (r *router) GetAuthToken(c *gin.Context, params api.GetAuthTokenParams) { + r.authHandler.GetAuthToken(c) +} diff --git a/pkg/middleware/apikey.go b/pkg/middleware/apikey.go deleted file mode 100644 index f730b3c..0000000 --- a/pkg/middleware/apikey.go +++ /dev/null @@ -1,20 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -func APIKeyMiddleware(validKey string) gin.HandlerFunc { - return func(c *gin.Context) { - key := c.GetHeader("X-API-Key") - if key != validKey { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "error": "invalid or missing API key", - }) - return - } - c.Next() - } -} diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go new file mode 100644 index 0000000..e69c95f --- /dev/null +++ b/pkg/middleware/auth.go @@ -0,0 +1,56 @@ +package middleware + +import ( + "fmt" + "log/slog" + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" +) + +const ( + ClientIDHeader = "Client-ID" + TokenIssuer = "source-score" +) + +func AuthTokenMiddleware(jwtSecret string) gin.HandlerFunc { + return func(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + if !strings.HasPrefix(strings.ToLower(authHeader), "bearer ") { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing token"}) + return + } + + clientID := c.GetHeader(ClientIDHeader) + if clientID == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%s header missing", ClientIDHeader)}) + return + } + + headerValArr := strings.Fields(strings.TrimSpace(authHeader)) + if len(headerValArr) != 2 { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization header format"}) + return + } + + token, err := jwt.ParseWithClaims( + headerValArr[1], + &jwt.RegisteredClaims{}, + func(*jwt.Token) (any, error) { return []byte(jwtSecret), nil }, + jwt.WithAudience(clientID), + jwt.WithIssuer(TokenIssuer), + jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name}), + ) + if err != nil || !token.Valid { + if err != nil { + slog.Error("failed to parse jwt token", "error", err) + } + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"}) + return + } + + c.Next() + } +}