Merge "Implement the Domain Manager Persona for Keystone"

This commit is contained in:
Zuul 2024-08-28 18:29:44 +00:00 committed by Gerrit Code Review
commit dd30a921a2
10 changed files with 194 additions and 50 deletions

View File

@ -26,10 +26,14 @@ functionality to their team, auditors, customers, and users without maintaining
custom policies. custom policies.
In addition to ``admin``, ``member``, and ``reader`` role, from 2023.2 (Bobcat) In addition to ``admin``, ``member``, and ``reader`` role, from 2023.2 (Bobcat)
release keystone will provide ``service`` role by default as well. Operators release keystone will provide the ``service`` and ``manager`` roles by default
can use this role for service to service API calls instead of using ``admin`` as well. Operators can use the ``service`` role for service to service API
role for the same. The service role will be separate from ``admin``, calls instead of using ``admin`` role for the same. The service role will be
``member``, ``reader`` and will not implicate any of these roles. separate from ``admin``, ``member``, ``reader`` and will not implicate any of
these roles.
Operators can give the ``manager`` role to users to within a domain to enable
self-service management of users, groups, projects and role assignments within
their domain.
.. _`token guide`: https://docs.openstack.org/keystone/latest/admin/tokens-overview.html#authorization-scopes .. _`token guide`: https://docs.openstack.org/keystone/latest/admin/tokens-overview.html#authorization-scopes
@ -39,14 +43,16 @@ Roles Definitions
The default roles provided by keystone via ``keystone-manage bootstrap`` The default roles provided by keystone via ``keystone-manage bootstrap``
(except for the ``service`` role) are related through role implications. The (except for the ``service`` role) are related through role implications. The
``admin`` role implies the ``member`` role, and the ``member`` role implies ``admin`` role implies the ``manager`` role, the ``manager`` implies the
the ``reader`` role. These implications mean users with the ``admin`` role ``member`` role, and the ``member`` role implies the ``reader`` role. These
automatically have the ``member`` and ``reader`` roles. Additionally, implications mean users with the ``admin`` role automatically have the
users with the ``member`` role automatically have the ``reader`` role. ``manager``, ``member`` and ``reader`` roles. Additionally, users with the
Implying roles reduces role assignments and forms a natural hierarchy between ``manager`` role automatically have the ``member`` and ``reader`` roles. Users
the default roles. It also reduces the complexity of default policies by with the ``member`` role automatically have the ``reader`` role. Implying roles
making check strings short. For example, a policy that requires ``reader`` reduces role assignments and forms a natural hierarchy between the default
can be expressed as: roles. It also reduces the complexity of default policies by making check
strings short. For example, a policy that requires ``reader`` can be expressed
as:
.. code-block:: yaml .. code-block:: yaml
@ -56,7 +62,7 @@ Instead of:
.. code-block:: yaml .. code-block:: yaml
"identity:list_foo": "role:admin or role:member or role:reader" "identity:list_foo": "role:admin or role:manager or role:member or role:reader"
Reader Reader
====== ======
@ -122,6 +128,30 @@ users with ``admin`` on a project can list, create, and delete instances.
Service developers can use the ``member`` role to provide more flexibility Service developers can use the ``member`` role to provide more flexibility
between ``admin`` and ``reader`` on different scopes. between ``admin`` and ``reader`` on different scopes.
Manager
=======
The ``manager`` role takes a special place in keystone. It sits between the
``admin`` and ``member`` role, allowing limited identity management while being
clearly differentiated from the ``admin`` role both in terms of purpose and
privileges. The ``manager`` role is meant to be assigned in a domain scope and
enables users to manage identity assets in a whole domain including users,
projects, groups and role assignments. This enables identity self-service
management capabilities for users within a domain without the need to assign
the most privileged ``admin`` role to them.
The keystone default policies include a special rule that specifies the list of
roles a user with the ``manager`` role is be able to assign and revoke within
the domain scope. This prevents such user from escalating their own privileges
or those of others beyond ``manager`` and for this purpose the list excludes
the ``admin`` role. The list can be adjusted by cloud administrators via policy
definitions in case the role model differs. For example, if a new role is
introduced for a specific cloud environment, the list can be adjusted to allow
users with the ``manager`` role to also assign it.
Other services might write default policies to enable the ``manager`` role to
have more privileged managing rights or cross-project privileges in a domain.
Admin Admin
===== =====
@ -255,14 +285,15 @@ role assignments on a specific domain using the following query:
.. code-block:: console .. code-block:: console
$ openstack role assignment list --names --domain foobar $ openstack role assignment list --names --domain foobar
+--------+-----------------+----------------------+---------+--------+--------+-----------+ +---------+-----------------+----------------------+---------+--------+--------+-----------+
| Role | User | Group | Project | Domain | System | Inherited | | Role | User | Group | Project | Domain | System | Inherited |
+--------+-----------------+----------------------+---------+--------+--------+-----------+ +---------+-----------------+----------------------+---------+--------+--------+-----------+
| reader | support@Default | | | foobar | | False | | reader | support@Default | | | foobar | | False |
| admin | jsmith@Default | | | foobar | | False | | admin | jsmith@Default | | | foobar | | False |
| admin | | foobar-admins@foobar | | foobar | | False | | admin | | foobar-admins@foobar | | foobar | | False |
| member | jdoe@foobar | | | foobar | | False | | manager | alice@foobar | | | foobar | | False |
+--------+-----------------+----------------------+---------+--------+--------+-----------+ | member | jdoe@foobar | | | foobar | | False |
+---------+-----------------+----------------------+---------+--------+--------+-----------+
Domain Administrators Domain Administrators
===================== =====================
@ -288,6 +319,32 @@ assignment:
| admin | | foobar-admins@foobar | | foobar | | False | | admin | | foobar-admins@foobar | | foobar | | False |
+-------+----------------+----------------------+---------+--------+--------+-----------+ +-------+----------------+----------------------+---------+--------+--------+-----------+
Domain Managers
===============
*Domain managers* can only manage specific resources related to identity
management within their domain. This includes creating new users, projects and
groups as well as updating and deleting them. They can also assign and revoke
roles between those or in relation to the domain. Furthermore, they can inspect
role assignments within the domain.
*Domain managers* cannot change any aspects of the domain itself. The role
assignments they can apply within their domain is limited to a specific list of
applicable roles and in the default configuration, this excludes the ``admin``
role to prevent privilege escalation.
You can find *domain managers* in your deployment with the following role
assignment:
.. code-block:: console
$ openstack role assignment list --names --domain foobar --role manager
+---------+-----------------+----------------------+---------+--------+--------+-----------+
| Role | User | Group | Project | Domain | System | Inherited |
+---------+-----------------+----------------------+---------+--------+--------+-----------+
| manager | alice@foobar | | | foobar | | False |
+---------+-----------------+----------------------+---------+--------+--------+-----------+
Domain Members & Domain Readers Domain Members & Domain Readers
=============================== ===============================

View File

@ -111,7 +111,10 @@ class RoleResource(ks_flask.ResourceBase):
""" """
role = self.request_body_json.get('role', {}) role = self.request_body_json.get('role', {})
if self._is_domain_role(role): if self._is_domain_role(role):
ENFORCER.enforce_call(action='identity:create_domain_role') target = {'role': role}
ENFORCER.enforce_call(
action='identity:create_domain_role', target_attr=target
)
else: else:
ENFORCER.enforce_call(action='identity:create_role') ENFORCER.enforce_call(action='identity:create_role')
validation.lazy_validate(schema.role_create, role) validation.lazy_validate(schema.role_create, role)

View File

@ -65,6 +65,18 @@ ADMIN_OR_CRED_OWNER = (
'(' + RULE_ADMIN_REQUIRED + ') ' 'or user_id:%(target.credential.user_id)s' '(' + RULE_ADMIN_REQUIRED + ') ' 'or user_id:%(target.credential.user_id)s'
) )
# This rule template is meant for restricting role assignments done by domain
# managers. It is intended to restrict the roles a domain manager can assign or
# revoke to a sensible default set while allowing overrides via policy file by
# adjusting the corresponding rule definition.
# For the default, any roles with higher-level privileges than "manager" (e.g.
# "admin") must be omitted to avoid privilege escalation.
DOMAIN_MANAGER_ALLOWED_ROLES = (
"'manager':%(target.role.name)s or "
"'member':%(target.role.name)s or "
"'reader':%(target.role.name)s"
)
rules = [ rules = [
policy.RuleDefault( policy.RuleDefault(
name='admin_required', check_str='role:admin or is_admin:1' name='admin_required', check_str='role:admin or is_admin:1'
@ -89,6 +101,10 @@ rules = [
name='service_admin_or_token_subject', name='service_admin_or_token_subject',
check_str='rule:service_or_admin or rule:token_subject', check_str='rule:service_or_admin or rule:token_subject',
), ),
policy.RuleDefault(
name="domain_managed_target_role",
check_str=DOMAIN_MANAGER_ALLOWED_ROLES,
),
] ]

View File

@ -61,6 +61,17 @@ GRANTS_DOMAIN_ADMIN = (
' ' + DOMAIN_MATCHES_TARGET_DOMAIN + ')' ' ' + DOMAIN_MATCHES_TARGET_DOMAIN + ')'
) )
GRANTS_DOMAIN_MANAGER = (
'(role:manager and ' + DOMAIN_MATCHES_USER_DOMAIN + ' and'
' ' + DOMAIN_MATCHES_PROJECT_DOMAIN + ') or '
'(role:manager and ' + DOMAIN_MATCHES_USER_DOMAIN + ' and'
' ' + DOMAIN_MATCHES_TARGET_DOMAIN + ') or '
'(role:manager and ' + DOMAIN_MATCHES_GROUP_DOMAIN + ' and'
' ' + DOMAIN_MATCHES_PROJECT_DOMAIN + ') or '
'(role:manager and ' + DOMAIN_MATCHES_GROUP_DOMAIN + ' and'
' ' + DOMAIN_MATCHES_TARGET_DOMAIN + ')'
)
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER = ( ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER = (
'(' + base.RULE_ADMIN_REQUIRED + ') or ' '(' + base.RULE_ADMIN_REQUIRED + ') or '
'(' + SYSTEM_READER_OR_DOMAIN_READER + ')' '(' + SYSTEM_READER_OR_DOMAIN_READER + ')'
@ -71,10 +82,12 @@ ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_LIST = (
'(' + SYSTEM_READER_OR_DOMAIN_READER_LIST + ')' '(' + SYSTEM_READER_OR_DOMAIN_READER_LIST + ')'
) )
ADMIN_OR_DOMAIN_ADMIN = ( ADMIN_OR_DOMAIN_ADMIN_OR_DOMAIN_MANAGER = (
'(' + base.RULE_ADMIN_REQUIRED + ') or ' '(' + base.RULE_ADMIN_REQUIRED + ') or '
'(' + GRANTS_DOMAIN_ADMIN + ') and ' '(' + GRANTS_DOMAIN_ADMIN + ') and '
'(' + DOMAIN_MATCHES_ROLE + ')' '(' + DOMAIN_MATCHES_ROLE + ') or '
'(' + GRANTS_DOMAIN_MANAGER + ') and '
'rule:domain_managed_target_role'
) )
DEPRECATED_REASON = ( DEPRECATED_REASON = (
@ -236,7 +249,7 @@ grant_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'create_grant', name=base.IDENTITY % 'create_grant',
check_str=ADMIN_OR_DOMAIN_ADMIN, check_str=ADMIN_OR_DOMAIN_ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description=( description=(
'Create a role grant between a target and an actor. A ' 'Create a role grant between a target and an actor. A '
@ -251,7 +264,7 @@ grant_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'revoke_grant', name=base.IDENTITY % 'revoke_grant',
check_str=ADMIN_OR_DOMAIN_ADMIN, check_str=ADMIN_OR_DOMAIN_ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description=( description=(
'Revoke a role grant between a target and an actor. A ' 'Revoke a role grant between a target and an actor. A '

View File

@ -33,6 +33,14 @@ SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP_USER = (
'domain_id:%(target.group.domain_id)s and ' 'domain_id:%(target.group.domain_id)s and '
'domain_id:%(target.user.domain_id)s)' 'domain_id:%(target.user.domain_id)s)'
) )
DOMAIN_MANAGER_FOR_TARGET_GROUP = (
'role:manager and domain_id:%(target.group.domain_id)s'
)
DOMAIN_MANAGER_FOR_TARGET_GROUP_USER = (
'role:manager and '
'domain_id:%(target.group.domain_id)s and '
'domain_id:%(target.user.domain_id)s'
)
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP = ( ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP = (
'(' '('
+ base.RULE_ADMIN_REQUIRED + base.RULE_ADMIN_REQUIRED
@ -53,6 +61,21 @@ SYSTEM_ADMIN_OR_DOMAIN_ADMIN = (
'(role:admin and domain_id:%(target.group.domain_id)s)' '(role:admin and domain_id:%(target.group.domain_id)s)'
) )
ADMIN_OR_DOMAIN_MANAGER_FOR_GROUPS = (
'('
+ base.RULE_ADMIN_REQUIRED
+ ') or ('
+ DOMAIN_MANAGER_FOR_TARGET_GROUP
+ ')'
)
ADMIN_OR_DOMAIN_MANAGER_FOR_GROUP_ASSIGNMENTS = (
'('
+ base.RULE_ADMIN_REQUIRED
+ ') or ('
+ DOMAIN_MANAGER_FOR_TARGET_GROUP_USER
+ ')'
)
DEPRECATED_REASON = ( DEPRECATED_REASON = (
"The group API is now aware of system scope and default roles." "The group API is now aware of system scope and default roles."
) )
@ -154,7 +177,7 @@ group_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'create_group', name=base.IDENTITY % 'create_group',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER_FOR_GROUPS,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Create group.', description='Create group.',
operations=[{'path': '/v3/groups', 'method': 'POST'}], operations=[{'path': '/v3/groups', 'method': 'POST'}],
@ -162,7 +185,7 @@ group_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_group', name=base.IDENTITY % 'update_group',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER_FOR_GROUPS,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Update group.', description='Update group.',
operations=[{'path': '/v3/groups/{group_id}', 'method': 'PATCH'}], operations=[{'path': '/v3/groups/{group_id}', 'method': 'PATCH'}],
@ -170,7 +193,7 @@ group_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_group', name=base.IDENTITY % 'delete_group',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER_FOR_GROUPS,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Delete group.', description='Delete group.',
operations=[{'path': '/v3/groups/{group_id}', 'method': 'DELETE'}], operations=[{'path': '/v3/groups/{group_id}', 'method': 'DELETE'}],
@ -189,7 +212,7 @@ group_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'remove_user_from_group', name=base.IDENTITY % 'remove_user_from_group',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER_FOR_GROUP_ASSIGNMENTS,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Remove user from group.', description='Remove user from group.',
operations=[ operations=[
@ -216,7 +239,7 @@ group_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'add_user_to_group', name=base.IDENTITY % 'add_user_to_group',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER_FOR_GROUP_ASSIGNMENTS,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Add user to group.', description='Add user to group.',
operations=[ operations=[

View File

@ -49,7 +49,7 @@ SYSTEM_READER_OR_DOMAIN_READER_OR_OWNER = (
# the context user_id to the target user id. # the context user_id to the target user id.
'user_id:%(target.user.id)s' 'user_id:%(target.user.id)s'
) )
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_OR_OWNER = ( ADMIN_SYSTEM_READER_OR_DOMAIN_READER_OR_OWNER = (
'(' '('
+ base.RULE_ADMIN_REQUIRED + base.RULE_ADMIN_REQUIRED
+ ') or ' + ') or '
@ -65,9 +65,9 @@ ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER = (
'(' + base.RULE_ADMIN_REQUIRED + ') or ' + SYSTEM_READER_OR_DOMAIN_READER '(' + base.RULE_ADMIN_REQUIRED + ') or ' + SYSTEM_READER_OR_DOMAIN_READER
) )
SYSTEM_ADMIN_OR_DOMAIN_ADMIN = ( ADMIN_OR_DOMAIN_MANAGER = (
'(role:admin and system_scope:all) or ' '(' + base.RULE_ADMIN_REQUIRED + ') or '
'(role:admin and domain_id:%(target.project.domain_id)s)' '(role:manager and domain_id:%(target.project.domain_id)s)'
) )
DEPRECATED_REASON = ( DEPRECATED_REASON = (
@ -175,7 +175,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_user_projects', name=base.IDENTITY % 'list_user_projects',
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_OR_OWNER, check_str=ADMIN_SYSTEM_READER_OR_DOMAIN_READER_OR_OWNER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='List projects for user.', description='List projects for user.',
operations=[{'path': '/v3/users/{user_id}/projects', 'method': 'GET'}], operations=[{'path': '/v3/users/{user_id}/projects', 'method': 'GET'}],
@ -183,7 +183,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'create_project', name=base.IDENTITY % 'create_project',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Create project.', description='Create project.',
operations=[{'path': '/v3/projects', 'method': 'POST'}], operations=[{'path': '/v3/projects', 'method': 'POST'}],
@ -191,7 +191,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_project', name=base.IDENTITY % 'update_project',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Update project.', description='Update project.',
operations=[{'path': '/v3/projects/{project_id}', 'method': 'PATCH'}], operations=[{'path': '/v3/projects/{project_id}', 'method': 'PATCH'}],
@ -199,7 +199,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_project', name=base.IDENTITY % 'delete_project',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Delete project.', description='Delete project.',
operations=[{'path': '/v3/projects/{project_id}', 'method': 'DELETE'}], operations=[{'path': '/v3/projects/{project_id}', 'method': 'DELETE'}],
@ -235,7 +235,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_project_tags', name=base.IDENTITY % 'update_project_tags',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Replace all tags on a project with the new set of tags.', description='Replace all tags on a project with the new set of tags.',
operations=[ operations=[
@ -245,7 +245,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'create_project_tag', name=base.IDENTITY % 'create_project_tag',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Add a single tag to a project.', description='Add a single tag to a project.',
operations=[ operations=[
@ -255,7 +255,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_project_tags', name=base.IDENTITY % 'delete_project_tags',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Remove all tags from a project.', description='Remove all tags from a project.',
operations=[ operations=[
@ -265,7 +265,7 @@ project_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_project_tag', name=base.IDENTITY % 'delete_project_tag',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Delete a specified tag from project.', description='Delete a specified tag from project.',
operations=[ operations=[

View File

@ -15,6 +15,19 @@ from oslo_policy import policy
from keystone.common.policies import base from keystone.common.policies import base
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_MANAGER_ROLE = (
'(' + base.RULE_ADMIN_OR_SYSTEM_READER + ') or '
'(role:manager and rule:domain_managed_target_role)'
)
# For the domain manager persona we only check for a domain id in the token
# that is not None here to exclude scopes like a project manager. Since most
# roles are global we do not have a target domain attribute to match against.
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_MANAGER = (
'(' + base.RULE_ADMIN_OR_SYSTEM_READER + ') or '
'(role:manager and not domain_id:None)'
)
DEPRECATED_REASON = ( DEPRECATED_REASON = (
"The role API is now aware of system scope and default roles." "The role API is now aware of system scope and default roles."
) )
@ -84,7 +97,7 @@ deprecated_delete_domain_role = policy.DeprecatedRule(
role_policies = [ role_policies = [
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_role', name=base.IDENTITY % 'get_role',
check_str=base.RULE_ADMIN_OR_SYSTEM_READER, check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_MANAGER_ROLE,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Show role details.', description='Show role details.',
operations=[ operations=[
@ -95,7 +108,7 @@ role_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_roles', name=base.IDENTITY % 'list_roles',
check_str=base.RULE_ADMIN_OR_SYSTEM_READER, check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='List roles.', description='List roles.',
operations=[ operations=[

View File

@ -30,10 +30,16 @@ ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_OR_USER = (
SYSTEM_READER_OR_DOMAIN_READER = ( SYSTEM_READER_OR_DOMAIN_READER = (
'(' + base.SYSTEM_READER + ') or (' + base.DOMAIN_READER + ')' '(' + base.SYSTEM_READER + ') or (' + base.DOMAIN_READER + ')'
) )
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER = (
ADMIN_SYSTEM_READER_OR_DOMAIN_READER = (
'(' + base.RULE_ADMIN_REQUIRED + ') or ' + SYSTEM_READER_OR_DOMAIN_READER '(' + base.RULE_ADMIN_REQUIRED + ') or ' + SYSTEM_READER_OR_DOMAIN_READER
) )
ADMIN_OR_DOMAIN_MANAGER = (
'(' + base.RULE_ADMIN_REQUIRED + ') or '
'(role:manager and token.domain.id:%(target.user.domain_id)s)'
)
DEPRECATED_REASON = ( DEPRECATED_REASON = (
"The user API is now aware of system scope and default roles." "The user API is now aware of system scope and default roles."
) )
@ -83,7 +89,7 @@ user_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_users', name=base.IDENTITY % 'list_users',
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER, check_str=ADMIN_SYSTEM_READER_OR_DOMAIN_READER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='List users.', description='List users.',
operations=[ operations=[
@ -120,7 +126,7 @@ user_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'create_user', name=base.IDENTITY % 'create_user',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Create a user.', description='Create a user.',
operations=[{'path': '/v3/users', 'method': 'POST'}], operations=[{'path': '/v3/users', 'method': 'POST'}],
@ -128,7 +134,7 @@ user_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_user', name=base.IDENTITY % 'update_user',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Update a user, including administrative password resets.', description='Update a user, including administrative password resets.',
operations=[{'path': '/v3/users/{user_id}', 'method': 'PATCH'}], operations=[{'path': '/v3/users/{user_id}', 'method': 'PATCH'}],
@ -136,7 +142,7 @@ user_policies = [
), ),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_user', name=base.IDENTITY % 'delete_user',
check_str=base.RULE_ADMIN_REQUIRED, check_str=ADMIN_OR_DOMAIN_MANAGER,
scope_types=['system', 'domain', 'project'], scope_types=['system', 'domain', 'project'],
description='Delete a user.', description='Delete a user.',
operations=[{'path': '/v3/users/{user_id}', 'method': 'DELETE'}], operations=[{'path': '/v3/users/{user_id}', 'method': 'DELETE'}],

View File

@ -243,6 +243,7 @@ class PolicyJsonTestCase(unit.TestCase):
'service_or_admin', 'service_or_admin',
'service_role', 'service_role',
'token_subject', 'token_subject',
'domain_managed_target_role',
] ]
def read_doc_targets(): def read_doc_targets():

View File

@ -0,0 +1,12 @@
---
features:
- >
[`bug 2045974 <https://bugs.launchpad.net/keystone/+bug/2045974>`_]
The Domain Manager Persona has been added. This makes identity-related
self-service capabilities for users within domains possible without
requiring the 'admin' role. Assigning the 'manager' role to users in domain
scope now allows them to manage projects, groups, users and role
assignments within the domain. This is subject to the following
restriction: the roles that domain managers can assign and revoke are
limited by a new ``domain_managed_target_role`` policy rule which defaults
to 'reader', 'member' and 'manager'.