def expand_resource(self, resource): """Return a resource string for given resource""" if "iam" in resource: for found in self.iam_arns_from_specification(resource): yield found elif "kms" in resource: for found in self.kms_arns_from_specification(resource): yield found elif "sns" in resource: for found in self.sns_arns_from_specification(resource): yield found elif "s3" in resource: for bucket_key in listify(resource, "s3"): if bucket_key == "__self__": if self.self_type == "role": raise BadPolicy("Role policy has no __self__ bucket", role=self.name) elif self.self_type == "key": raise BadPolicy("Key policy has no __self__ bucket", key=self.name) else: bucket_key = self.name yield "arn:aws:s3:::{0}".format(bucket_key) if '/' not in bucket_key: yield "arn:aws:s3:::{0}/*".format(bucket_key) else: raise BadPolicy("Unknown resource type", resource=resource)
def make_permission_statements(self, policy, allow=None): """ Make zero or more permissions statements from a policy definition If allow is None and no allow or Effect is specified, an error is raised Otherwise we assume the truthiness of allow """ result = dict((key, val) for key, val in policy.items() if key[0].isupper()) if allow is None and "Effect" not in policy: if not isinstance(policy.get("allow"), bool): raise BadPolicy("Need to specify whether we allow this policy or not", policy=policy, **{self.self_type:self.name}) result["Effect"] = ["Deny", "Allow"][policy["allow"]] elif allow is not None: result["Effect"] = ["Deny", "Allow"][allow] for key, dest in (("action", "Action"), ("notaction", "NotAction")): for specified in listified(policy, key): listify(result, dest).append(specified) for key, dest in (("resource", "Resource"), ("notresource", "NotResource")): for specified in listified(policy, key): listify(result, dest).extend(self.fill_out_resources(specified)) self.expand_principal(policy, result) if "Resource" not in result and "NotResource" not in result: raise BadPolicy("No Resource or NotResource was defined for policy", policy=policy, **{self.self_type:self.name}) if "Action" not in result and "NotAction" not in result: raise BadPolicy("No Action or NotAction defined for this policy", policy=policy, **{self.self_type:self.name}) if "Sid" not in result and self.self_type == "bucket": result["Sid"] = "" # Amazon gets rid of the lists if only one item # And this mucks around with the diffing.... for thing in ("Action", "NotAction", "Resource", "NotResource"): if thing in result: if len(listify(result, thing)) == 1: result[thing] = result[thing][0] else: result[thing] = sorted(result[thing]) yield result
def sns_arns_from_specification(self, resource): """Get us kms arns from this specification""" for key_id in listify(resource, "sns"): location = self.location if 'location' in resource: location = resource['location'] provided_accounts = resource.get("account", "") if not isinstance(provided_accounts, list): provided_accounts = [provided_accounts] for provided_account in provided_accounts: if provided_account: if provided_account not in self.accounts: raise BadPolicy("Unknown account specified", account=provided_account, specification=resource) else: account_id = self.accounts[provided_account] else: account_id = self.account_id yield "arn:aws:sns:{0}:{1}:{2}".format(location, account_id, key_id)
def kms_arns_from_specification(self, resource): """Get us kms arns from this specification""" for key_id in listify(resource, "kms"): alias = None if key_id == "__self__": if self.self_type != "key": raise BadPolicy("No __self__ key for this policy", type=self.self_type, name=self.name) else: alias = self.name location = self.location if not alias: if isinstance(key_id, six.string_types): alias = key_id location = resource.get("location", self.location) else: alias = key_id.get("alias") location = key_id.get("location", resource.get("location", self.location)) key_id = key_id.get("key_id") provided_accounts = resource.get("account", "") if not isinstance(provided_accounts, list): provided_accounts = [provided_accounts] for provided_account in provided_accounts: if provided_account: if provided_account not in self.accounts: raise BadPolicy("Unknown account specified", account=provided_account, specification=resource) else: account_id = self.accounts[provided_account] else: account_id = self.account_id if alias: yield "arn:aws:kms:{0}:{1}:alias/{2}".format(location, account_id, alias) else: yield "arn:aws:kms:{0}:{1}:key/{2}".format(location, account_id, key_id)
def expand_principal(self, statement, result): """Expand out Principal and NotPrincipal""" for principal, capd in [("principal", "Principal"), ("notprincipal", "NotPrincipal")]: if principal in statement: if capd not in result: result[capd] = {} looking_at = statement[principal] if not isinstance(looking_at, list): looking_at = [looking_at] for instruction in looking_at: principal = result[capd] for specified in listified(instruction, "service"): if specified == "ec2": specified = "ec2.amazonaws.com" listify(principal, "Service").append(specified) for specified in listified(instruction, "federated"): listify(principal, "Federated").extend(self.iam_arns_from_specification(specified)) if "Action" not in result: result["Action"] = "sts:AssumeRoleWithSAML" if "iam" in instruction: listify(principal, "AWS").extend(self.iam_arns_from_specification(instruction)) # Amazon gets rid of the lists if only one item # And this mucks around with the diffing.... for princ in (result.get("Principal"), result.get("NotPrincipal")): if princ: for principal_type in ("AWS", "Federated", "Service"): if principal_type in princ: if len(listify(princ, principal_type)) == 1: princ[principal_type] = princ[principal_type][0] else: princ[principal_type] = sorted(princ[principal_type])
from iam_syncr.helpers import listify, listified, as_list import six from tests.helpers import TestCase if six.PY2: import mock else: from unittest import mock describe TestCase, "listify": it "sets the key in the dct to an empty array if doesn't exist and returns it": dct = {} lst = listify(dct, "blah") self.assertIs(lst, dct["blah"]) it "returns the key as is if already a list": lst = [1, 2] dct = {"blah": lst} self.assertIs(listify(dct, "blah"), lst) self.assertEqual(dct, {"blah": lst}) it "makes the key a list and returns if in there but not a list already": for val in (0, 1, None, True, False, "", "adf", lambda: 1, mock.Mock(name="blah")): dct = {"blah": val} lst = listify(dct, "blah") self.assertEqual(lst, [val]) self.assertEqual(dct, {"blah": lst})