Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 6cf5257

Browse files
authored
Implement ApplicationPolicy class and add unit tests (#6)
* Implement ApplicationPolicy class and add unit tests
1 parent 3af6c00 commit 6cf5257

File tree

3 files changed

+112
-6
lines changed

3 files changed

+112
-6
lines changed

serverlessrepo/application_policy.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,68 @@
1+
import re
2+
3+
from .exceptions import InvalidApplicationPolicyError
4+
5+
16
class ApplicationPolicy(object):
27
"""
38
Class representing SAR application policy
49
"""
510

611
# Supported actions for setting SAR application permissions
712
GET_APPLICATION = 'GetApplication'
13+
LIST_APPLICATION_DEPENDENCIES = 'ListApplicationDependencies'
814
CREATE_CLOUD_FORMATION_CHANGE_SET = 'CreateCloudFormationChangeSet'
915
CREATE_CLOUD_FORMATION_TEMPLATE = 'CreateCloudFormationTemplate'
1016
LIST_APPLICATION_VERSIONS = 'ListApplicationVersions'
1117
SEARCH_APPLICATIONS = 'SearchApplications'
1218
DEPLOY = 'Deploy'
1319

20+
SUPPORTED_ACTIONS = [
21+
GET_APPLICATION,
22+
LIST_APPLICATION_DEPENDENCIES,
23+
CREATE_CLOUD_FORMATION_CHANGE_SET,
24+
CREATE_CLOUD_FORMATION_TEMPLATE,
25+
LIST_APPLICATION_VERSIONS,
26+
SEARCH_APPLICATIONS,
27+
DEPLOY
28+
]
29+
30+
_PRINCIPAL_PATTERN = re.compile(r'^([0-9]{12}|\*)$')
31+
1432
def __init__(self, principals, actions):
1533
"""
1634
Initializes the object given the principals and actions
1735
18-
:param principals: Comma-separated list of AWS account IDs, or *
19-
:type principals: str
20-
:param actions: Comma-separated list of actions supported by SAR
21-
:type actions: str
36+
:param principals: List of AWS account IDs, or *
37+
:type principals: list of str
38+
:param actions: List of actions supported by SAR
39+
:type actions: list of str
2240
"""
2341
self.principals = principals
2442
self.actions = actions
2543

26-
def is_valid(self):
44+
def validate(self):
2745
"""
2846
Checks if the formats of principals and actions are valid
2947
3048
:return: True, if the policy is valid
49+
:raises: InvalidApplicationPolicyError
3150
"""
51+
if not self.principals:
52+
raise InvalidApplicationPolicyError(error_message='principals not provided')
53+
54+
if not self.actions:
55+
raise InvalidApplicationPolicyError(error_message='actions not provided')
56+
57+
if any(not self._PRINCIPAL_PATTERN.match(p) for p in self.principals):
58+
raise InvalidApplicationPolicyError(
59+
error_message='principal should be 12-digit accountId or "*"')
60+
61+
unsupported_actions = sorted(set(self.actions) - set(self.SUPPORTED_ACTIONS))
62+
if len(unsupported_actions):
63+
raise InvalidApplicationPolicyError(
64+
error_message='{} not supported'.format(', '.join(unsupported_actions)))
65+
3266
return True
3367

3468
def to_statement(self):
@@ -38,4 +72,7 @@ def to_statement(self):
3872
:return: Dictionary containing Actions and Principals
3973
:rtype: dict
4074
"""
41-
pass
75+
return {
76+
'Principals': self.principals,
77+
'Actions': self.actions
78+
}

serverlessrepo/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,10 @@ class ApplicationMetadataNotFoundError(ServerlessRepoError):
2323
Raised when application metadata is not found
2424
"""
2525
MESSAGE = "Application metadata not found in the SAM template: '{error_message}'"
26+
27+
28+
class InvalidApplicationPolicyError(ServerlessRepoError):
29+
"""
30+
Raised when invalid application policy is provided
31+
"""
32+
MESSAGE = "Invalid application policy: '{error_message}'"

tests/unit/test_application_policy.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from unittest import TestCase
2+
3+
from serverlessrepo.application_policy import ApplicationPolicy
4+
from serverlessrepo.exceptions import InvalidApplicationPolicyError
5+
6+
7+
class TestApplicationPolicy(TestCase):
8+
9+
def test_init(self):
10+
app_policy = ApplicationPolicy(['1', '2'], ['a', 'b'])
11+
self.assertEqual(app_policy.principals, ['1', '2'])
12+
self.assertEqual(app_policy.actions, ['a', 'b'])
13+
14+
def test_valid_principals_actions(self):
15+
principals = ['123456789011', '*']
16+
actions = [ApplicationPolicy.DEPLOY, ApplicationPolicy.GET_APPLICATION]
17+
app_policy = ApplicationPolicy(principals, actions)
18+
self.assertTrue(app_policy.validate())
19+
20+
def test_empty_principals(self):
21+
app_policy = ApplicationPolicy([], [ApplicationPolicy.DEPLOY])
22+
with self.assertRaises(InvalidApplicationPolicyError) as context:
23+
app_policy.validate()
24+
25+
message = str(context.exception)
26+
expected = 'principals not provided'
27+
self.assertTrue(expected in message)
28+
29+
def test_not_12_digits_principals(self):
30+
app_policy = ApplicationPolicy(['123'], [ApplicationPolicy.DEPLOY])
31+
with self.assertRaises(InvalidApplicationPolicyError) as context:
32+
app_policy.validate()
33+
34+
message = str(context.exception)
35+
expected = 'principal should be 12-digit number or "*"'
36+
self.assertTrue(expected in message)
37+
38+
def test_empty_actions(self):
39+
app_policy = ApplicationPolicy(['123456789012'], [])
40+
with self.assertRaises(InvalidApplicationPolicyError) as context:
41+
app_policy.validate()
42+
43+
message = str(context.exception)
44+
expected = 'actions not provided'
45+
self.assertTrue(expected in message)
46+
47+
def test_not_supported_actions(self):
48+
app_policy = ApplicationPolicy(['123456789012'], ['RandomActionA', 'RandomActionB'])
49+
with self.assertRaises(InvalidApplicationPolicyError) as context:
50+
app_policy.validate()
51+
52+
message = str(context.exception)
53+
expected = 'RandomActionA, RandomActionB not supported'
54+
self.assertTrue(expected in message)
55+
56+
def test_to_statement(self):
57+
app_policy = ApplicationPolicy(['1', '2'], ['actionA', 'actionB'])
58+
expected_statement = {
59+
'Principals': ['1', '2'],
60+
'Actions': ['actionA', 'actionB']
61+
}
62+
self.assertEqual(app_policy.to_statement(), expected_statement)

0 commit comments

Comments
 (0)