|
1 | 1 | from samtranslator.model import PropertyType, Resource
|
2 | 2 | from samtranslator.model.types import is_type, one_of, is_str, list_of
|
3 |
| -from samtranslator.model.intrinsics import ref |
| 3 | +from samtranslator.model.intrinsics import ref, fnSub |
4 | 4 | from samtranslator.model.exceptions import InvalidResourceException
|
| 5 | +from samtranslator.translator.arn_generator import ArnGenerator |
| 6 | + |
| 7 | +APIGATEWAY_AUTHORIZER_KEY = "x-amazon-apigateway-authorizer" |
5 | 8 |
|
6 | 9 |
|
7 | 10 | class ApiGatewayV2HttpApi(Resource):
|
@@ -57,39 +60,209 @@ class ApiGatewayV2ApiMapping(Resource):
|
57 | 60 |
|
58 | 61 | class ApiGatewayV2Authorizer(object):
|
59 | 62 | def __init__(
|
60 |
| - self, api_logical_id=None, name=None, authorization_scopes=[], jwt_configuration={}, id_source=None, |
| 63 | + self, |
| 64 | + api_logical_id=None, |
| 65 | + name=None, |
| 66 | + authorization_scopes=None, |
| 67 | + jwt_configuration=None, |
| 68 | + id_source=None, |
| 69 | + function_arn=None, |
| 70 | + function_invoke_role=None, |
| 71 | + identity=None, |
| 72 | + authorizer_payload_format_version=None, |
| 73 | + enable_simple_responses=None, |
61 | 74 | ):
|
62 | 75 | """
|
63 | 76 | Creates an authorizer for use in V2 Http Apis
|
64 | 77 | """
|
65 |
| - if authorization_scopes is not None and not isinstance(authorization_scopes, list): |
66 |
| - raise InvalidResourceException(api_logical_id, "AuthorizationScopes must be a list.") |
67 |
| - |
68 |
| - # Currently only one type of auth |
69 |
| - self.auth_type = "oauth2" |
70 |
| - |
71 | 78 | self.api_logical_id = api_logical_id
|
72 | 79 | self.name = name
|
73 | 80 | self.authorization_scopes = authorization_scopes
|
74 |
| - |
75 |
| - # Validate necessary parameters exist |
76 |
| - if not jwt_configuration: |
77 |
| - raise InvalidResourceException(api_logical_id, name + " Authorizer must define 'JwtConfiguration'.") |
78 | 81 | self.jwt_configuration = jwt_configuration
|
79 |
| - if not id_source: |
80 |
| - raise InvalidResourceException(api_logical_id, name + " Authorizer must define 'IdentitySource'.") |
81 | 82 | self.id_source = id_source
|
| 83 | + self.function_arn = function_arn |
| 84 | + self.function_invoke_role = function_invoke_role |
| 85 | + self.identity = identity |
| 86 | + self.authorizer_payload_format_version = authorizer_payload_format_version |
| 87 | + self.enable_simple_responses = enable_simple_responses |
| 88 | + |
| 89 | + self._validate_input_parameters() |
| 90 | + |
| 91 | + authorizer_type = self._get_auth_type() |
| 92 | + |
| 93 | + # Validate necessary parameters exist |
| 94 | + if authorizer_type == "JWT": |
| 95 | + self._validate_jwt_authorizer() |
| 96 | + |
| 97 | + if authorizer_type == "REQUEST": |
| 98 | + self._validate_lambda_authorizer() |
| 99 | + |
| 100 | + def _get_auth_type(self): |
| 101 | + if self.jwt_configuration: |
| 102 | + return "JWT" |
| 103 | + return "REQUEST" |
| 104 | + |
| 105 | + def _validate_input_parameters(self): |
| 106 | + authorizer_type = self._get_auth_type() |
| 107 | + |
| 108 | + if self.authorization_scopes is not None and not isinstance(self.authorization_scopes, list): |
| 109 | + raise InvalidResourceException(self.api_logical_id, "AuthorizationScopes must be a list.") |
| 110 | + |
| 111 | + if self.authorization_scopes is not None and not authorizer_type == "JWT": |
| 112 | + raise InvalidResourceException( |
| 113 | + self.api_logical_id, "AuthorizationScopes must be defined only for OAuth2 Authorizer." |
| 114 | + ) |
| 115 | + |
| 116 | + if self.jwt_configuration is not None and not authorizer_type == "JWT": |
| 117 | + raise InvalidResourceException( |
| 118 | + self.api_logical_id, "JwtConfiguration must be defined only for OAuth2 Authorizer." |
| 119 | + ) |
| 120 | + |
| 121 | + if self.id_source is not None and not authorizer_type == "JWT": |
| 122 | + raise InvalidResourceException( |
| 123 | + self.api_logical_id, "IdentitySource must be defined only for OAuth2 Authorizer." |
| 124 | + ) |
| 125 | + |
| 126 | + if self.function_arn is not None and not authorizer_type == "REQUEST": |
| 127 | + raise InvalidResourceException( |
| 128 | + self.api_logical_id, "FunctionArn must be defined only for Lambda Authorizer." |
| 129 | + ) |
| 130 | + |
| 131 | + if self.function_invoke_role is not None and not authorizer_type == "REQUEST": |
| 132 | + raise InvalidResourceException( |
| 133 | + self.api_logical_id, "FunctionInvokeRole must be defined only for Lambda Authorizer." |
| 134 | + ) |
| 135 | + |
| 136 | + if self.identity is not None and not authorizer_type == "REQUEST": |
| 137 | + raise InvalidResourceException(self.api_logical_id, "Identity must be defined only for Lambda Authorizer.") |
| 138 | + |
| 139 | + if self.authorizer_payload_format_version is not None and not authorizer_type == "REQUEST": |
| 140 | + raise InvalidResourceException( |
| 141 | + self.api_logical_id, "AuthorizerPayloadFormatVersion must be defined only for Lambda Authorizer." |
| 142 | + ) |
| 143 | + |
| 144 | + if self.enable_simple_responses is not None and not authorizer_type == "REQUEST": |
| 145 | + raise InvalidResourceException( |
| 146 | + self.api_logical_id, "EnableSimpleResponses must be defined only for Lambda Authorizer." |
| 147 | + ) |
| 148 | + |
| 149 | + def _validate_jwt_authorizer(self): |
| 150 | + if not self.jwt_configuration: |
| 151 | + raise InvalidResourceException( |
| 152 | + self.api_logical_id, self.name + " OAuth2 Authorizer must define 'JwtConfiguration'." |
| 153 | + ) |
| 154 | + if not self.id_source: |
| 155 | + raise InvalidResourceException( |
| 156 | + self.api_logical_id, self.name + " OAuth2 Authorizer must define 'IdentitySource'." |
| 157 | + ) |
| 158 | + |
| 159 | + def _validate_lambda_authorizer(self): |
| 160 | + if not self.function_arn: |
| 161 | + raise InvalidResourceException( |
| 162 | + self.api_logical_id, self.name + " Lambda Authorizer must define 'FunctionArn'." |
| 163 | + ) |
| 164 | + if not self.authorizer_payload_format_version: |
| 165 | + raise InvalidResourceException( |
| 166 | + self.api_logical_id, self.name + " Lambda Authorizer must define 'AuthorizerPayloadFormatVersion'." |
| 167 | + ) |
82 | 168 |
|
83 | 169 | def generate_openapi(self):
|
84 | 170 | """
|
85 | 171 | Generates OAS for the securitySchemes section
|
86 | 172 | """
|
87 |
| - openapi = { |
88 |
| - "type": self.auth_type, |
89 |
| - "x-amazon-apigateway-authorizer": { |
| 173 | + authorizer_type = self._get_auth_type() |
| 174 | + |
| 175 | + if authorizer_type == "JWT": |
| 176 | + openapi = {"type": "oauth2"} |
| 177 | + openapi[APIGATEWAY_AUTHORIZER_KEY] = { |
90 | 178 | "jwtConfiguration": self.jwt_configuration,
|
91 | 179 | "identitySource": self.id_source,
|
92 | 180 | "type": "jwt",
|
93 |
| - }, |
94 |
| - } |
| 181 | + } |
| 182 | + |
| 183 | + if authorizer_type == "REQUEST": |
| 184 | + openapi = { |
| 185 | + "type": "apiKey", |
| 186 | + "name": "Unused", |
| 187 | + "in": "header", |
| 188 | + } |
| 189 | + openapi[APIGATEWAY_AUTHORIZER_KEY] = {"type": "request"} |
| 190 | + |
| 191 | + # Generate the lambda arn |
| 192 | + partition = ArnGenerator.get_partition_name() |
| 193 | + resource = "lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations" |
| 194 | + authorizer_uri = fnSub( |
| 195 | + ArnGenerator.generate_arn( |
| 196 | + partition=partition, service="apigateway", resource=resource, include_account_id=False |
| 197 | + ), |
| 198 | + {"__FunctionArn__": self.function_arn}, |
| 199 | + ) |
| 200 | + openapi[APIGATEWAY_AUTHORIZER_KEY]["authorizerUri"] = authorizer_uri |
| 201 | + |
| 202 | + # Set authorizerCredentials if present |
| 203 | + function_invoke_role = self._get_function_invoke_role() |
| 204 | + if function_invoke_role: |
| 205 | + openapi[APIGATEWAY_AUTHORIZER_KEY]["authorizerCredentials"] = function_invoke_role |
| 206 | + |
| 207 | + # Set authorizerResultTtlInSeconds if present |
| 208 | + reauthorize_every = self._get_reauthorize_every() |
| 209 | + if reauthorize_every is not None: |
| 210 | + openapi[APIGATEWAY_AUTHORIZER_KEY]["authorizerResultTtlInSeconds"] = reauthorize_every |
| 211 | + |
| 212 | + # Set identitySource if present |
| 213 | + if self.identity: |
| 214 | + openapi[APIGATEWAY_AUTHORIZER_KEY]["identitySource"] = self._get_identity_source() |
| 215 | + |
| 216 | + # Set authorizerPayloadFormatVersion. It's a required parameter |
| 217 | + openapi[APIGATEWAY_AUTHORIZER_KEY][ |
| 218 | + "authorizerPayloadFormatVersion" |
| 219 | + ] = self.authorizer_payload_format_version |
| 220 | + |
| 221 | + # Set authorizerPayloadFormatVersion. It's a required parameter |
| 222 | + if self.enable_simple_responses: |
| 223 | + openapi[APIGATEWAY_AUTHORIZER_KEY]["enableSimpleResponses"] = self.enable_simple_responses |
| 224 | + |
95 | 225 | return openapi
|
| 226 | + |
| 227 | + def _get_function_invoke_role(self): |
| 228 | + if not self.function_invoke_role or self.function_invoke_role == "NONE": |
| 229 | + return None |
| 230 | + |
| 231 | + return self.function_invoke_role |
| 232 | + |
| 233 | + def _get_identity_source(self): |
| 234 | + identity_source_headers = [] |
| 235 | + identity_source_query_strings = [] |
| 236 | + identity_source_stage_variables = [] |
| 237 | + identity_source_context = [] |
| 238 | + |
| 239 | + if self.identity.get("Headers"): |
| 240 | + identity_source_headers = list(map(lambda h: "$request.header." + h, self.identity.get("Headers"))) |
| 241 | + |
| 242 | + if self.identity.get("QueryStrings"): |
| 243 | + identity_source_query_strings = list( |
| 244 | + map(lambda qs: "$request.querystring." + qs, self.identity.get("QueryStrings")) |
| 245 | + ) |
| 246 | + |
| 247 | + if self.identity.get("StageVariables"): |
| 248 | + identity_source_stage_variables = list( |
| 249 | + map(lambda sv: "$stageVariables." + sv, self.identity.get("StageVariables")) |
| 250 | + ) |
| 251 | + |
| 252 | + if self.identity.get("Context"): |
| 253 | + identity_source_context = list(map(lambda c: "$context." + c, self.identity.get("Context"))) |
| 254 | + |
| 255 | + identity_source = ( |
| 256 | + identity_source_headers |
| 257 | + + identity_source_query_strings |
| 258 | + + identity_source_stage_variables |
| 259 | + + identity_source_context |
| 260 | + ) |
| 261 | + |
| 262 | + return identity_source |
| 263 | + |
| 264 | + def _get_reauthorize_every(self): |
| 265 | + if not self.identity: |
| 266 | + return None |
| 267 | + |
| 268 | + return self.identity.get("ReauthorizeEvery") |
0 commit comments