@@ -234,10 +234,12 @@ def raw_query_string(self) -> str:
234
234
235
235
@property
236
236
def cookies (self ) -> List [str ]:
237
+ """Cookies"""
237
238
return self ["cookies" ]
238
239
239
240
@property
240
241
def headers (self ) -> Dict [str , str ]:
242
+ """Http headers"""
241
243
return self ["headers" ]
242
244
243
245
@property
@@ -314,6 +316,8 @@ def asdict(self) -> dict:
314
316
315
317
316
318
class HttpVerb (enum .Enum ):
319
+ """Enum of http methods / verbs"""
320
+
317
321
GET = "GET"
318
322
POST = "POST"
319
323
PUT = "PUT"
@@ -324,15 +328,32 @@ class HttpVerb(enum.Enum):
324
328
ALL = "*"
325
329
326
330
331
+ DENY_ALL_RESPONSE = {
332
+ "principalId" : "deny-all-user" ,
333
+ "policyDocument" : {
334
+ "Version" : "2012-10-17" ,
335
+ "Statement" : [
336
+ {
337
+ "Action" : "execute-api:Invoke" ,
338
+ "Effect" : "Deny" ,
339
+ "Resource" : ["*" ],
340
+ }
341
+ ],
342
+ },
343
+ }
344
+
345
+
327
346
class APIGatewayAuthorizerResponse :
328
- """Api Gateway HTTP API V1 payload or Rest api authorizer response helper
347
+ """The IAM Policy Response required for API Gateway REST APIs and HTTP APIs.
329
348
330
349
Based on: - https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints/blob/\
331
350
master/blueprints/python/api-gateway-authorizer-python.py
332
- """
333
351
334
- version = "2012-10-17"
335
- """The policy version used for the evaluation. This should always be '2012-10-17'"""
352
+ Documentation:
353
+ -------------
354
+ - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html
355
+ - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
356
+ """
336
357
337
358
path_regex = r"^[/.a-zA-Z0-9-\*]+$"
338
359
"""The regular expression used to validate resource paths for the policy"""
@@ -345,6 +366,7 @@ def __init__(
345
366
api_id : str ,
346
367
stage : str ,
347
368
context : Optional [Dict ] = None ,
369
+ usage_identifier_key : Optional [str ] = None ,
348
370
):
349
371
"""
350
372
Parameters
@@ -373,32 +395,57 @@ def __init__(
373
395
context : Dict, optional
374
396
Optional, context.
375
397
Note: only names of type string and values of type int, string or boolean are supported
398
+ usage_identifier_key: str, optional
399
+ If the API uses a usage plan (the apiKeySource is set to `AUTHORIZER`), the Lambda authorizer function
400
+ must return one of the usage plan's API keys as the usageIdentifierKey property value.
401
+ > **Note:** This only applies for REST APIs.
376
402
"""
377
403
self .principal_id = principal_id
378
404
self .region = region
379
405
self .aws_account_id = aws_account_id
380
406
self .api_id = api_id
381
407
self .stage = stage
382
408
self .context = context
409
+ self .usage_identifier_key = usage_identifier_key
383
410
self ._allow_routes : List [Dict ] = []
384
411
self ._deny_routes : List [Dict ] = []
412
+ self ._resource_pattern = re .compile (self .path_regex )
385
413
386
- def _add_route (self , effect : str , verb : str , resource : str , conditions : List [Dict ]):
414
+ @staticmethod
415
+ def from_route_arn (
416
+ arn : str ,
417
+ principal_id : str ,
418
+ context : Optional [Dict ] = None ,
419
+ usage_identifier_key : Optional [str ] = None ,
420
+ ) -> "APIGatewayAuthorizerResponse" :
421
+ parsed_arn = parse_api_gateway_arn (arn )
422
+ return APIGatewayAuthorizerResponse (
423
+ principal_id ,
424
+ parsed_arn .region ,
425
+ parsed_arn .aws_account_id ,
426
+ parsed_arn .api_id ,
427
+ parsed_arn .stage ,
428
+ context ,
429
+ usage_identifier_key ,
430
+ )
431
+
432
+ def _add_route (self , effect : str , http_method : str , resource : str , conditions : Optional [List [Dict ]] = None ):
387
433
"""Adds a route to the internal lists of allowed or denied routes. Each object in
388
434
the internal list contains a resource ARN and a condition statement. The condition
389
435
statement can be null."""
390
- if verb != "*" and verb not in HttpVerb .__members__ :
436
+ if http_method != "*" and http_method not in HttpVerb .__members__ :
391
437
allowed_values = [verb .value for verb in HttpVerb ]
392
- raise ValueError (f"Invalid HTTP verb: '{ verb } '. Use either '{ allowed_values } '" )
438
+ raise ValueError (f"Invalid HTTP verb: '{ http_method } '. Use either '{ allowed_values } '" )
393
439
394
- resource_pattern = re .compile (self .path_regex )
395
- if not resource_pattern .match (resource ):
440
+ if not self ._resource_pattern .match (resource ):
396
441
raise ValueError (f"Invalid resource path: { resource } . Path should match { self .path_regex } " )
397
442
398
443
if resource [:1 ] == "/" :
399
444
resource = resource [1 :]
400
445
401
- resource_arn = APIGatewayRouteArn (self .region , self .aws_account_id , self .api_id , self .stage , verb , resource ).arn
446
+ resource_arn = APIGatewayRouteArn (
447
+ self .region , self .aws_account_id , self .api_id , self .stage , http_method , resource
448
+ ).arn
402
449
403
450
route = {"resourceArn" : resource_arn , "conditions" : conditions }
404
451
@@ -412,24 +459,27 @@ def _get_empty_statement(effect: str) -> Dict[str, Any]:
412
459
"""Returns an empty statement object prepopulated with the correct action and the desired effect."""
413
460
return {"Action" : "execute-api:Invoke" , "Effect" : effect .capitalize (), "Resource" : []}
414
461
415
- def _get_statement_for_effect (self , effect : str , methods : List ) -> List :
416
- """This function loops over an array of objects containing a resourceArn and
417
- conditions statement and generates the array of statements for the policy."""
418
- if len ( methods ) == 0 :
462
+ def _get_statement_for_effect (self , effect : str , routes : List [ Dict ] ) -> List [ Dict ] :
463
+ """This function loops over an array of objects containing a ` resourceArn` and
464
+ ` conditions` statement and generates the array of statements for the policy."""
465
+ if not routes :
419
466
return []
420
467
421
- statements = []
422
-
468
+ statements : List [Dict ] = []
423
469
statement = self ._get_empty_statement (effect )
424
- for method in methods :
425
- if method ["conditions" ] is None or len (method ["conditions" ]) == 0 :
426
- statement ["Resource" ].append (method ["resourceArn" ])
427
- else :
470
+
471
+ for route in routes :
472
+ resource_arn = route ["resourceArn" ]
473
+ conditions = route .get ("conditions" )
474
+ if conditions is not None and len (conditions ) > 0 :
428
475
conditional_statement = self ._get_empty_statement (effect )
429
- conditional_statement ["Resource" ].append (method [ "resourceArn" ] )
430
- conditional_statement ["Condition" ] = method [ " conditions" ]
476
+ conditional_statement ["Resource" ].append (resource_arn )
477
+ conditional_statement ["Condition" ] = conditions
431
478
statements .append (conditional_statement )
432
479
480
+ else :
481
+ statement ["Resource" ].append (resource_arn )
482
+
433
483
if len (statement ["Resource" ]) > 0 :
434
484
statements .append (statement )
435
485
@@ -442,7 +492,7 @@ def allow_all_routes(self, http_method: str = HttpVerb.ALL.value):
442
492
----------
443
493
http_method: str
444
494
"""
445
- self ._add_route (effect = "Allow" , verb = http_method , resource = "*" , conditions = [] )
495
+ self ._add_route (effect = "Allow" , http_method = http_method , resource = "*" )
446
496
447
497
def deny_all_routes (self , http_method : str = HttpVerb .ALL .value ):
448
498
"""Adds a '*' allow to the policy to deny access to all methods of an API
@@ -452,25 +502,23 @@ def deny_all_routes(self, http_method: str = HttpVerb.ALL.value):
452
502
http_method: str
453
503
"""
454
504
455
- self ._add_route (effect = "Deny" , verb = http_method , resource = "*" , conditions = [] )
505
+ self ._add_route (effect = "Deny" , http_method = http_method , resource = "*" )
456
506
457
507
def allow_route (self , http_method : str , resource : str , conditions : Optional [List [Dict ]] = None ):
458
508
"""Adds an API Gateway method (Http verb + Resource path) to the list of allowed
459
509
methods for the policy.
460
510
461
511
Optionally includes a condition for the policy statement. More on AWS policy
462
512
conditions here: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition"""
463
- conditions = conditions or []
464
- self ._add_route (effect = "Allow" , verb = http_method , resource = resource , conditions = conditions )
513
+ self ._add_route (effect = "Allow" , http_method = http_method , resource = resource , conditions = conditions )
465
514
466
515
def deny_route (self , http_method : str , resource : str , conditions : Optional [List [Dict ]] = None ):
467
516
"""Adds an API Gateway method (Http verb + Resource path) to the list of denied
468
517
methods for the policy.
469
518
470
519
Optionally includes a condition for the policy statement. More on AWS policy
471
520
conditions here: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition"""
472
- conditions = conditions or []
473
- self ._add_route (effect = "Deny" , verb = http_method , resource = resource , conditions = conditions )
521
+ self ._add_route (effect = "Deny" , http_method = http_method , resource = resource , conditions = conditions )
474
522
475
523
def asdict (self ) -> Dict [str , Any ]:
476
524
"""Generates the policy document based on the internal lists of allowed and denied
@@ -482,12 +530,15 @@ def asdict(self) -> Dict[str, Any]:
482
530
483
531
response : Dict [str , Any ] = {
484
532
"principalId" : self .principal_id ,
485
- "policyDocument" : {"Version" : self . version , "Statement" : []},
533
+ "policyDocument" : {"Version" : "2012-10-17" , "Statement" : []},
486
534
}
487
535
488
536
response ["policyDocument" ]["Statement" ].extend (self ._get_statement_for_effect ("Allow" , self ._allow_routes ))
489
537
response ["policyDocument" ]["Statement" ].extend (self ._get_statement_for_effect ("Deny" , self ._deny_routes ))
490
538
539
+ if self .usage_identifier_key :
540
+ response ["usageIdentifierKey" ] = self .usage_identifier_key
541
+
491
542
if self .context :
492
543
response ["context" ] = self .context
493
544
0 commit comments