diff --git a/createFeatureFlag/go.mod b/createFeatureFlag/go.mod index fa69785..d886ed5 100644 --- a/createFeatureFlag/go.mod +++ b/createFeatureFlag/go.mod @@ -3,7 +3,7 @@ module createFeatureFlag go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/go-playground/validator/v10 v10.14.1 @@ -14,6 +14,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/createFeatureFlag/go.sum b/createFeatureFlag/go.sum index 13719c4..fa93f2f 100644 --- a/createFeatureFlag/go.sum +++ b/createFeatureFlag/go.sum @@ -1,5 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a h1:4hBZDCacd+X+JCRE2x3pHRiU1RGWzkj239WCL7OgY/8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -16,6 +18,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/createFeatureFlag/main.go b/createFeatureFlag/main.go index c588e18..c749000 100644 --- a/createFeatureFlag/main.go +++ b/createFeatureFlag/main.go @@ -7,6 +7,8 @@ import ( "time" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/models" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" @@ -23,7 +25,7 @@ func init() { validate = validator.New() } -func createFeatureFlag(db *dynamodb.DynamoDB, createFeatureFlagRequest utils.CreateFeatureFlagRequest) error { +func createFeatureFlag(db *dynamodb.DynamoDB, createFeatureFlagRequest utils.CreateFeatureFlagRequest) (models.FeatureFlag, error) { featureFlag := models.FeatureFlag{ Id: uuid.New().String(), Name: createFeatureFlagRequest.FlagName, @@ -38,7 +40,7 @@ func createFeatureFlag(db *dynamodb.DynamoDB, createFeatureFlagRequest utils.Cre item, err := database.MarshalMap(featureFlag) if err != nil { log.Printf("Error marshalling object to DynamoDB AttributeValue: \n %v", err) - return err + return models.FeatureFlag{}, err } input := &dynamodb.PutItemInput{ @@ -49,9 +51,9 @@ func createFeatureFlag(db *dynamodb.DynamoDB, createFeatureFlagRequest utils.Cre _, err = db.PutItem(input) if err != nil { log.Printf("Error putting item to Dynamodb: \n %v", err) - return err + return models.FeatureFlag{}, err } - return nil + return featureFlag, nil } func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { @@ -60,8 +62,18 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, db := database.CreateDynamoDB() utils.CheckRequestAllowed(db, utils.ConcurrencyDisablingLambda) - - err := json.Unmarshal([]byte(req.Body), &createFeatureFlagRequest) + + corsResponse, err, passed := middleware.HandleCORS(req) + if !passed { + return corsResponse, err + } + + jwtResponse, _, err := jwt.JWTMiddleware()(req) + if err != nil || jwtResponse.StatusCode != http.StatusOK { + return jwtResponse, err + } + + err = json.Unmarshal([]byte(req.Body), &createFeatureFlagRequest) if err != nil { log.Printf("Error unmarshal request body: \n %v", err) return utils.ClientError(http.StatusUnprocessableEntity, "Error unmarshalling request body") @@ -74,19 +86,32 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, }, nil } - err = createFeatureFlag(db, createFeatureFlagRequest) + featureFlag, err := createFeatureFlag(db, createFeatureFlagRequest) if err != nil { log.Printf("Error while creating feature flag: \n %v ", err) return utils.ServerError(err) } + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + response := events.APIGatewayProxyResponse{ StatusCode: http.StatusCreated, - Headers: map[string]string{ - "Content-Type": "application/json", - }, - Body: "Created feature flag successfully", + Headers: corsHeaders, } + + responseBody, err := json.Marshal(map[string]interface{}{ + "message": "Created feature flag successfully", + "data": featureFlag, + }) + + if err != nil { + log.Printf("Error marshalling response body: %v", err) + return utils.ServerError(err) + } + + response.Body = string(responseBody) + return response, nil } diff --git a/createUserFeatureFlag/go.mod b/createUserFeatureFlag/go.mod index 952ae61..0aaa2b8 100644 --- a/createUserFeatureFlag/go.mod +++ b/createUserFeatureFlag/go.mod @@ -3,7 +3,7 @@ module createUserFeatureFlag go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/go-playground/validator/v10 v10.14.1 @@ -13,6 +13,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/createUserFeatureFlag/go.sum b/createUserFeatureFlag/go.sum index 19790ea..369d9fb 100644 --- a/createUserFeatureFlag/go.sum +++ b/createUserFeatureFlag/go.sum @@ -1,5 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a h1:4hBZDCacd+X+JCRE2x3pHRiU1RGWzkj239WCL7OgY/8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -16,6 +18,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/createUserFeatureFlag/main.go b/createUserFeatureFlag/main.go index 5bd9634..da2840b 100644 --- a/createUserFeatureFlag/main.go +++ b/createUserFeatureFlag/main.go @@ -8,6 +8,8 @@ import ( "time" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/models" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" @@ -54,8 +56,18 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, userId := req.PathParameters["userId"] flagId := req.PathParameters["flagId"] + corsResponse, err, passed := middleware.HandleCORS(req) + if !passed { + return corsResponse, err + } + + jwtResponse, _, err := jwt.JWTMiddleware()(req) + if err != nil || jwtResponse.StatusCode != http.StatusOK { + return jwtResponse, err + } + var requestBody utils.CreateFeatureFlagUserMappingRequest - err := json.Unmarshal([]byte(req.Body), &requestBody) + err = json.Unmarshal([]byte(req.Body), &requestBody) if err != nil { log.Printf("Error unmarshal request body: \n %v", err) return utils.ClientError(http.StatusUnprocessableEntity, "Error unmarshal request body") @@ -103,9 +115,13 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, return utils.ServerError(err) } + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + response := events.APIGatewayProxyResponse{ Body: string(resultJson), StatusCode: http.StatusOK, + Headers: corsHeaders, } return response, nil diff --git a/getAllFeatureFlags/go.mod b/getAllFeatureFlags/go.mod index e09edfd..b176992 100644 --- a/getAllFeatureFlags/go.mod +++ b/getAllFeatureFlags/go.mod @@ -3,12 +3,13 @@ module getAllFeatureFlags go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 ) require ( + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect ) diff --git a/getAllFeatureFlags/go.sum b/getAllFeatureFlags/go.sum index 3a3a451..8912fa6 100644 --- a/getAllFeatureFlags/go.sum +++ b/getAllFeatureFlags/go.sum @@ -1,11 +1,15 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a h1:4hBZDCacd+X+JCRE2x3pHRiU1RGWzkj239WCL7OgY/8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240806210615-ae45f3d1311a/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= github.com/aws/aws-sdk-go v1.44.284/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/getAllFeatureFlags/main.go b/getAllFeatureFlags/main.go index fdc66ea..e58c000 100644 --- a/getAllFeatureFlags/main.go +++ b/getAllFeatureFlags/main.go @@ -6,6 +6,8 @@ import ( "net/http" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" @@ -45,9 +47,18 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo db := database.CreateDynamoDB() utils.CheckRequestAllowed(db, utils.ConcurrencyDisablingLambda) - - featureFlagsResponse, err := getAllFeatureFlags(db) + corsResponse, err, passed := middleware.HandleCORS(request) + if !passed { + return corsResponse, err + } + + response, _, err := jwt.JWTMiddleware()(request) + if err != nil || response.StatusCode != http.StatusOK { + return response, err + } + + featureFlagsResponse, err := getAllFeatureFlags(db) if err != nil { return utils.ServerError(err) } @@ -62,11 +73,14 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo return utils.ServerError(err) } + origin := request.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + return events.APIGatewayProxyResponse{ Body: string(jsonResult), StatusCode: http.StatusOK, + Headers: corsHeaders, }, nil - } func main() { diff --git a/getFeatureFlagById/go.mod b/getFeatureFlagById/go.mod index 6384b3d..19a55e7 100644 --- a/getFeatureFlagById/go.mod +++ b/getFeatureFlagById/go.mod @@ -3,12 +3,13 @@ module getFeatureFlagById go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572 + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 ) require ( github.com/aws/aws-sdk-go v1.44.284 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect ) diff --git a/getFeatureFlagById/go.sum b/getFeatureFlagById/go.sum index d68db5a..8912fa6 100644 --- a/getFeatureFlagById/go.sum +++ b/getFeatureFlagById/go.sum @@ -1,11 +1,15 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572 h1:Hyc2jIH2tjWTafRMjPeznbRHpbw/1Q8UmRdHVoRrzE8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= github.com/aws/aws-sdk-go v1.44.284/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/getFeatureFlagById/main.go b/getFeatureFlagById/main.go index f31cd25..f72543a 100644 --- a/getFeatureFlagById/main.go +++ b/getFeatureFlagById/main.go @@ -6,37 +6,60 @@ import ( "net/http" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - id, _ := req.PathParameters["flagId"] - featureFlag, err := database.ProcessGetFeatureFlagByHashKey(utils.Id, id) + corsResponse, err, passed := middleware.HandleCORS(req) + + if !passed { + return corsResponse, err + } + + response, _, err := jwt.JWTMiddleware()(req) + if err != nil || response.StatusCode != http.StatusOK { + return response, err + } + + featureFlagId, ok := req.PathParameters["flagId"] + if !ok { + log.Println("flagId is required") + clientErrorResponse, _ := utils.ClientError(http.StatusBadRequest, "flagId is required") + return clientErrorResponse, nil + } + + featureFlag, err := database.ProcessGetFeatureFlagByHashKey(utils.Id, featureFlagId) if err != nil { - return utils.ServerError(err) + log.Printf("Database error: %v", err) + serverErrorResponse, _ := utils.ServerError(err) + return serverErrorResponse, nil } if featureFlag == nil { log.Println("Feature Flag not found") - return utils.ClientError(http.StatusNotFound, "Feature flag not found") - + clientErrorResponse, _ := utils.ClientError(http.StatusNotFound, "Feature flag not found") + return clientErrorResponse, nil } log.Println(featureFlag, " is the feature flag") jsonResponse, err := json.Marshal(featureFlag) if err != nil { - log.Printf("Error converting FeatureFlag to JSON \n %v", err) - return utils.ServerError(err) + log.Printf("Error converting FeatureFlag to JSON: %v", err) + serverErrorResponse, _ := utils.ServerError(err) + return serverErrorResponse, nil } - response := events.APIGatewayProxyResponse{ + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + + response = events.APIGatewayProxyResponse{ StatusCode: http.StatusOK, - Headers: map[string]string{ - "Content-Type": "application/json", - }, - Body: string(jsonResponse), + Headers: corsHeaders, + Body: string(jsonResponse), } return response, nil } diff --git a/getUserFeatureFlag/go.mod b/getUserFeatureFlag/go.mod index cbf6488..86900ee 100644 --- a/getUserFeatureFlag/go.mod +++ b/getUserFeatureFlag/go.mod @@ -3,7 +3,7 @@ module getUserFeatureFlag go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/google/uuid v1.3.0 @@ -12,6 +12,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/getUserFeatureFlag/go.sum b/getUserFeatureFlag/go.sum index 3042a93..887a22f 100644 --- a/getUserFeatureFlag/go.sum +++ b/getUserFeatureFlag/go.sum @@ -1,5 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c h1:KoOWwnVlR63D5qTJYJtzuOjUZ1wTVc76llysEZbNKds= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -7,6 +9,8 @@ github.com/aws/aws-sdk-go v1.44.284/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/getUserFeatureFlag/main.go b/getUserFeatureFlag/main.go index 83bb8b9..8aebe8b 100644 --- a/getUserFeatureFlag/main.go +++ b/getUserFeatureFlag/main.go @@ -6,6 +6,8 @@ import ( "net/http" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" lambda "github.com/aws/aws-lambda-go/lambda" @@ -52,6 +54,16 @@ func processGetById(userId string, flagId string) (*utils.FeatureFlagUserMapping } func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + corsResponse, err, passed := middleware.HandleCORS(req) + if !passed { + return corsResponse, err + } + + response, _, err := jwt.JWTMiddleware()(req) + if err != nil || response.StatusCode != http.StatusOK { + return response, err + } + userId := req.PathParameters["userId"] flagId := req.PathParameters["flagId"] @@ -74,9 +86,13 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, return utils.ServerError(err) } + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + return events.APIGatewayProxyResponse{ Body: string(resultJson), StatusCode: http.StatusOK, + Headers: corsHeaders, }, nil } diff --git a/getUserFeatureFlags/go.mod b/getUserFeatureFlags/go.mod index db788fd..2331c8a 100644 --- a/getUserFeatureFlags/go.mod +++ b/getUserFeatureFlags/go.mod @@ -3,7 +3,7 @@ module getUserFeatureFlags go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/google/uuid v1.3.0 @@ -12,6 +12,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/getUserFeatureFlags/go.sum b/getUserFeatureFlags/go.sum index ce2e4ab..887a22f 100644 --- a/getUserFeatureFlags/go.sum +++ b/getUserFeatureFlags/go.sum @@ -1,7 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231002185428-1ab29298fc37 h1:xNdIzSF5QgGyF3jCf+a93zTJWeXEpl3q98OXca6CbuU= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231002185428-1ab29298fc37/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c h1:KoOWwnVlR63D5qTJYJtzuOjUZ1wTVc76llysEZbNKds= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -9,6 +9,8 @@ github.com/aws/aws-sdk-go v1.44.284/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/getUserFeatureFlags/main.go b/getUserFeatureFlags/main.go index 6e234a1..2401831 100644 --- a/getUserFeatureFlags/main.go +++ b/getUserFeatureFlags/main.go @@ -7,6 +7,8 @@ import ( "net/http" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" @@ -52,6 +54,17 @@ func processGetById(userId string) ([]utils.FeatureFlagUserMappingResponse, erro } func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + + corsResponse, err, passed := middleware.HandleCORS(req) + if !passed { + return corsResponse, err + } + + response, _, err := jwt.JWTMiddleware()(req) + if err != nil || response.StatusCode != http.StatusOK { + return response, err + } + userId := req.PathParameters["userId"] result, err := processGetById(userId) if err != nil { @@ -67,9 +80,13 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, return utils.ServerError(err) } + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + return events.APIGatewayProxyResponse{ Body: string(resultJson), StatusCode: http.StatusOK, + Headers: corsHeaders, }, nil } diff --git a/go.mod b/go.mod index 4cee9b0..08230f6 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/aws/aws-lambda-go v1.40.0 github.com/aws/aws-sdk-go v1.44.248 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/joho/godotenv v1.5.1 ) diff --git a/go.sum b/go.sum index 5310947..4e0a505 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/aws/aws-sdk-go v1.44.248/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/jwt/jwt.go b/jwt/jwt.go new file mode 100644 index 0000000..40c9d73 --- /dev/null +++ b/jwt/jwt.go @@ -0,0 +1,231 @@ +package jwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "log" + "net/http" + "os" + "strings" + "sync" + + "github.com/Real-Dev-Squad/feature-flag-backend/utils" + "github.com/aws/aws-lambda-go/events" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ssm" + "github.com/golang-jwt/jwt/v5" +) + +var ( + jwtUtilsInstance *JWTUtils + initError error + once sync.Once +) + +type JWTUtils struct { + publicKey *rsa.PublicKey +} + +type EnvConfig struct { + SessionCookieName string + Environment string + PublicKeyName string +} + +func LoadEnvConfig() (*EnvConfig, error) { + return &EnvConfig{ + SessionCookieName: os.Getenv("SESSION_COOKIE_NAME"), + Environment: os.Getenv("ENVIRONMENT"), + PublicKeyName: os.Getenv("RDS_BACKEND_PUBLIC_KEY_NAME"), + }, nil +} + +func GetInstance() (*JWTUtils, error) { + once.Do(func() { + jwtUtilsInstance = &JWTUtils{} + if err := jwtUtilsInstance.initialize(); err != nil { + initError = fmt.Errorf("internal server error") + jwtUtilsInstance = nil + } + }) + + if initError != nil { + return nil, initError + } + + if jwtUtilsInstance == nil { + return nil, errors.New("internal server error") + } + + return jwtUtilsInstance, nil +} + +func (j *JWTUtils) initialize() error { + if j == nil { + return errors.New("internal server error") + } + + envConfig, _ := LoadEnvConfig() + + parameterName := envConfig.PublicKeyName + if parameterName == "" { + switch envConfig.Environment { + case utils.PROD: + parameterName = utils.RDS_BACKEND_PUBLIC_KEY_NAME_PROD + case utils.DEV: + parameterName = utils.RDS_BACKEND_PUBLIC_KEY_NAME_DEV + default: + parameterName = utils.RDS_BACKEND_PUBLIC_KEY_NAME_LOCAL + } + } + + publicKeyString, err := getPublicKeyFromParameterStore(parameterName) + if err != nil { + return err + } + + block, _ := pem.Decode([]byte(publicKeyString)) + if block == nil { + return errors.New("internal server error") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return fmt.Errorf("internal server error") + } + + rsaPublicKey, ok := pub.(*rsa.PublicKey) + if !ok { + return errors.New("internal server error") + } + + j.publicKey = rsaPublicKey + return nil +} + +func getPublicKeyFromParameterStore(parameterName string) (string, error) { + sess, err := session.NewSession() + if err != nil { + return "", err + } + + svc := ssm.New(sess) + input := &ssm.GetParameterInput{ + Name: aws.String(parameterName), + WithDecryption: aws.Bool(true), + } + + result, err := svc.GetParameter(input) + if err != nil { + return "", err + } + + return *result.Parameter.Value, nil +} + +func (j *JWTUtils) ValidateToken(tokenString string) (jwt.MapClaims, error) { + if j == nil || j.publicKey == nil { + return nil, errors.New("internal server error") + } + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + return nil, fmt.Errorf("invalid token") + } + return j.publicKey, nil + }) + + if err != nil { + return nil, fmt.Errorf("invalid token") + } + + if !token.Valid { + return nil, errors.New("invalid token") + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return nil, errors.New("invalid token") + } + + return claims, nil +} + +func (j *JWTUtils) ExtractClaim(claims jwt.MapClaims, claimKey string) (string, error) { + if claims == nil { + return "", errors.New("internal server error") + } + + value, ok := claims[claimKey].(string) + if !ok || value == "" { + return "", fmt.Errorf("unauthorized") + } + return value, nil +} + +func handleMiddlewareResponse(statusCode int, message string) (events.APIGatewayProxyResponse, string, error) { + return events.APIGatewayProxyResponse{ + StatusCode: statusCode, + Body: message, + }, "", nil +} + +func JWTMiddleware() func(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, string, error) { + return func(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, string, error) { + jwtUtils, err := GetInstance() + if err != nil { + log.Printf("Failed to get JWTUtils instance: %v", err) + return handleMiddlewareResponse(http.StatusInternalServerError, "Internal server error") + } + + cookie := req.Headers["Cookie"] + if cookie == "" { + return handleMiddlewareResponse(http.StatusUnauthorized, "Unauthenticated") + } + + envConfig, _ := LoadEnvConfig() + + cookieName := envConfig.SessionCookieName + if cookieName == "" { + switch envConfig.Environment { + case utils.PROD: + cookieName = utils.SESSION_COOKIE_NAME_PROD + case utils.DEV: + cookieName = utils.SESSION_COOKIE_NAME_DEV + default: + cookieName = utils.SESSION_COOKIE_NAME_LOCAL + } + } + + var jwtToken string + cookies := strings.Split(cookie, ";") + for _, c := range cookies { + c = strings.TrimSpace(c) + if strings.HasPrefix(c, cookieName+"=") { + jwtToken = strings.TrimPrefix(c, cookieName+"=") + break + } + } + + if jwtToken == "" { + return handleMiddlewareResponse(http.StatusUnauthorized, "Unauthenticated") + } + + claims, err := jwtUtils.ValidateToken(jwtToken) + if err != nil { + log.Printf("Token validation failed: %v", err) + return handleMiddlewareResponse(http.StatusUnauthorized, "Invalid token") + } + + userId, err := jwtUtils.ExtractClaim(claims, "userId") + if err != nil { + return handleMiddlewareResponse(http.StatusUnauthorized, "Unauthorized") + } + + return handleMiddlewareResponse(http.StatusOK, userId) + } +} diff --git a/middlewares/cors.go b/middlewares/cors.go new file mode 100644 index 0000000..3899c58 --- /dev/null +++ b/middlewares/cors.go @@ -0,0 +1,85 @@ +package cors + +import ( + "log" + "net/http" + "regexp" + + "github.com/aws/aws-lambda-go/events" +) + +var AllowedOrigins = []*regexp.Regexp{ + regexp.MustCompile(`^https?://([a-zA-Z0-9-]+\.)*realdevsquad\.com$`), +} + +func generateCORSHeaders(origin string) map[string]string { + return map[string]string{ + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": "Authorization, Content-Type, Cache-Control, Cookie", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Expose-Headers": "Set-Cookie", + "Vary": "Origin", + } +} + +func GetCORSHeaders(origin string) map[string]string { + for _, pattern := range AllowedOrigins { + if pattern.MatchString(origin) { + return generateCORSHeaders(origin) + } + } + + return generateCORSHeaders("null") +} + +func HandleCORS(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error, bool) { + corsResponse, err := CORSMiddleware()(req) + if err != nil { + log.Printf("CORS error: %v", err) + return corsResponse, err, false + } + + if corsResponse.StatusCode != http.StatusOK { + return corsResponse, nil, false + } + + return corsResponse, nil, true +} + +func CORSMiddleware() func(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return func(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + log.Println("Received Headers:", req.Headers) + origin, exists := req.Headers["Origin"] + if !exists { + return events.APIGatewayProxyResponse{ + StatusCode: http.StatusForbidden, + Body: "CORS policy: No origin header found.", + }, nil + } + + isRDSDomain := AllowedOrigins[0].MatchString(origin) + + if isRDSDomain { + headers := generateCORSHeaders(origin) + + if req.HTTPMethod == "OPTIONS" { + return events.APIGatewayProxyResponse{ + StatusCode: http.StatusOK, + Headers: headers, + Body: "", + }, nil + } + + return events.APIGatewayProxyResponse{ + StatusCode: http.StatusOK, + Headers: headers, + }, nil + } + + return events.APIGatewayProxyResponse{ + StatusCode: http.StatusForbidden, + Body: "CORS policy does not allow access from this origin.", + }, nil + } +} diff --git a/resetLimitLambda/go.mod b/resetLimitLambda/go.mod index 24e1ea1..ce78fee 100644 --- a/resetLimitLambda/go.mod +++ b/resetLimitLambda/go.mod @@ -3,12 +3,13 @@ module rateLimiterLambda go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231002185428-1ab29298fc37 + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.323 ) require ( + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect ) diff --git a/resetLimitLambda/go.sum b/resetLimitLambda/go.sum index 9a76ae6..acc23cd 100644 --- a/resetLimitLambda/go.sum +++ b/resetLimitLambda/go.sum @@ -1,11 +1,15 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231002185428-1ab29298fc37 h1:xNdIzSF5QgGyF3jCf+a93zTJWeXEpl3q98OXca6CbuU= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231002185428-1ab29298fc37/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.323 h1:97/dn93DWrN1VfhAWQ2tV+xuE6oO/LO9rSsEsuC4PLU= github.com/aws/aws-sdk-go v1.44.323/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/resetLimitLambda/main.go b/resetLimitLambda/main.go index e3f1ab3..f49810b 100644 --- a/resetLimitLambda/main.go +++ b/resetLimitLambda/main.go @@ -9,6 +9,8 @@ import ( "sync" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/models" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" @@ -87,6 +89,17 @@ func init() { } func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + + corsResponse, err, passed := middleware.HandleCORS(event) + if !passed { + return corsResponse, err + } + + jwtResponse, _, err := jwt.JWTMiddleware()(event) + if err != nil || jwtResponse.StatusCode != http.StatusOK { + return jwtResponse, err + } + var concurrencyLimitRequest ConcurrencyLimitRequest if err := json.Unmarshal([]byte(event.Body), &concurrencyLimitRequest); err != nil { return events.APIGatewayProxyResponse{ @@ -130,17 +143,22 @@ func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.A } wg.Wait() + origin := event.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + err = updateConcurrencyLimitInDB(concurrencyLimitRequest.PendingLimit) if err != nil { return events.APIGatewayProxyResponse{ Body: "Failed to update concurrency limit in database", StatusCode: http.StatusInternalServerError, + Headers: corsHeaders, }, nil } return events.APIGatewayProxyResponse{ Body: "Successfully updated concurrency limit", StatusCode: http.StatusOK, + Headers: corsHeaders, }, nil } diff --git a/template.yaml b/template.yaml index 14334ab..8ba1525 100644 --- a/template.yaml +++ b/template.yaml @@ -5,6 +5,14 @@ Globals: Environment: Variables: ENVIRONMENT: PRODUCTION + RDS_BACKEND_PUBLIC_KEY_NAME: !If + - IsProd + - "PROD_RDS_BACKEND_PUBLIC_KEY" + - "STAGING_RDS_BACKEND_PUBLIC_KEY" + SESSION_COOKIE_NAME: !If + - IsProd + - "rds-session" + - "rds-session-staging" CreateFeatureFlagFunction: !If - IsProd - FeatureFlagBackendProdLam-CreateFeatureFlagFunctio-YOVQpOQ9W4hR diff --git a/updateFeatureFlag/go.mod b/updateFeatureFlag/go.mod index dbf477c..3b3ac0c 100644 --- a/updateFeatureFlag/go.mod +++ b/updateFeatureFlag/go.mod @@ -3,7 +3,7 @@ module updateFeatureFlag go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572 + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/go-playground/validator/v10 v10.14.1 @@ -13,6 +13,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/updateFeatureFlag/go.sum b/updateFeatureFlag/go.sum index 0c97be4..369d9fb 100644 --- a/updateFeatureFlag/go.sum +++ b/updateFeatureFlag/go.sum @@ -1,5 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572 h1:Hyc2jIH2tjWTafRMjPeznbRHpbw/1Q8UmRdHVoRrzE8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -16,6 +18,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/updateFeatureFlag/main.go b/updateFeatureFlag/main.go index 8b9a706..5bd1885 100644 --- a/updateFeatureFlag/main.go +++ b/updateFeatureFlag/main.go @@ -8,6 +8,8 @@ import ( "time" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" @@ -91,11 +93,21 @@ func updateFeatureFlag(flagId string, updateFeatureFlagRequest utils.UpdateFeatu func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { id, _ := request.PathParameters["flagId"] + corsResponse, err, passed := middleware.HandleCORS(request) + if !passed { + return corsResponse, err + } + + jwtResponse, _, err := jwt.JWTMiddleware()(request) + if err != nil || jwtResponse.StatusCode != http.StatusOK { + return jwtResponse, err + } + updateFeatureFlagRequest := utils.UpdateFeatureFlagRequest{} //marshal to updateFeatureFlag bytes := []byte(request.Body) - err := json.Unmarshal(bytes, &updateFeatureFlagRequest) + err = json.Unmarshal(bytes, &updateFeatureFlagRequest) if err != nil { log.Printf("Error in reading input \n %v", err) return utils.ClientError(http.StatusBadRequest, "Error in reading input") @@ -110,16 +122,26 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo return response, nil } + origin := request.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + found := utils.ValidateFeatureFlagStatus(updateFeatureFlagRequest.Status) if !found { response := events.APIGatewayProxyResponse{ Body: "Allowed values of Status are ENABLED, DISABLED", StatusCode: http.StatusBadRequest, + Headers: corsHeaders, } return response, nil } - return updateFeatureFlag(id, updateFeatureFlagRequest) + response, err := updateFeatureFlag(id, updateFeatureFlagRequest) + if err != nil { + return response, err + } + response.Headers = corsHeaders + + return response, nil } func main() { diff --git a/updateUserFeatureFlag/go.mod b/updateUserFeatureFlag/go.mod index f7195b0..4004e46 100644 --- a/updateUserFeatureFlag/go.mod +++ b/updateUserFeatureFlag/go.mod @@ -3,7 +3,7 @@ module updateUserFeatureFlag go 1.20 require ( - github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c + github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.44.284 github.com/go-playground/validator/v10 v10.14.1 @@ -13,6 +13,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/updateUserFeatureFlag/go.sum b/updateUserFeatureFlag/go.sum index c658b6c..369d9fb 100644 --- a/updateUserFeatureFlag/go.sum +++ b/updateUserFeatureFlag/go.sum @@ -1,7 +1,7 @@ -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572 h1:Hyc2jIH2tjWTafRMjPeznbRHpbw/1Q8UmRdHVoRrzE8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20231006175112-8daf955a8572/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c h1:KoOWwnVlR63D5qTJYJtzuOjUZ1wTVc76llysEZbNKds= -github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20240829163650-2f4fa0b0129c/go.mod h1:KIXxGKS5Hn3yFpu0nGfhYFrNF7yChl2yjGk8ZbRCso8= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2 h1:nqE0aZPsY4qkv5YfZ3g/nyNYn32Mrz45ypXDpbxb5no= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250105174329-d640dca1d9d2/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce h1:yvIuR8iGTJGyckU7DZv5LklDxGHYkib/joUAjRilozI= +github.com/Real-Dev-Squad/feature-flag-backend v0.0.0-20250121125844-dc297346c8ce/go.mod h1:5PjUN7WNRsmIsAFRipvthv2FR5EmDXPUKdpst0ejrDo= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.284 h1:Oc5Kubi43/VCkerlt3ZU3KpBju6BpNkoG3s7E8vj/O8= @@ -18,6 +18,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/updateUserFeatureFlag/main.go b/updateUserFeatureFlag/main.go index 79a8636..283381b 100644 --- a/updateUserFeatureFlag/main.go +++ b/updateUserFeatureFlag/main.go @@ -9,6 +9,8 @@ import ( "time" "github.com/Real-Dev-Squad/feature-flag-backend/database" + "github.com/Real-Dev-Squad/feature-flag-backend/jwt" + middleware "github.com/Real-Dev-Squad/feature-flag-backend/middlewares" "github.com/Real-Dev-Squad/feature-flag-backend/utils" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" @@ -82,8 +84,18 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, userId := req.PathParameters["userId"] flagId := req.PathParameters["flagId"] + corsResponse, err, passed := middleware.HandleCORS(req) + if !passed { + return corsResponse, err + } + + jwtResponse, _, err := jwt.JWTMiddleware()(req) + if err != nil || jwtResponse.StatusCode != http.StatusOK { + return jwtResponse, err + } + var requestBody utils.UpdateFeatureFlagUserMappingRequest - err := json.Unmarshal([]byte(req.Body), &requestBody) + err = json.Unmarshal([]byte(req.Body), &requestBody) if err != nil { log.Printf("Error unmarshal request body: \n %v", err) return utils.ClientError(http.StatusUnprocessableEntity, "Error unmarshalling request body") @@ -123,9 +135,13 @@ func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, return utils.ServerError(err) } + origin := req.Headers["Origin"] + corsHeaders := middleware.GetCORSHeaders(origin) + response := events.APIGatewayProxyResponse{ Body: string(resultJson), StatusCode: http.StatusOK, + Headers: corsHeaders, } return response, nil diff --git a/utils/Constants.go b/utils/Constants.go index d18bcab..a7691c0 100644 --- a/utils/Constants.go +++ b/utils/Constants.go @@ -20,4 +20,10 @@ const ( UserId = "userId" FlagId = "flagId" ConcurrencyDisablingLambda = 0 + SESSION_COOKIE_NAME_PROD = "rds-session" + SESSION_COOKIE_NAME_DEV = "rds-session-staging" + SESSION_COOKIE_NAME_LOCAL = "rds-session-development" + RDS_BACKEND_PUBLIC_KEY_NAME_DEV = "STAGING_RDS_BACKEND_PUBLIC_KEY" + RDS_BACKEND_PUBLIC_KEY_NAME_PROD = "PROD_RDS_BACKEND_PUBLIC_KEY" + RDS_BACKEND_PUBLIC_KEY_NAME_LOCAL = "publickey" )