Skip to content

Commit

Permalink
feat: cors support for api resource in azure
Browse files Browse the repository at this point in the history
  • Loading branch information
davemooreuws committed Oct 25, 2023
1 parent 135b8df commit cd01b35
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
76 changes: 69 additions & 7 deletions cloud/azure/deploy/api/apimanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ package api

import (
"fmt"
"strconv"
"strings"

"github.com/nitrictech/nitric/cloud/common/deploy/cors"
"github.com/nitrictech/nitric/cloud/common/deploy/resources"

"github.com/getkin/kin-openapi/openapi3"
Expand All @@ -32,6 +34,7 @@ import (
"github.com/nitrictech/nitric/cloud/azure/deploy/utils"
common "github.com/nitrictech/nitric/cloud/common/deploy/tags"
commonutils "github.com/nitrictech/nitric/cloud/common/deploy/utils"
v1 "github.com/nitrictech/nitric/core/pkg/api/nitric/v1"
)

type AzureApiManagementArgs struct {
Expand All @@ -42,6 +45,7 @@ type AzureApiManagementArgs struct {
OpenAPISpec *openapi3.T
Apps map[string]*exec.ContainerApp
ManagedIdentity *managedidentity.UserAssignedIdentity
Cors *v1.ApiCorsDefinition
}

type AzureApiManagement struct {
Expand All @@ -54,6 +58,8 @@ type AzureApiManagement struct {

const policyTemplate = `<policies><inbound><base /><set-backend-service base-url="https://%s" />%s<authentication-managed-identity resource="%s" client-id="%s" /><set-header name="X-Forwarded-Authorization" exists-action="override"><value>@(context.Request.Headers.GetValueOrDefault("Authorization",""))</value></set-header></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>`

const corsPolicyTemplate = `<policies><inbound><base />%s</inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>`

const jwtTemplate = `<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-expiration-time="false">
<openid-config url="%s.well-known/openid-configuration" />
<required-claims>
Expand Down Expand Up @@ -181,6 +187,13 @@ func NewAzureApiManagement(ctx *pulumi.Context, name string, args *AzureApiManag
}
}

// this.api.id returns a URL path, which is the incorrect value here.
// We instead need the value passed to apiId in the api creation above.
// However, we want to maintain the pulumi dependency, so we need to keep the 'apply' call.
apiId := res.Api.ID().ToStringOutput().ApplyT(func(id string) string {
return name
}).(pulumi.StringOutput)

for _, pathItem := range args.OpenAPISpec.Paths {
for _, op := range pathItem.Operations() {
if v, ok := op.Extensions["x-nitric-target"]; ok {
Expand Down Expand Up @@ -214,13 +227,6 @@ func NewAzureApiManagement(ctx *pulumi.Context, name string, args *AzureApiManag
continue
}

// this.api.id returns a URL path, which is the incorrect value here.
// We instead need the value passed to apiId in the api creation above.
// However, we want to maintain the pulumi dependency, so we need to keep the 'apply' call.
apiId := res.Api.ID().ToStringOutput().ApplyT(func(id string) string {
return name
}).(pulumi.StringOutput)

_ = ctx.Log.Info("op policy "+op.OperationID+" , name "+name, &pulumi.LogArgs{Ephemeral: true})

_, err = apimanagement.NewApiOperationPolicy(ctx, utils.ResourceName(ctx, name+"-"+op.OperationID, utils.ApiOperationPolicyRT), &apimanagement.ApiOperationPolicyArgs{
Expand All @@ -239,6 +245,41 @@ func NewAzureApiManagement(ctx *pulumi.Context, name string, args *AzureApiManag
}
}

if args.Cors != nil {
corsConfig, err := cors.GetCorsConfig(args.Cors)
if err != nil {
return nil, err
}

corsTemplate := fmt.Sprintf(`<cors allow-credentials="%s">`, strconv.FormatBool(corsConfig.AllowCredentials))

// origins
corsTemplate = corsTemplate + addCorsElements(corsConfig.AllowOrigins, "allowed-origins", "origin", "")

// methods
corsTemplate = corsTemplate + addCorsElements(corsConfig.AllowMethods, "allowed-methods", "method", fmt.Sprintf(`preflight-result-max-age="%s"`, strconv.FormatInt(int64(corsConfig.MaxAge), 10)))

// headers
corsTemplate = corsTemplate + addCorsElements(corsConfig.AllowHeaders, "allowed-headers", "header", "")

// expose headers
corsTemplate = corsTemplate + addCorsElements(corsConfig.ExposeHeaders, "expose-headers", "header", "")

corsTemplate = corsTemplate + "</cors>"

_, err = apimanagement.NewApiPolicy(ctx, utils.ResourceName(ctx, name+"-cors", utils.ApiOperationPolicyRT), &apimanagement.ApiPolicyArgs{
ResourceGroupName: args.ResourceGroupName,
ApiId: apiId,
ServiceName: res.Service.Name,
PolicyId: pulumi.String("policy"),
Format: pulumi.String("xml"),
Value: pulumi.Sprintf(corsPolicyTemplate, corsTemplate),
})
if err != nil {
return nil, errors.WithMessage(err, "NewApiPolicy "+name+"-cors")
}
}

ctx.Export("api:"+name, res.Service.GatewayUrl)

return res, ctx.RegisterResourceOutputs(res, pulumi.Map{
Expand All @@ -247,3 +288,24 @@ func NewAzureApiManagement(ctx *pulumi.Context, name string, args *AzureApiManag
"api": res.Api,
})
}

func addCorsElements(values []string, tag string, childTag string, attrs string) string {
if len(values) == 0 {
return ""
}

template := ""

if attrs != "" {
template = fmt.Sprintf(`<%s %s>`, tag, attrs)
} else {
template = fmt.Sprintf(`<%s>`, tag)
}

for _, v := range values {
template = template + fmt.Sprintf("<%s>%s</%s>", childTag, v, childTag)
}
template = template + fmt.Sprintf(`</%s>`, tag)

return template
}
4 changes: 3 additions & 1 deletion cloud/azure/deploy/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,10 @@ func (d *DeployServer) Up(request *deploy.DeployUpRequest, stream deploy.DeployS
MongoDatabaseConnectionString: mongoConnectionString,
Config: *euConfig.ContainerApps,
Schedules: schedules,
StackID: stackID,
}, pulumi.Parent(contEnv))
if err != nil {
return status.Errorf(codes.Internal, "error occurred whilst creating container app %s", eu.Name)
return status.Errorf(codes.Internal, "error occurred whilst creating container app %s", err.Error())
}
} else {
return status.Errorf(codes.InvalidArgument, "unsupported target for function config %s", eu.Name)
Expand Down Expand Up @@ -380,6 +381,7 @@ func (d *DeployServer) Up(request *deploy.DeployUpRequest, stream deploy.DeployS
Apps: apps,
ManagedIdentity: contEnv.ManagedUser,
StackID: stackID,
Cors: a.GetApi().GetCors(),
})
if err != nil {
return err
Expand Down

0 comments on commit cd01b35

Please sign in to comment.