Skip to content
129 changes: 129 additions & 0 deletions adapters/apigateway-api-key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package adapters

import (
"context"
"github.com/aws/aws-sdk-go-v2/service/apigateway"
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
"github.com/overmindtech/aws-source/adapterhelpers"
"github.com/overmindtech/sdp-go"
"strings"
)

// convertGetApiKeyOutputToApiKey converts a GetApiKeyOutput to an ApiKey
func convertGetApiKeyOutputToApiKey(output *apigateway.GetApiKeyOutput) *types.ApiKey {
return &types.ApiKey{
Id: output.Id,
Name: output.Name,
Enabled: output.Enabled,
CreatedDate: output.CreatedDate,
LastUpdatedDate: output.LastUpdatedDate,
StageKeys: output.StageKeys,
Tags: output.Tags,
}
}

func apiKeyListFunc(ctx context.Context, client *apigateway.Client, _ string) ([]*types.ApiKey, error) {
out, err := client.GetApiKeys(ctx, &apigateway.GetApiKeysInput{})
if err != nil {
return nil, err
}

var items []*types.ApiKey
for _, apiKey := range out.Items {
items = append(items, &apiKey)
}

return items, nil
}

func apiKeyOutputMapper(scope string, awsItem *types.ApiKey) (*sdp.Item, error) {
attributes, err := adapterhelpers.ToAttributesWithExclude(awsItem, "tags")
if err != nil {
return nil, err
}

item := sdp.Item{
Type: "apigateway-api-key",
UniqueAttribute: "Id",
Attributes: attributes,
Scope: scope,
Tags: awsItem.Tags,
}

for _, key := range awsItem.StageKeys {
// {restApiId}/{stage}
restAPIID := strings.Split(key, "/")[0]
if restAPIID != "" {
item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
Query: &sdp.Query{
Type: "apigateway-rest-api",
Method: sdp.QueryMethod_GET,
Query: restAPIID,
Scope: scope,
},
BlastPropagation: &sdp.BlastPropagation{
// They are tightly coupled, so we need to propagate both ways
In: true,
Out: true,
},
})
}
}

return &item, nil
}

func NewAPIGatewayApiKeyAdapter(client *apigateway.Client, accountID string, region string) *adapterhelpers.GetListAdapter[*types.ApiKey, *apigateway.Client, *apigateway.Options] {
return &adapterhelpers.GetListAdapter[*types.ApiKey, *apigateway.Client, *apigateway.Options]{
ItemType: "apigateway-api-key",
Client: client,
AccountID: accountID,
Region: region,
AdapterMetadata: apiKeyAdapterMetadata,
GetFunc: func(ctx context.Context, client *apigateway.Client, scope, query string) (*types.ApiKey, error) {
out, err := client.GetApiKey(ctx, &apigateway.GetApiKeyInput{
ApiKey: &query,
})
if err != nil {
return nil, err
}
return convertGetApiKeyOutputToApiKey(out), nil
},
ListFunc: apiKeyListFunc,
SearchFunc: func(ctx context.Context, client *apigateway.Client, scope string, query string) ([]*types.ApiKey, error) {
out, err := client.GetApiKeys(ctx, &apigateway.GetApiKeysInput{
NameQuery: &query,
})
if err != nil {
return nil, err
}

var items []*types.ApiKey
for _, apiKey := range out.Items {
items = append(items, &apiKey)
}

return items, nil
},
ItemMapper: func(_, scope string, awsItem *types.ApiKey) (*sdp.Item, error) {
return apiKeyOutputMapper(scope, awsItem)
},
}
}

var apiKeyAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{
Type: "apigateway-api-key",
DescriptiveName: "API Key",
Category: sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY,
SupportedQueryMethods: &sdp.AdapterSupportedQueryMethods{
Get: true,
List: true,
Search: true,
GetDescription: "Get an API Key by ID",
ListDescription: "List all API Keys",
SearchDescription: "Search for API Keys by their name",
},
TerraformMappings: []*sdp.TerraformMapping{
{TerraformQueryMap: "aws_api_gateway_api_key.id"},
},
})
60 changes: 60 additions & 0 deletions adapters/apigateway-api-key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package adapters

import (
"testing"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apigateway"
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
"github.com/overmindtech/aws-source/adapterhelpers"
"github.com/overmindtech/sdp-go"
)

func TestApiKeyOutputMapper(t *testing.T) {
awsItem := &types.ApiKey{
Id: aws.String("api-key-id"),
Name: aws.String("api-key-name"),
Enabled: true,
CreatedDate: aws.Time(time.Now()),
LastUpdatedDate: aws.Time(time.Now()),
StageKeys: []string{"rest-api-id/stage"},
Tags: map[string]string{"key": "value"},
}

item, err := apiKeyOutputMapper("scope", awsItem)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if err := item.Validate(); err != nil {
t.Error(err)
}

tests := adapterhelpers.QueryTests{
{
ExpectedType: "apigateway-rest-api",
ExpectedMethod: sdp.QueryMethod_GET,
ExpectedQuery: "rest-api-id",
ExpectedScope: "scope",
},
}

tests.Execute(t, item)
}

func TestNewAPIGatewayApiKeyAdapter(t *testing.T) {
config, account, region := adapterhelpers.GetAutoConfig(t)

client := apigateway.NewFromConfig(config)

adapter := NewAPIGatewayApiKeyAdapter(client, account, region)

test := adapterhelpers.E2ETest{
Adapter: adapter,
Timeout: 10 * time.Second,
SkipList: true,
}

test.Run(t)
}
143 changes: 143 additions & 0 deletions adapters/apigateway-authorizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package adapters

import (
"context"
"fmt"
"strings"

"github.com/aws/aws-sdk-go-v2/service/apigateway"
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
"github.com/overmindtech/aws-source/adapterhelpers"
"github.com/overmindtech/sdp-go"
)

// convertGetAuthorizerOutputToAuthorizer converts a GetAuthorizerOutput to an Authorizer
func convertGetAuthorizerOutputToAuthorizer(output *apigateway.GetAuthorizerOutput) *types.Authorizer {
return &types.Authorizer{
Id: output.Id,
Name: output.Name,
Type: output.Type,
ProviderARNs: output.ProviderARNs,
AuthType: output.AuthType,
AuthorizerUri: output.AuthorizerUri,
AuthorizerCredentials: output.AuthorizerCredentials,
IdentitySource: output.IdentitySource,
IdentityValidationExpression: output.IdentityValidationExpression,
AuthorizerResultTtlInSeconds: output.AuthorizerResultTtlInSeconds,
}
}

func authorizerOutputMapper(query, scope string, awsItem *types.Authorizer) (*sdp.Item, error) {
attributes, err := adapterhelpers.ToAttributesWithExclude(awsItem, "tags")
if err != nil {
return nil, err
}

item := sdp.Item{
Type: "apigateway-authorizer",
UniqueAttribute: "Id",
Attributes: attributes,
Scope: scope,
}

item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
Query: &sdp.Query{
Type: "apigateway-rest-api",
Method: sdp.QueryMethod_GET,
Query: strings.Split(query, "/")[0],
Scope: scope,
},
BlastPropagation: &sdp.BlastPropagation{
// They are tightly coupled, so we need to propagate the blast to the linked item
In: true,
Out: true,
},
})

return &item, nil
}

func NewAPIGatewayAuthorizerAdapter(client *apigateway.Client, accountID string, region string) *adapterhelpers.GetListAdapter[*types.Authorizer, *apigateway.Client, *apigateway.Options] {
return &adapterhelpers.GetListAdapter[*types.Authorizer, *apigateway.Client, *apigateway.Options]{
ItemType: "apigateway-authorizer",
Client: client,
AccountID: accountID,
Region: region,
AdapterMetadata: authorizerAdapterMetadata,
GetFunc: func(ctx context.Context, client *apigateway.Client, scope, query string) (*types.Authorizer, error) {
f := strings.Split(query, "/")
if len(f) != 2 {
return nil, &sdp.QueryError{
ErrorType: sdp.QueryError_NOTFOUND,
ErrorString: fmt.Sprintf("query must be in the format of: the rest-api-id/authorizer-id, but found: %s", query),
}
}
out, err := client.GetAuthorizer(ctx, &apigateway.GetAuthorizerInput{
RestApiId: &f[0],
AuthorizerId: &f[1],
})
if err != nil {
return nil, err
}
return convertGetAuthorizerOutputToAuthorizer(out), nil
},
DisableList: true,
SearchFunc: func(ctx context.Context, client *apigateway.Client, scope string, query string) ([]*types.Authorizer, error) {
f := strings.Split(query, "/")
var restAPIID string
var name string

switch len(f) {
case 1:
restAPIID = f[0]
case 2:
restAPIID = f[0]
name = f[1]
default:
return nil, &sdp.QueryError{
ErrorType: sdp.QueryError_NOTFOUND,
ErrorString: fmt.Sprintf(
"query must be in the format of: the rest-api-id/authorizer-id or rest-api-id, but found: %s",
query,
),
}
}

out, err := client.GetAuthorizers(ctx, &apigateway.GetAuthorizersInput{
RestApiId: &restAPIID,
})
if err != nil {
return nil, err
}

var items []*types.Authorizer
for _, authorizer := range out.Items {
if name != "" && strings.Contains(*authorizer.Name, name) {
items = append(items, &authorizer)
} else {
items = append(items, &authorizer)
}
}

return items, nil
},
ItemMapper: func(query, scope string, awsItem *types.Authorizer) (*sdp.Item, error) {
return authorizerOutputMapper(query, scope, awsItem)
},
}
}

var authorizerAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{
Type: "apigateway-authorizer",
DescriptiveName: "API Gateway Authorizer",
Category: sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY,
SupportedQueryMethods: &sdp.AdapterSupportedQueryMethods{
Get: true,
Search: true,
GetDescription: "Get an API Gateway Authorizer by its rest API ID and ID: rest-api-id/authorizer-id",
SearchDescription: "Search for API Gateway Authorizers by their rest API ID or with rest API ID and their name: rest-api-id/authorizer-name",
},
TerraformMappings: []*sdp.TerraformMapping{
{TerraformQueryMap: "aws_api_gateway_authorizer.id"},
},
})
63 changes: 63 additions & 0 deletions adapters/apigateway-authorizer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package adapters

import (
"github.com/overmindtech/sdp-go"
"testing"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apigateway"
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
"github.com/overmindtech/aws-source/adapterhelpers"
)

func TestAuthorizerOutputMapper(t *testing.T) {
awsItem := &types.Authorizer{
Id: aws.String("authorizer-id"),
Name: aws.String("authorizer-name"),
Type: types.AuthorizerTypeRequest,
ProviderARNs: []string{"arn:aws:iam::123456789012:role/service-role"},
AuthType: aws.String("custom"),
AuthorizerUri: aws.String("arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:my-function/invocations"),
AuthorizerCredentials: aws.String("arn:aws:iam::123456789012:role/service-role"),
IdentitySource: aws.String("method.request.header.Authorization"),
IdentityValidationExpression: aws.String(".*"),
AuthorizerResultTtlInSeconds: aws.Int32(300),
}

item, err := authorizerOutputMapper("rest-api-id", "scope", awsItem)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if err := item.Validate(); err != nil {
t.Error(err)
}

tests := adapterhelpers.QueryTests{
{
ExpectedType: "apigateway-rest-api",
ExpectedMethod: sdp.QueryMethod_GET,
ExpectedQuery: "rest-api-id",
ExpectedScope: "scope",
},
}

tests.Execute(t, item)
}

func TestNewAPIGatewayAuthorizerAdapter(t *testing.T) {
config, account, region := adapterhelpers.GetAutoConfig(t)

client := apigateway.NewFromConfig(config)

adapter := NewAPIGatewayAuthorizerAdapter(client, account, region)

test := adapterhelpers.E2ETest{
Adapter: adapter,
Timeout: 10 * time.Second,
SkipList: true,
}

test.Run(t)
}
Loading
Loading