PageRenderTime 69ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/example/authorizer/src/policy.py

https://gitlab.com/ryandub/yoke
Python | 178 lines | 171 code | 5 blank | 2 comment | 3 complexity | 0cc89f8707a02e7e053132f866cc435e MD5 | raw file
  1. import re
  2. class HttpVerb:
  3. GET = 'GET'
  4. POST = 'POST'
  5. PUT = 'PUT'
  6. PATCH = 'PATCH'
  7. HEAD = 'HEAD'
  8. DELETE = 'DELETE'
  9. OPTIONS = 'OPTIONS'
  10. ALL = '*'
  11. class AuthPolicy(object):
  12. def __init__(self, token, aws_account_id, principal=None):
  13. self.token = token
  14. # AWS account ID policy will be generated for
  15. self.aws_account_id = aws_account_id
  16. # Principal used for policy, unique ID for end user
  17. self.principal_id = principal or ''
  18. # Policy version should always be '2012-10-17'
  19. self.version = '2012-10-17'
  20. # Used to validate resource paths for policy
  21. self.path_regex = '^[/.a-zA-Z0-9-\*]+$'
  22. # Lists of allowed/denied methods, objects with resource ARN and
  23. # nullable conditions statement
  24. self.allowMethods = []
  25. self.denyMethods = []
  26. # The API Gateway API id. By default this is set to '*'
  27. self.rest_api_id = '*'
  28. # The region where the API is deployed. Default is '*'
  29. self.region = '*'
  30. # The name of the stage used in the policy. Default is '*'
  31. self.stage = '*'
  32. def _add_method(self, effect, verb, resource, conditions):
  33. """
  34. Adds a method to the internal lists of allowed or denied methods.
  35. Each object in the internal list contains a resource ARN and a
  36. condition statement. The condition statement can be null.
  37. """
  38. if verb != '*' and not hasattr(HttpVerb, verb):
  39. raise NameError('Invalid HTTP verb ' + verb +
  40. '. Allowed verbs in HttpVerb class')
  41. resource_pattern = re.compile(self.path_regex)
  42. if not resource_pattern.match(resource):
  43. raise NameError('Invalid resource path: ' + resource +
  44. '. Path should match ' + self.path_regex)
  45. if resource[:1] == '/':
  46. resource = resource[1:]
  47. resource_arn = ('arn:aws:execute-api:' +
  48. self.region + ':' +
  49. self.aws_account_id + ':' +
  50. self.rest_api_id + '/' +
  51. self.stage + '/' +
  52. verb + '/' +
  53. resource)
  54. if effect.lower() == 'allow':
  55. self.allowMethods.append({
  56. 'resource_arn': resource_arn,
  57. 'conditions': conditions
  58. })
  59. elif effect.lower() == 'deny':
  60. self.denyMethods.append({
  61. 'resource_arn': resource_arn,
  62. 'conditions': conditions
  63. })
  64. def _get_empty_statement(self, effect):
  65. """
  66. Returns an empty statement object prepopulated with the
  67. correct action and the desired effect.
  68. """
  69. statement = {
  70. 'Action': 'execute-api:Invoke',
  71. 'Effect': effect[:1].upper() + effect[1:].lower(),
  72. 'Resource': []
  73. }
  74. return statement
  75. def _get_effect_statement(self, effect, methods):
  76. """
  77. This function loops over an array of objects containing
  78. a resourceArn and conditions statement and generates
  79. the array of statements for the policy.
  80. """
  81. statements = []
  82. if len(methods) > 0:
  83. statement = self._get_empty_statement(effect)
  84. for method in methods:
  85. if (method['conditions'] is None or
  86. len(method['conditions']) == 0):
  87. statement['Resource'].append(method['resource_arn'])
  88. else:
  89. cond_statement = self._get_empty_statement(effect)
  90. cond_statement['Resource'].append(method['resource_arn'])
  91. cond_statement['Condition'] = method['conditions']
  92. statements.append(cond_statement)
  93. statements.append(statement)
  94. return statements
  95. def allow_all_methods(self):
  96. """Adds a '*' allow to authorize access to all methods of an API"""
  97. self._add_method('Allow', HttpVerb.ALL, '*', [])
  98. def deny_all_methods(self):
  99. """Adds a '*' allow to deny access to all methods of an API"""
  100. self._add_method('Deny', HttpVerb.ALL, '*', [])
  101. def allow_method(self, verb, resource):
  102. """
  103. Adds an API Gateway method (Http verb + Resource path)
  104. to the list of allowed methods for the policy
  105. """
  106. self._add_method('Allow', verb, resource, [])
  107. def deny_method(self, verb, resource):
  108. """
  109. Adds an API Gateway method (Http verb + Resource path)
  110. to the list of denied methods for the policy
  111. """
  112. self._add_method('Deny', verb, resource, [])
  113. def allow_method_with_conditions(self, verb, resource, conditions):
  114. """
  115. Adds an API Gateway method (Http verb + Resource path) to the
  116. list of allowed methods and includes a condition for the policy
  117. statement. More on AWS policy conditions here:
  118. http://docs.aws.amazon.com/IAM/latest/UserGuide/
  119. reference_policies_elements.html#Condition
  120. """
  121. self._add_method('Allow', verb, resource, conditions)
  122. def deny_method_with_conditions(self, verb, resource, conditions):
  123. """
  124. Adds an API Gateway method (Http verb + Resource path) to the
  125. list of denied methods and includes a condition for the policy
  126. statement. More on AWS policy conditions here:
  127. http://docs.aws.amazon.com/IAM/latest/UserGuide/
  128. reference_policies_elements.html#Condition
  129. """
  130. self._add_method('Deny', verb, resource, conditions)
  131. def build(self):
  132. """
  133. Generates the policy document based on the internal lists of
  134. allowed and denied conditions. This will generate a policy with
  135. two main statements for the effect: one statement for Allow and
  136. one statement for Deny. Methods that includes conditions will
  137. have their own statement in the policy.
  138. """
  139. if ((self.allowMethods is None or len(self.allowMethods) == 0) and
  140. (self.denyMethods is None or len(self.denyMethods) == 0)):
  141. raise NameError('No statements defined for the policy')
  142. policy = {
  143. 'principalId': self.principal_id,
  144. 'policyDocument': {
  145. 'Version': self.version,
  146. 'Statement': []
  147. }
  148. }
  149. policy['policyDocument']['Statement'].extend(
  150. self._get_effect_statement('Allow', self.allowMethods))
  151. policy['policyDocument']['Statement'].extend(
  152. self._get_effect_statement('Deny', self.denyMethods))
  153. return policy