def test_binding_create_from_is_correct(self): """Test that the IamPolicyBinding create is correct.""" # test role, members, role name pattern binding = {'role': 'roles/viewer', 'members': self.test_members} iam_binding = IamPolicyBinding.create_from(binding) self.assertEqual(binding['role'], iam_binding.role_name) self.assertEqual(binding['members'], _get_member_list(iam_binding.members)) self.assertEqual('^roles\/viewer$', iam_binding.role_pattern.pattern) # test roles glob binding2 = {'role': 'roles/*', 'members': self.test_members} iam_binding2 = IamPolicyBinding.create_from(binding2) self.assertEqual('^roles\/.+?$', iam_binding2.role_pattern.pattern)
def test_add_single_rule_builds_correct_map(self): """Test that adding a single rule builds the correct map.""" rule_book = ire.IamRuleBook(test_rules.RULES1, self.fake_timestamp) actual_rules = rule_book.resource_rules_map # expected rule_bindings = [{ 'role': 'roles/*', 'members': ['user:*@company.com'] }] rule = scanner_rules.Rule( 'my rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') expected_org_rules = ire.ResourceRules(self.org789, rules=set([rule]), applies_to='self_and_children') expected_proj1_rules = ire.ResourceRules(self.project1, rules=set([rule]), applies_to='self') expected_proj2_rules = ire.ResourceRules(self.project2, rules=set([rule]), applies_to='self') expected_rules = { (self.org789, 'self_and_children'): expected_org_rules, (self.project1, 'self'): expected_proj1_rules, (self.project2, 'self'): expected_proj2_rules } self.assertEqual(expected_rules, actual_rules)
def test_one_member_mismatch(self): """Test a policy where one member mismatches the whitelist. Setup: * Create a RulesEngine and add test_rules.RULES1. * Create the policy binding. * Create the Rule and rule bindings. * Create the resource association for the Rule. Expected results: One policy binding member missing from the whitelist. """ # actual rules_local_path = get_datafile_path(__file__, 'test_rules_1.yaml') rules_engine = ire.IamRulesEngine(rules_local_path) rules_engine.rule_book = ire.IamRuleBook(test_rules.RULES1, self.fake_timestamp) rules_engine.rule_book.org_res_rel_dao = mock.MagicMock() find_ancestor_mock = mock.MagicMock(side_effect=[[self.org789]]) rules_engine.rule_book.org_res_rel_dao.find_ancestors = \ find_ancestor_mock policy = { 'bindings': [{ 'role': 'roles/editor', 'members': ['user:[email protected]', 'user:[email protected]'] }] } actual_violations = set( rules_engine.find_policy_violations(self.project1, policy)) # expected rule_bindings = [{ 'role': 'roles/*', 'members': ['user:*@company.com'] }] rule = scanner_rules.Rule( 'my rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') expected_outstanding = { 'roles/editor': [IamPolicyMember.create_from('user:[email protected]')] } expected_violations = set([ scanner_rules.RuleViolation( resource_type=self.project1.type, resource_id=self.project1.id, rule_name=rule.rule_name, rule_index=rule.rule_index, role='roles/editor', violation_type=scanner_rules.VIOLATION_TYPE.get(rule.mode), members=tuple(expected_outstanding['roles/editor'])) ]) self.assertEqual(expected_violations, actual_violations)
def _get_bindings(self, bindings): """Get a list of this Rule's bindings as IamPolicyBindings. Args: bindings: A list of bindings (dict). Returns: A list of IamPolicyBindings. """ return [IamPolicyBinding.create_from(b) for b in bindings]
def test_policy_binding_matches_whitelist_rules(self): """Test that a policy binding matches the whitelist rules. Setup: * Create a test policy binding. * Create a test rule binding. * Create a whitelist rule with the test rules. Expected results: All policy binding members are in the whitelist. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'user:[email protected]', 'group:[email protected]', 'serviceAccount:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:*@company.com', 'user:abc@*.somewhere.com', 'group:*@googlegroups.com', 'serviceAccount:*@*.gserviceaccount.com', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='whitelist') resource_rule = ire.ResourceRules(rules=[rule]) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(0, len(results))
def test_policy_binding_mismatches_required_rules(self): """Test that a required list of members mismatches policy binding. Setup: * Create a test policy binding. * Create a test rule binding. * Create a required rule with the test rules. Expected results: All required members are found in the policy. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'group:[email protected]', 'serviceAccount:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:[email protected]', 'user:[email protected]', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='required') resource_rule = ire.ResourceRules(resource=self.project1) resource_rule.rules.add(rule) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(1, len(results))
def test_policy_binding_does_not_match_blacklist_rules(self): """Test that a policy binding does not match the blacklist. Setup: * Create a test policy binding. * Create a test rule binding. * Create a blacklist rule with the test rules. Expected results: No policy bindings found in the blacklist. """ test_binding = { 'role': 'roles/owner', 'members': [ 'user:[email protected]', ] } rule_bindings = [{ 'role': 'roles/owner', 'members': [ 'user:*@company.com', 'user:abc@*.somewhere.com', 'group:*@googlegroups.com', 'serviceAccount:*@*.gserviceaccount.com', ] }] rule = scanner_rules.Rule( 'test rule', 0, [IamPolicyBinding.create_from(b) for b in rule_bindings], mode='blacklist') resource_rule = ire.ResourceRules(rules=[rule]) results = list( resource_rule.find_mismatches(self.project1, test_binding)) self.assertEqual(0, len(results))
def find_mismatches(self, policy_resource, binding_to_match): """Determine if the policy binding matches this rule's criteria. How the member matching operates: 1. Whitelist: policy members match at least one rule member 2. Blacklist: policy members must not match any rule members 3. Require: rule members must all match policy members Args: policy_resource: The resource that the policy belongs to. binding_to_match: The IamPolicyBinding binding to compare to this rule's bindings. Returns: A generator of RuleViolations. """ policy_binding = IamPolicyBinding.create_from(binding_to_match) for rule in self.rules: # TODO: transform the policy binding into a {role: members} dict found_role = False for binding in rule.bindings: policy_role_name = policy_binding.role_name if not policy_binding.role_name.startswith('roles'): policy_role_name = 'roles/{}'.format( policy_binding.role_name) # If the rule's role pattern matches the policy binding's role # pattern, then check the members to see whether they match, # according to the rule mode. if binding.role_pattern.match(policy_role_name): if rule.mode == RuleMode.REQUIRED: role_name = binding.role_name else: role_name = policy_role_name found_role = True violating_members = (self._dispatch_rule_mode_check( mode=rule.mode, rule_members=binding.members, policy_members=policy_binding.members)) if violating_members: yield RuleViolation( resource_type=policy_resource.resource_type, resource_id=policy_resource.resource_id, rule_name=rule.rule_name, rule_index=rule.rule_index, violation_type=RULE_VIOLATION_TYPE.get( rule.mode, RULE_VIOLATION_TYPE['UNSPECIFIED']), role=role_name, members=violating_members) # Extra check if the role did not match in the REQUIRED case. if not found_role and rule.mode == RuleMode.REQUIRED: for binding in rule.bindings: yield RuleViolation( resource_type=policy_resource.resource_type, resource_id=policy_resource.resource_id, rule_name=rule.rule_name, rule_index=rule.rule_index, violation_type=RULE_VIOLATION_TYPE.get( rule.mode, RULE_VIOLATION_TYPE['UNSPECIFIED']), role=binding.role_name, members=binding.members)
def test_binding_missing_members_raises(self): """Test that a binding with no members raises an exception.""" with self.assertRaises(InvalidIamPolicyBindingError): IamPolicyBinding('roles/fake', [])
def test_binding_missing_role_raises(self): """Test that a binding with no role raises an exception.""" with self.assertRaises(InvalidIamPolicyBindingError): IamPolicyBinding(None, ['*'])