From 092570fc5ef43497c29cf174bfff43323a49fb58 Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Thu, 13 Jun 2019 20:12:56 +0000 Subject: [PATCH] Implement system scope and default roles for token API This commit adds protection testing for the token API along with changes to default policies to properly consume system-scope and default roles. Originally, this work was going to include the ability for project and domain administrator to validate, check, or revoke tokens within the context of their authorization (e.g., a domain administrator could revoke tokens on projects within their domain). This seems like extra work for not much benefit since we're using bearer tokens. The holder of the token can do anything with that token, which means they can validate it or revoke it without using their own token. Adding project and domain administrator support seems unnecessary given the existing functionality. If someone comes forward asking for this functionality, we can re-evaluate the effort. For now, this patch is limited to system user support, allowing them to validate, check, and revoke any token in the system. Service users can still validate tokens on behalf of users. Users can do anything they wish with their own tokens. This commit also bumps the minimum version of oslo.log so that we can use the official TRAIN deprecated release marker. Change-Id: Ia8b35258b43213bd117df4275c907aac223342b3 Closes-Bug: 1818844 Closes-Bug: 1750676 --- keystone/common/policies/token.py | 82 +-- .../tests/unit/protection/v3/test_tokens.py | 526 ++++++++++++++++++ lower-constraints.txt | 2 +- .../notes/bug-1750676-cf70c1a27b2c8de3.yaml | 35 ++ requirements.txt | 2 +- 5 files changed, 614 insertions(+), 33 deletions(-) create mode 100644 keystone/tests/unit/protection/v3/test_tokens.py create mode 100644 releasenotes/notes/bug-1750676-cf70c1a27b2c8de3.yaml diff --git a/keystone/common/policies/token.py b/keystone/common/policies/token.py index 49165f42e2..6db9913ec2 100644 --- a/keystone/common/policies/token.py +++ b/keystone/common/policies/token.py @@ -10,54 +10,74 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy from keystone.common.policies import base +DEPRECATED_REASON = """ +As of the Train release, the token API now understands how to handle +system-scoped tokens, making the API more accessible to users without +compromising security or manageability for administrators. This support +includes a read-only role by default. +""" + +deprecated_check_token = policy.DeprecatedRule( + name=base.IDENTITY % 'check_token', + check_str=base.RULE_ADMIN_OR_TOKEN_SUBJECT +) +deprecated_validate_token = policy.DeprecatedRule( + name=base.IDENTITY % 'validate_token', + check_str=base.RULE_SERVICE_ADMIN_OR_TOKEN_SUBJECT +) +deprecated_revoke_token = policy.DeprecatedRule( + name=base.IDENTITY % 'revoke_token', + check_str=base.RULE_ADMIN_OR_TOKEN_SUBJECT +) + +SYSTEM_ADMIN_OR_TOKEN_SUBJECT = ( + '(role:admin and system_scope:all) or rule:token_subject' # nosec +) +SYSTEM_USER_OR_TOKEN_SUBJECT = ( + '(role:reader and system_scope:all) or rule:token_subject' # nosec +) +SYSTEM_USER_OR_SERVICE_OR_TOKEN_SUBJECT = ( + '(role:reader and system_scope:all) ' # nosec + 'or rule:service_role or rule:token_subject' # nosec +) + + token_policies = [ policy.DocumentedRuleDefault( name=base.IDENTITY % 'check_token', - check_str=base.RULE_ADMIN_OR_TOKEN_SUBJECT, - # FIXME(lbragstad): Token validation should be handled within keystone, - # but it makes sense to have this be a system-level operation and a - # project-level operation. If this API is called by a system-level - # administrator, they should be able to check any token. If this API - # is called by a project administrator, then the token should be - # checked with respect to the project the administrator has a role on. - # Otherwise it would be possible for administrators in one project to - # validate tokens scoped to another project, which is a security - # concern. Note the following line should be uncommented once keystone - # supports the ability for project administrators to validate tokens - # only within their project. - # scope_types=['system', 'project'], + check_str=SYSTEM_USER_OR_TOKEN_SUBJECT, + scope_types=['system', 'domain', 'project'], description='Check a token.', operations=[{'path': '/v3/auth/tokens', - 'method': 'HEAD'}]), + 'method': 'HEAD'}], + deprecated_rule=deprecated_check_token, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.TRAIN), policy.DocumentedRuleDefault( name=base.IDENTITY % 'validate_token', - check_str=base.RULE_SERVICE_ADMIN_OR_TOKEN_SUBJECT, - # FIXME(lbragstad): See the comment above about why this is commented - # out. If this weren't commented out and the `enforce_scope` were set - # to True, then users with project-scoped tokens would no longer be - # able to validate them by setting the same token as the X-Auth-Header - # and X-Subject-Token. - # scope_types=['system', 'project'], + check_str=SYSTEM_USER_OR_SERVICE_OR_TOKEN_SUBJECT, + scope_types=['system', 'domain', 'project'], description='Validate a token.', operations=[{'path': '/v3/auth/tokens', - 'method': 'GET'}]), + 'method': 'GET'}], + deprecated_rule=deprecated_validate_token, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.TRAIN), policy.DocumentedRuleDefault( name=base.IDENTITY % 'revoke_token', - check_str=base.RULE_ADMIN_OR_TOKEN_SUBJECT, - # FIXME(lbragstad): System administrators should be able to revoke any - # valid token. Project administrators should only be able to invalidate - # tokens scoped to the project they administer. Users should be able to - # invalidate their own tokens. If we uncommented this line without - # adding support for each of these cases in code, we'd be breaking the - # ability for users to invalidate their own tokens. - # scope_types=['system', 'project'], + check_str=SYSTEM_ADMIN_OR_TOKEN_SUBJECT, + scope_types=['system', 'domain', 'project'], description='Revoke a token.', operations=[{'path': '/v3/auth/tokens', - 'method': 'DELETE'}]) + 'method': 'DELETE'}], + deprecated_rule=deprecated_revoke_token, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.TRAIN) ] diff --git a/keystone/tests/unit/protection/v3/test_tokens.py b/keystone/tests/unit/protection/v3/test_tokens.py new file mode 100644 index 0000000000..a4379838e6 --- /dev/null +++ b/keystone/tests/unit/protection/v3/test_tokens.py @@ -0,0 +1,526 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from six.moves import http_client + +from keystone.common import provider_api +import keystone.conf +from keystone.tests.common import auth as common_auth +from keystone.tests import unit +from keystone.tests.unit import base_classes +from keystone.tests.unit import ksfixtures + +CONF = keystone.conf.CONF +PROVIDERS = provider_api.ProviderAPIs + + +class _SystemUserTokenTests(object): + + def test_user_can_validate_system_scoped_token(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_system_grant_for_user( + user['id'], self.bootstrapper.reader_role_id + ) + + system_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + system=True + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=system_auth) + system_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = system_token + c.get('/v3/auth/tokens', headers=self.headers) + + def test_user_can_validate_domain_scoped_token(self): + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + user = unit.new_user_ref(domain_id=domain['id']) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + domain_id=domain['id'] + ) + + domain_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=domain_auth) + domain_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = domain_token + c.get('/v3/auth/tokens', headers=self.headers) + + def test_user_can_validate_project_scoped_token(self): + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, + unit.new_project_ref(domain_id=CONF.identity.default_domain_id) + ) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + project_id=project['id'] + ) + + project_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + project_id=project['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=project_auth) + project_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = project_token + c.get('/v3/auth/tokens', headers=self.headers) + + +class _SystemMemberAndReaderTokenTests(object): + + def test_user_cannot_revoke_a_system_scoped_token(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_system_grant_for_user( + user['id'], self.bootstrapper.reader_role_id + ) + + system_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + system=True + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=system_auth) + system_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = system_token + c.delete( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + + def test_user_cannot_revoke_a_domain_scoped_token(self): + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + user = unit.new_user_ref(domain_id=domain['id']) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + domain_id=domain['id'] + ) + + domain_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=domain_auth) + domain_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = domain_token + c.delete( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + + def test_user_cannot_revoke_a_project_scoped_token(self): + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, + unit.new_project_ref(domain_id=CONF.identity.default_domain_id) + ) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + project_id=project['id'] + ) + + project_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + project_id=project['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=project_auth) + project_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = project_token + c.delete( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + + +class SystemReaderTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _SystemUserTokenTests, + _SystemMemberAndReaderTokenTests): + + def setUp(self): + super(SystemReaderTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + system_reader = unit.new_user_ref( + domain_id=CONF.identity.default_domain_id + ) + self.user_id = PROVIDERS.identity_api.create_user( + system_reader + )['id'] + PROVIDERS.assignment_api.create_system_grant_for_user( + self.user_id, self.bootstrapper.reader_role_id + ) + + auth = self.build_authentication_request( + user_id=self.user_id, password=system_reader['password'], + system=True + ) + + # Grab a token using the persona we're testing and prepare headers + # for requests we'll be making in the tests. + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=auth) + self.token_id = r.headers['X-Subject-Token'] + self.headers = {'X-Auth-Token': self.token_id} + + +class SystemMemberTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _SystemUserTokenTests, + _SystemMemberAndReaderTokenTests): + + def setUp(self): + super(SystemMemberTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + system_reader = unit.new_user_ref( + domain_id=CONF.identity.default_domain_id + ) + self.user_id = PROVIDERS.identity_api.create_user( + system_reader + )['id'] + PROVIDERS.assignment_api.create_system_grant_for_user( + self.user_id, self.bootstrapper.reader_role_id + ) + + auth = self.build_authentication_request( + user_id=self.user_id, password=system_reader['password'], + system=True + ) + + # Grab a token using the persona we're testing and prepare headers + # for requests we'll be making in the tests. + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=auth) + self.token_id = r.headers['X-Subject-Token'] + self.headers = {'X-Auth-Token': self.token_id} + + +class SystemAdminTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _SystemUserTokenTests): + + def setUp(self): + super(SystemAdminTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + self.user_id = self.bootstrapper.admin_user_id + auth = self.build_authentication_request( + user_id=self.user_id, + password=self.bootstrapper.admin_password, + system=True + ) + + # Grab a token using the persona we're testing and prepare headers + # for requests we'll be making in the tests. + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=auth) + self.token_id = r.headers['X-Subject-Token'] + self.headers = {'X-Auth-Token': self.token_id} + + def test_user_can_revoke_a_system_scoped_token(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_system_grant_for_user( + user['id'], self.bootstrapper.reader_role_id + ) + + system_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + system=True + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=system_auth) + system_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = system_token + c.delete('/v3/auth/tokens', headers=self.headers) + + def test_user_can_revoke_a_domain_scoped_token(self): + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + user = unit.new_user_ref(domain_id=domain['id']) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + domain_id=domain['id'] + ) + + domain_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=domain_auth) + domain_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = domain_token + c.delete('/v3/auth/tokens', headers=self.headers) + + def test_user_can_revoke_a_project_scoped_token(self): + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, + unit.new_project_ref(domain_id=CONF.identity.default_domain_id) + ) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + project_id=project['id'] + ) + + project_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + project_id=project['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=project_auth) + project_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = project_token + c.delete('/v3/auth/tokens', headers=self.headers) + + +class _DomainAndProjectUserTests(object): + + def test_user_can_validate_their_own_tokens(self): + with self.test_client() as c: + self.headers['X-Subject-Token'] = self.token_id + c.get('/v3/auth/tokens', headers=self.headers) + + def test_user_cannot_validate_system_scoped_token(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_system_grant_for_user( + user['id'], self.bootstrapper.reader_role_id + ) + + system_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + system=True + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=system_auth) + system_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = system_token + c.get( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + + def test_user_cannot_validate_domain_scoped_token(self): + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + user = unit.new_user_ref(domain_id=domain['id']) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + domain_id=domain['id'] + ) + + domain_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=domain_auth) + domain_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = domain_token + c.get( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + pass + + def test_user_cannot_validate_project_scoped_token(self): + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, + unit.new_project_ref(domain_id=CONF.identity.default_domain_id) + ) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user['id'] = PROVIDERS.identity_api.create_user(user)['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + project_id=project['id'] + ) + + project_auth = self.build_authentication_request( + user_id=user['id'], password=user['password'], + project_id=project['id'] + ) + + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=project_auth) + project_token = r.headers['X-Subject-Token'] + + with self.test_client() as c: + self.headers['X-Subject-Token'] = project_token + c.get( + '/v3/auth/tokens', headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + + +class DomainUserTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _DomainAndProjectUserTests): + + def setUp(self): + super(DomainUserTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + self.domain_id = domain['id'] + domain_user = unit.new_user_ref(domain_id=self.domain_id) + self.domain_user_id = PROVIDERS.identity_api.create_user( + domain_user + )['id'] + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.member_role_id, user_id=self.domain_user_id, + domain_id=self.domain_id + ) + + auth = self.build_authentication_request( + user_id=self.domain_user_id, password=domain_user['password'], + domain_id=self.domain_id + ) + + # Grab a token using the persona we're testing and prepare headers + # for requests we'll be making in the tests. + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=auth) + self.token_id = r.headers['X-Subject-Token'] + self.headers = {'X-Auth-Token': self.token_id} + + +class ProjectUserTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _DomainAndProjectUserTests): + + def setUp(self): + super(ProjectUserTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + self.domain_id = domain['id'] + + project_reader = unit.new_user_ref(domain_id=self.domain_id) + project_reader_id = PROVIDERS.identity_api.create_user( + project_reader + )['id'] + project = unit.new_project_ref(domain_id=self.domain_id) + project_id = PROVIDERS.resource_api.create_project( + project['id'], project + )['id'] + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=project_reader_id, + project_id=project_id + ) + + auth = self.build_authentication_request( + user_id=project_reader_id, + password=project_reader['password'], + project_id=project_id + ) + + # Grab a token using the persona we're testing and prepare headers + # for requests we'll be making in the tests. + with self.test_client() as c: + r = c.post('/v3/auth/tokens', json=auth) + self.token_id = r.headers['X-Subject-Token'] + self.headers = {'X-Auth-Token': self.token_id} diff --git a/lower-constraints.txt b/lower-constraints.txt index dab11af996..307e9246d3 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -31,7 +31,7 @@ oslo.config==5.2.0 oslo.context==2.22.0 oslo.db==4.27.0 oslo.i18n==3.15.3 -oslo.log==3.38.0 +oslo.log==3.44.0 oslo.messaging==5.29.0 oslo.middleware==3.31.0 oslo.policy==1.43.1 diff --git a/releasenotes/notes/bug-1750676-cf70c1a27b2c8de3.yaml b/releasenotes/notes/bug-1750676-cf70c1a27b2c8de3.yaml new file mode 100644 index 0000000000..293e149499 --- /dev/null +++ b/releasenotes/notes/bug-1750676-cf70c1a27b2c8de3.yaml @@ -0,0 +1,35 @@ +--- +features: + - | + [`bug 1750676 `_] + [`bug 1818844 `_] + The token API now supports the ``admin``, ``member``, and ``reader`` + default roles. +upgrade: + - | + [`bug 1750676 `_] + [`bug 1818844 `_] + The token API uses new default policies that make it easier for system + users to delegate functionality in a secure way. Please consider the new + policies if your deployment overrides the token policies. +deprecations: + - | + [`bug 1750676 `_] + [`bug 1818844 `_] + The ``identity:check_token`` policy now uses ``(role:reader and + system_scope:all) or rule:token_subject`` instead of ``rule:admin_required + or rule:token_subject``. The ``identity:validate_token`` policy now uses + ``(role:reader and system_scope:all) or rule:service_role or + rule:token_subject`` instead or ``rule:service_or_admin or + rule:token_subject``. The ``identity:revoke_token`` policy now uses + ``(role:admin and system_scope:all) or rule:token_subject`` instead of + ``rule:admin_or_token_subject``. These new defaults automatically account + for a read-only role by default and allow more granular access to the API. + Please consider these new defaults if your deployment overrides the token + policies. +security: + - | + [`bug 1750676 `_] + [`bug 1818844 `_] + The token API now uses system-scope and default roles properly to provide + more granular access to the token API. diff --git a/requirements.txt b/requirements.txt index 90e0375b64..b46f3cfe40 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ oslo.context>=2.22.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.db>=4.27.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 -oslo.log>=3.38.0 # Apache-2.0 +oslo.log>=3.44.0 # Apache-2.0 oslo.middleware>=3.31.0 # Apache-2.0 oslo.policy>=1.43.1 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0