Skip to content

Commit

Permalink
CertAuthz tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ananthb committed Oct 9, 2023
1 parent 321b780 commit 7710c76
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 15 deletions.
14 changes: 8 additions & 6 deletions internal/middleware/certauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ import (

const ServiceUnavailableMsg = "guru meditation error"

type authzFn func(context.Context, AuthenticatedRequestContext) (events.APIGatewayCustomAuthorizerResponse, error)

// CertAuthorizer returns a Lambda Authorizer function that authorizes requests
// based on the client certificate.
func CertAuthorizer(
namespace uuid.UUID,
) func(context.Context, AuthenticatedRequestContext) (events.APIGatewayCustomAuthorizerResponse, error) {
// based on the client certificate in the request context.
// If the certificate is valid, the Authorizer returns an Allow policy.
// The the certificate namespace does not match the configured namespace,
// the Authorizer returns a Deny policy.
func CertAuthorizer(namespace uuid.UUID) authzFn {
return func(ctx context.Context, rctx AuthenticatedRequestContext) (events.APIGatewayCustomAuthorizerResponse, error) {
block, _ := pem.Decode([]byte(rctx.Authentication.ClientCert.ClientCertPem))
if block == nil {
Expand All @@ -37,8 +40,7 @@ func CertAuthorizer(
return events.APIGatewayCustomAuthorizerResponse{}, errors.New(ServiceUnavailableMsg)
}

var pubKey JWK
pubKey.FromECDSA(cert.PublicKey)
pubKey := JWKFromECDSA(cert.PublicKey)
pubKeyStr, err := json.Marshal(pubKey)
if err != nil {
slog.ErrorCtx(ctx, "failed to marshal public key", "error", err)
Expand Down
117 changes: 117 additions & 0 deletions internal/middleware/certauthz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package middleware

import (
"context"
"reflect"
"testing"

"github.com/aws/aws-lambda-go/events"
"github.com/google/uuid"
)

type certAuthzTestCase struct {
ns uuid.UUID
in AuthenticatedRequestContext
out events.APIGatewayCustomAuthorizerResponse
err bool
}

var certAuthzTestCases = []certAuthzTestCase{
{
ns: uuid.MustParse("80485314-6c73-40ff-86c5-a5942a0f514f"),
in: AuthenticatedRequestContext{
APIGatewayCustomAuthorizerRequest: events.APIGatewayCustomAuthorizerRequest{
MethodArn: "arn:aws:execute-api:us-east-1:123456789012:api-id/stage-name/GET/resource-path",
},
Authentication: Authentication{
ClientCert: ClientCert{
ClientCertPem: "-----BEGIN CERTIFICATE-----\nMIIB4DCCAYagAwIBAgIBATAKBggqhkjOPQQDAjBeMS0wKwYDVQQKEyQ4MDQ4NTMx\nNC02YzczLTQwZmYtODZjNS1hNTk0MmEwZjUxNGYxLTArBgNVBAMTJGI5Mjg5ZGE3\nLTg4MTMtNTFlZC05NTdiLWI2YmM1YTRkNjQxNjAeFw0yMzA5MjAxODQyMDhaFw0y\nMzA5MjAxOTQyMDhaMF4xLTArBgNVBAoTJDgwNDg1MzE0LTZjNzMtNDBmZi04NmM1\nLWE1OTQyYTBmNTE0ZjEtMCsGA1UEAxMkYjkyODlkYTctODgxMy01MWVkLTk1N2It\nYjZiYzVhNGQ2NDE2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7pyPlY0DYYm7\n8D+BugKXrNDxXn2NfOibB+wV3IMGBRiL8D6rhJuTWcgMUmhuPI6Ssy9yKexpxNYV\nrxsvwF84u6M1MDMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB\nMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSAAwRQIhAPXeYIqFROWKpYrBwN9M\n96rmqQJcC9+x+N0n6PzVfB96AiA5d/3q16GG219mdSpc05CtFpYp4CW/oVzlwUQt\nc+gqcQ==\n-----END CERTIFICATE-----",
IssuerDN: "CN=b9289da7-8813-51ed-957b-b6bc5a4d6416,O=80485314-6c73-40ff-86c5-a5942a0f514f",
SubjectDN: "CN=b9289da7-8813-51ed-957b-b6bc5a4d6416,O=80485314-6c73-40ff-86c5-a5942a0f514f",
},
},
},
out: events.APIGatewayCustomAuthorizerResponse{
PrincipalID: "b9289da7-8813-51ed-957b-b6bc5a4d6416",
PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
Version: "2012-10-17",
Statement: []events.IAMPolicyStatement{
{
Action: []string{"execute-api:Invoke"},
Effect: "Allow",
Resource: []string{
"arn:aws:execute-api:us-east-1:123456789012:api-id/stage-name/GET/resource-path",
},
},
},
},
Context: map[string]interface{}{
"namespace": "80485314-6c73-40ff-86c5-a5942a0f514f",
"publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"107927077086532896835579100061901814530678651729391130141381261794751161959704\",\"y\":\"63295961781010443906011747343675505672305089399194087223428542059136675690683\"}",
},
},
},
{
in: AuthenticatedRequestContext{},
err: true,
},
{
in: AuthenticatedRequestContext{
Authentication: Authentication{
ClientCert: ClientCert{
ClientCertPem: "-----BEGIN CERTIFICATE REQUEST-----\nMIIBGTCBwAIBADBeMS0wKwYDVQQDDCQ3NmViZGJkNS1kYzQwLTU4YzEtYTEwMS0x\nY2U2YjBlZDllYjAxLTArBgNVBAoMJGQyNTdjMTY3LTFhOTgtNDkwZS04MWEzLWIz\nOTVmMWFiZmY3YTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIRKO/ou3QfVp5Ym\naKyBForLVwIKx67Ts9q1tC2lyGXCTYhFAFpE8zBSq2NCWT1QaFBF4GBh4Ve4XNyH\nf/l+B/agADAKBggqhkjOPQQDAgNIADBFAiAaAejXa589rggsr3VHeTAkbi1ULSXw\njDIeM4TUVgM2cgIhAOy09QkVAYVeq2ksf6n/kCMm2CAZNX5wLjVzpRUCaD6T\n-----END CERTIFICATE REQUEST-----",
},
},
},
err: true,
},
{
ns: uuid.MustParse("b9289da7-8813-51ed-957b-b6bc5a4d6416"),
in: AuthenticatedRequestContext{
APIGatewayCustomAuthorizerRequest: events.APIGatewayCustomAuthorizerRequest{
MethodArn: "arn:aws:execute-api:us-east-1:123456789012:api-id/stage-name/GET/resource-path",
},
Authentication: Authentication{
ClientCert: ClientCert{
ClientCertPem: "-----BEGIN CERTIFICATE-----\nMIIB4DCCAYagAwIBAgIBATAKBggqhkjOPQQDAjBeMS0wKwYDVQQKEyQ4MDQ4NTMx\nNC02YzczLTQwZmYtODZjNS1hNTk0MmEwZjUxNGYxLTArBgNVBAMTJGI5Mjg5ZGE3\nLTg4MTMtNTFlZC05NTdiLWI2YmM1YTRkNjQxNjAeFw0yMzA5MjAxODQyMDhaFw0y\nMzA5MjAxOTQyMDhaMF4xLTArBgNVBAoTJDgwNDg1MzE0LTZjNzMtNDBmZi04NmM1\nLWE1OTQyYTBmNTE0ZjEtMCsGA1UEAxMkYjkyODlkYTctODgxMy01MWVkLTk1N2It\nYjZiYzVhNGQ2NDE2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7pyPlY0DYYm7\n8D+BugKXrNDxXn2NfOibB+wV3IMGBRiL8D6rhJuTWcgMUmhuPI6Ssy9yKexpxNYV\nrxsvwF84u6M1MDMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB\nMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSAAwRQIhAPXeYIqFROWKpYrBwN9M\n96rmqQJcC9+x+N0n6PzVfB96AiA5d/3q16GG219mdSpc05CtFpYp4CW/oVzlwUQt\nc+gqcQ==\n-----END CERTIFICATE-----",
IssuerDN: "CN=b9289da7-8813-51ed-957b-b6bc5a4d6416,O=80485314-6c73-40ff-86c5-a5942a0f514f",
SubjectDN: "CN=b9289da7-8813-51ed-957b-b6bc5a4d6416,O=80485314-6c73-40ff-86c5-a5942a0f514f",
},
},
},
out: events.APIGatewayCustomAuthorizerResponse{
PrincipalID: "b9289da7-8813-51ed-957b-b6bc5a4d6416",
PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
Version: "2012-10-17",
Statement: []events.IAMPolicyStatement{
{
Action: []string{"execute-api:Invoke"},
Effect: "Deny",
Resource: []string{
"arn:aws:execute-api:us-east-1:123456789012:api-id/stage-name/GET/resource-path",
},
},
},
},
},
},
}

func TestCertAuthorizer(t *testing.T) {
for _, tc := range certAuthzTestCases {
out, err := CertAuthorizer(tc.ns)(context.Background(), tc.in)
if tc.err {
if err == nil {
t.Errorf("expected error, got nil")
}
continue
}
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("expected %v, got %v", tc.out, out)
}
}
}
20 changes: 13 additions & 7 deletions internal/middleware/requestcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ func (j *JWK) UnmarshalJSON(data []byte) error {
return nil
}

func (j *JWK) FromECDSA(key *ecdsa.PublicKey) {
j.KeyType = keyTypeEC
j.Curve = curveP256
j.X = key.X.String()
j.Y = key.Y.String()
}

func (j JWK) ToECDSA() (*ecdsa.PublicKey, bool) {
var x, y big.Int
if _, ok := x.SetString(j.X, 10); !ok {
Expand All @@ -95,6 +88,19 @@ func (j JWK) ToECDSA() (*ecdsa.PublicKey, bool) {
}, true
}

func (j *JWK) FromECDSA(key *ecdsa.PublicKey) {
j.KeyType = keyTypeEC
j.Curve = curveP256
j.X = key.X.String()
j.Y = key.Y.String()
}

func JWKFromECDSA(key *ecdsa.PublicKey) JWK {
var j JWK
j.FromECDSA(key)
return j
}

type AuthenticatedRequestContext struct {
events.APIGatewayCustomAuthorizerRequest
Authentication Authentication `json:"authentication"`
Expand Down
3 changes: 1 addition & 2 deletions internal/middleware/tlsident.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ func TLSIdentifier(namespace uuid.UUID) func(http.Handler) http.Handler {
return
}

var j JWK
j.FromECDSA(cert.PublicKey)
j := JWKFromECDSA(cert.PublicKey)

rctx := AuthorizedRequestContext{
Authorizer: Authorizer{
Expand Down

0 comments on commit 7710c76

Please sign in to comment.