|
package main |
|
|
|
import ( |
|
"context" |
|
"errors" |
|
"strings" |
|
|
|
"github.com/aws/aws-lambda-go/events" |
|
"github.com/aws/aws-lambda-go/lambda" |
|
) |
|
|
|
const ( |
|
APIKeyHeader = "x-api-key" |
|
AppIDHeader = "x-app-id" |
|
|
|
// APIKey is our one and only API key for now |
|
// TODO: in real life you probably want to fetch these from a DB or whatever your source of API keys is |
|
APIKey = "some-api-key-text-goes-here" |
|
// AppID is our only allowed app for now |
|
AppID = "myapp" |
|
) |
|
|
|
func validAPIKey(apiKey, appID string) bool { |
|
return apiKey == APIKey && appID == AppID |
|
} |
|
|
|
// resourceWildcard returns a wildcard resource specifier for the policy document |
|
// so that we authorize the API key user for all API's in this system with a |
|
// single call. This is desired because API Gateway caches this policy, so if |
|
// we use the exact methodArn of the HTTP call, it winds up then blocking that |
|
// same user from calling other APIs because the policy for their API key is |
|
// cached with just the first API call's methodArn. See: |
|
// https://forum.serverless.com/t/help-really-weird-access-denied-issue/12676/6 |
|
// It strips off everything after the first encountered slash, and set that to |
|
// the wildcard. e.g. a methodArn of: |
|
// arn:aws:execute-api:us-east-2:123456789000:abcde12345/dev/POST/widgets |
|
// will be converted to: |
|
// arn:aws:execute-api:us-east-2:123456789000:abcde12345/* |
|
func resourceWildcard(methodArn string) string { |
|
parts := strings.Split(methodArn, "/") |
|
return parts[0] + "/*" |
|
} |
|
|
|
func generatePolicy(principalID, effect, resource string) events.APIGatewayCustomAuthorizerResponse { |
|
authResponse := events.APIGatewayCustomAuthorizerResponse{PrincipalID: principalID} |
|
|
|
if effect != "" && resource != "" { |
|
authResponse.PolicyDocument = events.APIGatewayCustomAuthorizerPolicy{ |
|
Version: "2012-10-17", |
|
Statement: []events.IAMPolicyStatement{ |
|
{ |
|
Action: []string{"execute-api:Invoke"}, |
|
Effect: effect, |
|
Resource: []string{resource}, |
|
}, |
|
}, |
|
} |
|
} |
|
return authResponse |
|
} |
|
|
|
func handler(ctx context.Context, request events.APIGatewayCustomAuthorizerRequestTypeRequest) (events.APIGatewayCustomAuthorizerResponse, error) { |
|
if validAPIKey(request.Headers[APIKeyHeader], request.Headers[AppIDHeader]) { |
|
return generatePolicy("user", "Allow", resourceWildcard(request.MethodArn)), nil |
|
} |
|
|
|
return events.APIGatewayCustomAuthorizerResponse{}, errors.New("Unauthorized") |
|
} |
|
|
|
func main() { |
|
lambda.Start(handler) |
|
} |