Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/apigateway authorization scopes #4533

Merged
merged 3 commits into from
Jun 4, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions aws/resource_aws_api_gateway_method.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ func resourceAwsApiGatewayMethod() *schema.Resource {
Optional: true,
},

"authorization_scopes": &schema.Schema{
Type: schema.TypeList,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the ordering matter for this attribute? If not, we can use schema.TypeSet which has some nice helpers for doing set differences. See also: https://www.terraform.io/docs/extend/schemas/schema-types.html#typeset

Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"api_key_required": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -126,6 +132,10 @@ func resourceAwsApiGatewayMethodCreate(d *schema.ResourceData, meta interface{})
input.AuthorizerId = aws.String(v.(string))
}

if v, ok := d.GetOk("authorization_scopes"); ok {
input.AuthorizationScopes = expandStringList(v.([]interface{}))
}

if v, ok := d.GetOk("request_validator_id"); ok {
input.RequestValidatorId = aws.String(v.(string))
}
Expand Down Expand Up @@ -165,6 +175,7 @@ func resourceAwsApiGatewayMethodRead(d *schema.ResourceData, meta interface{}) e
d.Set("api_key_required", out.ApiKeyRequired)
d.Set("authorization", out.AuthorizationType)
d.Set("authorizer_id", out.AuthorizerId)
d.Set("authorization_scopes", out.AuthorizationScopes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When setting non-scalar attributes, we should perform error checking:

if err := d.Set("authorization_scopes", out.AUthorizationScopes); err != nil {
  return fmt.Errorf("error setting authorization_scopes: %s", err)
}

d.Set("request_models", aws.StringValueMap(out.RequestModels))
d.Set("request_validator_id", out.RequestValidatorId)

Expand Down Expand Up @@ -229,6 +240,35 @@ func resourceAwsApiGatewayMethodUpdate(d *schema.ResourceData, meta interface{})
})
}

if d.HasChange("authorization_scopes") {
o, n := d.GetChange("authorization_scopes")
path := "/authorizationScopes"

old := o.([]interface{})
new := n.([]interface{})

// Remove every authorization scope. Simpler to remove and add new ones,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note here: If we switch this to schema.TypeSet, there are some set difference functions available: https://godoc.org/github.com/hashicorp/terraform/helper/schema#Set.Difference

It seems like we should try to reduce any downtime associated with updating these if possible.

// since there are no replacings.
for _, v := range old {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(path),
Value: aws.String(v.(string)),
})
}

// Handle additions
if len(new) > 0 {
for _, v := range new {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(path),
Value: aws.String(v.(string)),
})
}
}
}

if d.HasChange("api_key_required") {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Expand Down
126 changes: 125 additions & 1 deletion aws/resource_aws_api_gateway_method_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,46 @@ func TestAccAWSAPIGatewayMethod_customauthorizer(t *testing.T) {
})
}

func TestAccAWSAPIGatewayMethod_cognitoauthorizer(t *testing.T) {
var conf apigateway.Method
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayMethodDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayMethodConfigWithCognitoAuthorizer(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf),
testAccCheckAWSAPIGatewayMethodAttributes(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_method.test", "http_method", "GET"),
resource.TestCheckResourceAttr(
"aws_api_gateway_method.test", "authorization", "COGNITO_USER_POOLS"),
resource.TestMatchResourceAttr(
"aws_api_gateway_method.test", "authorizer_id", regexp.MustCompile("^[a-z0-9]{6}$")),
resource.TestCheckResourceAttr(
"aws_api_gateway_method.test", "request_models.application/json", "Error"),
),
},

{
Config: testAccAWSAPIGatewayMethodConfigUpdate(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf),
testAccCheckAWSAPIGatewayMethodAttributesUpdate(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_method.test", "authorization", "NONE"),
resource.TestCheckResourceAttr(
"aws_api_gateway_method.test", "authorizer_id", ""),
),
},
},
})
}

func TestAccAWSAPIGatewayMethod_customrequestvalidator(t *testing.T) {
var conf apigateway.Method
rInt := acctest.RandInt()
Expand Down Expand Up @@ -130,7 +170,7 @@ func testAccCheckAWSAPIGatewayMethodAttributes(conf *apigateway.Method) resource
if *conf.HttpMethod != "GET" {
return fmt.Errorf("Wrong HttpMethod: %q", *conf.HttpMethod)
}
if *conf.AuthorizationType != "NONE" && *conf.AuthorizationType != "CUSTOM" {
if *conf.AuthorizationType != "NONE" && *conf.AuthorizationType != "CUSTOM" && *conf.AuthorizationType != "COGNITO_USER_POOLS" {
return fmt.Errorf("Wrong Authorization: %q", *conf.AuthorizationType)
}

Expand Down Expand Up @@ -337,6 +377,90 @@ resource "aws_api_gateway_method" "test" {
}`, rInt, rInt, rInt, rInt, rInt)
}

func testAccAWSAPIGatewayMethodConfigWithCognitoAuthorizer(rInt int) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
name = "tf-acc-test-cognito-auth-%d"
}

resource "aws_iam_role" "invocation_role" {
name = "tf_acc_api_gateway_auth_invocation_role-%d"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_iam_role" "iam_for_lambda" {
name = "tf_acc_iam_for_lambda_api_gateway_authorizer-%d"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_cognito_user_pool" "test" {
name = "tf-acc-test-cognito-pool"
}


resource "aws_api_gateway_authorizer" "test" {
name = "tf-acc-test-cognito-authorizer"
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
authorizer_uri = "${aws_cognito_user_pool.test.arn}"
identity_source = "method.request.header.Authorization"
provider_arns = ["${aws_cognito_user_pool.test.arn}"]
type = "COGNITO_USER_POOLS"
}

resource "aws_api_gateway_resource" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
path_part = "test"
}

resource "aws_api_gateway_method" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "GET"
authorization = "COGNITO_USER_POOLS"
authorizer_id = "${aws_api_gateway_authorizer.test.id}"
authorization_scopes = ["test.read", "test.write"]

request_models = {
"application/json" = "Error"
}

request_parameters = {
"method.request.header.Content-Type" = false
"method.request.querystring.page" = true
}
}`, rInt, rInt, rInt)
}

func testAccAWSAPIGatewayMethodConfig(rInt int) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
Expand Down
5 changes: 3 additions & 2 deletions website/docs/r/api_gateway_method.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,15 @@ The following arguments are supported:
* `rest_api_id` - (Required) The ID of the associated REST API
* `resource_id` - (Required) The API resource ID
* `http_method` - (Required) The HTTP Method (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `ANY`)
* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`, `AWS_IAM`)
* `authorizer_id` - (Optional) The authorizer id to be used when the authorization is `CUSTOM`
* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`, `AWS_IAM`, `COGNITO_USER_POOLS`)
* `authorizer_id` - (Optional) The authorizer id to be used when the authorization is `CUSTOM` or `COGNITO_USER_POOLS`
* `api_key_required` - (Optional) Specify if the method requires an API key
* `request_models` - (Optional) A map of the API models used for the request's content type
where key is the content type (e.g. `application/json`)
and value is either `Error`, `Empty` (built-in models) or `aws_api_gateway_model`'s `name`.
* `request_validator_id` - (Optional) The ID of a `aws_api_gateway_request_validator`
* `request_parameters` - (Optional) A map of request query string parameters and headers that should be passed to the integration.
* `authorization_scopes` - (Optional) The authorization scopes used when the authorization is `COGNITO_USER_POOLS`
For example:
```hcl
request_parameters = {
Expand Down