예제 #1
0
    def _test_validate_service(self,
                               v2_services,
                               v3_services,
                               expected_failure=False,
                               expected_services=None):
        with mock.patch.object(policy_authority.PolicyAuthority,
                               'os_admin',
                               autospec=True) as m_admin:
            m_admin.identity_services_client.list_services.\
                return_value = v2_services
            m_admin.identity_services_v3_client.list_services.\
                return_value = v3_services

        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id

        mock_os = self.patchobject(policy_authority, 'os')
        mock_os.path.join.return_value = self.admin_policy_file

        if not expected_services:
            expected_services = [s['name'] for s in self.services['services']]

        # Guarantee a blank slate for this test.
        if hasattr(policy_authority.PolicyAuthority, 'available_services'):
            delattr(policy_authority.PolicyAuthority, 'available_services')

        if expected_failure:
            policy_parser = None

            expected_exception = 'invalid_service is NOT a valid service'
            with self.assertRaisesRegex(
                    rbac_exceptions.RbacInvalidServiceException,
                    expected_exception):
                policy_authority.PolicyAuthority(test_tenant_id, test_user_id,
                                                 "INVALID_SERVICE")
        else:
            policy_parser = policy_authority.PolicyAuthority(
                test_tenant_id, test_user_id, "tenant_rbac_policy")

        # Check that the attribute is available at object and class levels.
        # If initialization failed, only check at class level.
        if policy_parser:
            self.assertTrue(hasattr(policy_parser, 'available_services'))
            self.assertEqual(expected_services,
                             policy_parser.available_services)
        self.assertTrue(
            hasattr(policy_authority.PolicyAuthority, 'available_services'))
        self.assertEqual(expected_services,
                         policy_authority.PolicyAuthority.available_services)
예제 #2
0
    def test_get_rules_from_file_and_from_code(self, mock_stevedore):
        fake_policy_rules = self._get_fake_policies({
            'code_policy_action_1': 'rule:code_rule_1',
            'code_policy_action_2': 'rule:code_rule_2',
            'code_policy_action_3': 'rule:code_rule_3',
        })

        mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
        mock_manager.configure_mock(name='tenant_rbac_policy')
        mock_stevedore.named.NamedExtensionManager.return_value = [
            mock_manager
        ]

        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "tenant_rbac_policy")

        rules = authority.get_rules()
        self.assertIsInstance(rules, policy_authority.policy.Rules)

        actual_policy_data = {k: str(v) for k, v in rules.items()}
        expected_policy_data = {
            "code_policy_action_1": "rule:code_rule_1",
            "code_policy_action_2": "rule:code_rule_2",
            "code_policy_action_3": "rule:code_rule_3",
            "rule1": "tenant_id:%(network:tenant_id)s",
            "rule2": "tenant_id:%(tenant_id)s",
            "rule3": "project_id:%(project_id)s",
            "rule4": "user_id:%(user_id)s",
            "admin_tenant_rule": "(role:admin and tenant_id:%(tenant_id)s)",
            "admin_user_rule": "(role:admin and user_id:%(user_id)s)"
        }

        self.assertEqual(expected_policy_data, actual_policy_data)
예제 #3
0
    def test_alt_admin_policy_file_with_context_is_admin(self):
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(test_tenant_id,
                                                     test_user_id,
                                                     "alt_admin_rbac_policy")

        role = 'fake_admin'
        allowed_rules = ['non_admin_rule']
        disallowed_rules = ['admin_rule']

        for rule in allowed_rules:
            allowed = authority.allowed(rule, role)
            self.assertTrue(allowed)

        for rule in disallowed_rules:
            allowed = authority.allowed(rule, role)
            self.assertFalse(allowed)

        role = 'super_admin'
        allowed_rules = ['admin_rule']
        disallowed_rules = ['non_admin_rule']

        for rule in allowed_rules:
            allowed = authority.allowed(rule, role)
            self.assertTrue(allowed)

        for rule in disallowed_rules:
            allowed = authority.allowed(rule, role)
            self.assertFalse(allowed)
예제 #4
0
    def _test_custom_multi_roles_policy(self, *args):
        default_roles = ['zero', 'one', 'two', 'three', 'four',
                         'five', 'six', 'seven', 'eight', 'nine']

        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "custom_rbac_policy")

        expected = {
            'policy_action_1': ['two', 'four', 'six', 'eight'],
            'policy_action_2': ['one', 'three', 'five', 'seven', 'nine'],
            'policy_action_4': ['one', 'two', 'three', 'five', 'seven'],
            'policy_action_5': ['zero', 'one', 'two', 'three', 'four', 'five',
                                'six', 'seven', 'eight', 'nine'],
        }

        for rule, role_list in expected.items():
            allowed_roles_lists = [roles for roles in [
                role_list[len(role_list) // 2:],
                role_list[:len(role_list) // 2]] if roles]
            for test_roles in allowed_roles_lists:
                self.assertTrue(authority.allowed(rule, test_roles))

            disallowed_roles = list(set(default_roles) - set(role_list))
            disallowed_roles_lists = [roles for roles in [
                disallowed_roles[len(disallowed_roles) // 2:],
                disallowed_roles[:len(disallowed_roles) // 2]] if roles]
            for test_roles in disallowed_roles_lists:
                self.assertFalse(authority.allowed(rule, test_roles))
예제 #5
0
    def test_discover_policy_files_with_many_invalid_one_valid(
            self, m_os, m_creds, *args):
        # Only the 3rd path is valid.
        m_os.path.isfile.side_effect = [False, False, True, False]

        # Ensure the outer for loop runs only once in `discover_policy_files`.
        m_creds.Manager().identity_services_v3_client.\
            list_services.return_value = {
                'services': [{'name': 'test_service'}]}

        # The expected policy will be 'baz/test_service'.
        self.useFixture(
            fixtures.ConfPatcher(
                custom_policy_files=['foo/%s', 'bar/%s', 'baz/%s'],
                group='patrole'))

        policy_parser = policy_authority.PolicyAuthority(
            None, None, 'test_service')

        # Ensure that "policy_files" is set at class and instance levels.
        self.assertIn('policy_files', dir(policy_authority.PolicyAuthority))
        self.assertIn('policy_files', dir(policy_parser))
        self.assertIn('test_service', policy_parser.policy_files)
        self.assertEqual('baz/test_service',
                         policy_parser.policy_files['test_service'])
예제 #6
0
    def test_discover_policy_files_with_many_invalid_one_valid(
            self, m_glob, m_os, m_admin, *args):
        service = 'test_service'
        custom_policy_files = ['foo/%s', 'bar/%s', 'baz/%s']
        m_glob.iglob.side_effect = [
            iter([path % service]) for path in custom_policy_files
        ]
        # Only the 3rd path is valid.
        m_os.path.isfile.side_effect = [False, False, True]

        # Ensure the outer for loop runs only once in `discover_policy_files`.
        m_admin.identity_services_v3_client.\
            list_services.return_value = {
                'services': [{'name': service}]}

        # The expected policy will be 'baz/test_service'.
        self.useFixture(
            fixtures.ConfPatcher(custom_policy_files=custom_policy_files,
                                 group='patrole'))

        policy_parser = policy_authority.PolicyAuthority(None, None, service)

        # Ensure that "policy_files" is set at class and instance levels.
        self.assertTrue(
            hasattr(policy_authority.PolicyAuthority, 'policy_files'))
        self.assertTrue(hasattr(policy_parser, 'policy_files'))
        self.assertEqual(['baz/%s' % service],
                         policy_parser.policy_files[service])
예제 #7
0
    def test_custom_policy(self, m_log):
        default_roles = [
            'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven',
            'eight', 'nine'
        ]

        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(test_tenant_id,
                                                     test_user_id,
                                                     "custom_rbac_policy")

        expected = {
            'policy_action_1': ['two', 'four', 'six', 'eight'],
            'policy_action_2': ['one', 'three', 'five', 'seven', 'nine'],
            'policy_action_3': ['zero'],
            'policy_action_4': ['one', 'two', 'three', 'five', 'seven'],
            'policy_action_5': [
                'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven',
                'eight', 'nine'
            ],
            'policy_action_6': ['eight'],
        }

        for rule, role_list in expected.items():
            for role in role_list:
                self.assertTrue(authority.allowed(rule, role))
            for role in set(default_roles) - set(role_list):
                self.assertFalse(authority.allowed(rule, role))
예제 #8
0
def _is_authorized(test_obj, service, rule, extra_target_data):
    """Validates whether current RBAC role has permission to do policy action.

    :param test_obj: An instance or subclass of ``tempest.test.BaseTestCase``.
    :param service: The OpenStack service that enforces ``rule``.
    :param rule: The name of the policy action. Examples include
        "identity:create_user" or "os_compute_api:os-agents".
    :param extra_target_data: Dictionary, keyed with ``oslo.policy`` generic
        check names, whose values are string literals that reference nested
        ``tempest.test.BaseTestCase`` attributes. Used by ``oslo.policy`` for
        performing matching against attributes that are sent along with the API
        calls.

    :returns: True if the current RBAC role can perform the policy action,
        else False.

    :raises RbacResourceSetupFailed: If `project_id` or `user_id` are missing
        from the `auth_provider` attribute in `test_obj`.
    """

    try:
        project_id = test_obj.os_primary.credentials.project_id
        user_id = test_obj.os_primary.credentials.user_id
    except AttributeError as e:
        msg = ("{0}: project_id or user_id not found in os_primary.credentials"
               .format(e))
        LOG.error(msg)
        raise rbac_exceptions.RbacResourceSetupFailed(msg)

    roles = CONF.patrole.rbac_test_roles
    # TODO(vegasq) drop once CONF.patrole.rbac_test_role is removed
    if CONF.patrole.rbac_test_role:
        if not roles:
            roles.append(CONF.patrole.rbac_test_role)

    # Adding implied roles
    roles = test_obj.get_all_needed_roles(roles)

    # Test RBAC against custom requirements. Otherwise use oslo.policy.
    if CONF.patrole.test_custom_requirements:
        authority = requirements_authority.RequirementsAuthority(
            CONF.patrole.custom_requirements_file, service)
    else:
        formatted_target_data = _format_extra_target_data(
            test_obj, extra_target_data)
        authority = policy_authority.PolicyAuthority(
            project_id,
            user_id,
            service,
            extra_target_data=formatted_target_data)
    is_allowed = authority.allowed(rule, roles)

    if is_allowed:
        LOG.debug("[Policy action]: %s, [Role]: %s is allowed!", rule, roles)
    else:
        LOG.debug("[Policy action]: %s, [Role]: %s is NOT allowed!", rule,
                  roles)

    return is_allowed
예제 #9
0
    def test_tenant_user_policy(self):
        """Test whether rules with format tenant_id/user_id formatting work.

        Test whether Neutron rules that contain project_id, tenant_id, and
        network:tenant_id pass. And test whether Nova rules that contain
        user_id pass.
        """
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(test_tenant_id,
                                                     test_user_id,
                                                     "tenant_rbac_policy")

        # Check whether Member role can perform expected actions.
        allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
        for rule in allowed_rules:
            allowed = authority.allowed(rule, 'Member')
            self.assertTrue(allowed)

        disallowed_rules = ['admin_tenant_rule', 'admin_user_rule']
        for disallowed_rule in disallowed_rules:
            self.assertFalse(authority.allowed(disallowed_rule, 'Member'))

        # Check whether admin role can perform expected actions.
        allowed_rules.extend(disallowed_rules)
        for rule in allowed_rules:
            allowed = authority.allowed(rule, 'admin')
            self.assertTrue(allowed)

        # Check whether _try_rule is called with the correct target dictionary.
        with mock.patch.object(
            authority, '_try_rule', return_value=True, autospec=True) \
            as mock_try_rule:

            expected_target = {
                "project_id": mock.sentinel.tenant_id,
                "tenant_id": mock.sentinel.tenant_id,
                "network:tenant_id": mock.sentinel.tenant_id,
                "user_id": mock.sentinel.user_id
            }

            expected_access_data = {
                "roles": ['Member'],
                "is_admin": False,
                "is_admin_project": True,
                "user_id": mock.sentinel.user_id,
                "tenant_id": mock.sentinel.tenant_id,
                "project_id": mock.sentinel.tenant_id
            }

            for rule in allowed_rules:
                allowed = authority.allowed(rule, 'Member')
                self.assertTrue(allowed)
                mock_try_rule.assert_called_once_with(rule, expected_target,
                                                      expected_access_data,
                                                      mock.ANY)
                mock_try_rule.reset_mock()
예제 #10
0
    def test_discover_policy_files(self):
        policy_parser = policy_authority.PolicyAuthority(
            None, None, 'tenant_rbac_policy')

        # Ensure that "policy_files" is set at class and instance levels.
        self.assertIn('policy_files', dir(policy_authority.PolicyAuthority))
        self.assertIn('policy_files', dir(policy_parser))
        self.assertIn('tenant_rbac_policy', policy_parser.policy_files)
        self.assertEqual(self.conf_policy_path % 'tenant_rbac_policy',
                         policy_parser.policy_files['tenant_rbac_policy'])
예제 #11
0
    def test_invalid_policy_rule_throws_rbac_parsing_exception(self, m_log):
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "custom_rbac_policy")

        fake_rule = 'fake_rule'
        expected_message = "Policy action: {0} not found in policy file: {1}."\
                           .format(fake_rule, self.custom_policy_file)

        e = self.assertRaises(rbac_exceptions.RbacParsingException,
                              authority.allowed, fake_rule, None)
        self.assertIn(expected_message, str(e))
        m_log.debug.assert_called_once_with(expected_message)
예제 #12
0
    def test_invalid_policy_rule_throws_rbac_parsing_exception(self, m_log):
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "custom_rbac_policy")

        fake_rule = 'fake_rule'
        expected_message = (
            'Policy action "{0}" not found in policy files: {1} or among '
            'registered policy in code defaults for {2} service.').format(
            fake_rule, [self.custom_policy_file], "custom_rbac_policy")

        e = self.assertRaises(rbac_exceptions.RbacParsingException,
                              authority.allowed, fake_rule, [None])
        self.assertIn(expected_message, str(e))
        m_log.debug.assert_called_once_with(expected_message)
예제 #13
0
    def test_unknown_exception_throws_rbac_parsing_exception(self, m_log):
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id

        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "custom_rbac_policy")
        authority.rules = mock.MagicMock(
            __name__='foo',
            **{'__getitem__.return_value.side_effect': Exception(
               mock.sentinel.error)})

        expected_message = "Policy action: {0} not found in "\
                           "policy file: {1}.".format(mock.sentinel.rule,
                                                      self.custom_policy_file)

        e = self.assertRaises(rbac_exceptions.RbacParsingException,
                              authority.allowed, mock.sentinel.rule, None)
        self.assertIn(expected_message, str(e))
        m_log.debug.assert_called_once_with(expected_message)
예제 #14
0
    def _test_policy_file(self, roles, allowed_rules,
                          disallowed_rules, authority=None, service=None):
        if authority is None:
            self.assertIsNotNone(service)
            test_tenant_id = mock.sentinel.tenant_id
            test_user_id = mock.sentinel.user_id
            authority = policy_authority.PolicyAuthority(
                test_tenant_id, test_user_id, service)

        roles = self.test_obj.get_all_needed_roles(roles)

        for rule in allowed_rules:
            allowed = authority.allowed(rule, roles)
            self.assertTrue(allowed)

        for rule in disallowed_rules:
            allowed = authority.allowed(rule, roles)
            self.assertFalse(allowed)

        return authority
예제 #15
0
    def test_unknown_exception_throws_rbac_parsing_exception(self, m_log):
        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id

        authority = policy_authority.PolicyAuthority(
            test_tenant_id, test_user_id, "custom_rbac_policy")
        authority.rules = mock.MagicMock(
            __name__='foo',
            **{'__getitem__.return_value.side_effect': Exception(
               mock.sentinel.error)})

        expected_message = (
            'Policy action "[{0}]" not found in policy files: {1} or among '
            'registered policy in code defaults for {2} service.').format(
            mock.sentinel.rule, [self.custom_policy_file],
            "custom_rbac_policy")

        e = self.assertRaises(rbac_exceptions.RbacParsingException,
                              authority.allowed, [mock.sentinel.rule], [None])
        self.assertIn(expected_message, str(e))
        m_log.debug.assert_called_once_with(expected_message)
예제 #16
0
    def test_get_policy_data_from_file_and_from_code(self, mock_stevedore):
        fake_policy_rules = [
            self._get_fake_policy_rule('code_policy_action_1',
                                       'rule:code_rule_1'),
            self._get_fake_policy_rule('code_policy_action_2',
                                       'rule:code_rule_2'),
            self._get_fake_policy_rule('code_policy_action_3',
                                       'rule:code_rule_3'),
        ]

        mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
        mock_manager.configure_mock(name='fake_service')
        mock_stevedore.named.NamedExtensionManager.return_value = [
            mock_manager
        ]

        test_tenant_id = mock.sentinel.tenant_id
        test_user_id = mock.sentinel.user_id
        authority = policy_authority.PolicyAuthority(test_tenant_id,
                                                     test_user_id,
                                                     "tenant_rbac_policy")

        policy_data = authority._get_policy_data('fake_service')
        self.assertIsInstance(policy_data, str)

        actual_policy_data = json.loads(policy_data)
        expected_policy_data = {
            "code_policy_action_1": "rule:code_rule_1",
            "code_policy_action_2": "rule:code_rule_2",
            "code_policy_action_3": "rule:code_rule_3",
            "rule1": "tenant_id:%(network:tenant_id)s",
            "rule2": "tenant_id:%(tenant_id)s",
            "rule3": "project_id:%(project_id)s",
            "rule4": "user_id:%(user_id)s",
            "admin_tenant_rule": "role:admin and tenant_id:%(tenant_id)s",
            "admin_user_rule": "role:admin and user_id:%(user_id)s"
        }

        self.assertEqual(expected_policy_data, actual_policy_data)
예제 #17
0
def _is_authorized(test_obj, service, rule, extra_target_data, admin_only):
    """Validates whether current RBAC role has permission to do policy action.

    :param test_obj: An instance or subclass of ``tempest.test.BaseTestCase``.
    :param service: The OpenStack service that enforces ``rule``.
    :param rule: The name of the policy action. Examples include
        "identity:create_user" or "os_compute_api:os-agents".
    :param extra_target_data: Dictionary, keyed with ``oslo.policy`` generic
        check names, whose values are string literals that reference nested
        ``tempest.test.BaseTestCase`` attributes. Used by ``oslo.policy`` for
        performing matching against attributes that are sent along with the API
        calls.
    :param admin_only: Skips over ``oslo.policy`` check because the policy
        action defined by ``rule`` is not enforced by the service's policy
        enforcement engine. For example, Keystone v2 performs an admin check
        for most of its endpoints. If True, ``rule`` is effectively ignored.

    :returns: True if the current RBAC role can perform the policy action,
        else False.

    :raises RbacResourceSetupFailed: If `project_id` or `user_id` are missing
        from the `auth_provider` attribute in `test_obj`.
    :raises RbacParsingException: if ``[patrole] strict_policy_check`` is True
        and the ``rule`` does not exist in the system.
    :raises skipException: If ``[patrole] strict_policy_check`` is False and
        the ``rule`` does not exist in the system.
    """

    if admin_only:
        LOG.info("As admin_only is True, only admin role should be "
                 "allowed to perform the API. Skipping oslo.policy "
                 "check for policy action {0}.".format(rule))
        return rbac_utils.is_admin()

    try:
        project_id = test_obj.os_primary.credentials.project_id
        user_id = test_obj.os_primary.credentials.user_id
    except AttributeError as e:
        msg = ("{0}: project_id or user_id not found in os_primary.credentials"
               .format(e))
        LOG.error(msg)
        raise rbac_exceptions.RbacResourceSetupFailed(msg)

    try:
        role = CONF.patrole.rbac_test_role
        # Test RBAC against custom requirements. Otherwise use oslo.policy.
        if CONF.patrole.test_custom_requirements:
            authority = requirements_authority.RequirementsAuthority(
                CONF.patrole.custom_requirements_file, service)
        else:
            formatted_target_data = _format_extra_target_data(
                test_obj, extra_target_data)
            authority = policy_authority.PolicyAuthority(
                project_id,
                user_id,
                service,
                extra_target_data=formatted_target_data)
        is_allowed = authority.allowed(rule, role)

        if is_allowed:
            LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule, role)
        else:
            LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!", rule, role)
        return is_allowed
    except rbac_exceptions.RbacParsingException as e:
        if CONF.patrole.strict_policy_check:
            raise e
        else:
            raise testtools.TestCase.skipException(str(e))
    return False