summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2020-04-10 00:18:37 +0000
committerGerrit Code Review <review@openstack.org>2020-04-10 00:18:37 +0000
commitb3d37351389600a8383c7148e8b89aa73ad6e558 (patch)
tree69b7d8582c873fc1e2c12fe8bfad02d3b415a195
parentf5fd13f26f3e00478140c9d330a0a2689ac05a8c (diff)
parent8153a9d5925b0ba60e43329ff6bfb5a4d1a12f97 (diff)
downloadkeystone-b3d37351389600a8383c7148e8b89aa73ad6e558.tar.gz
Merge "Add expiring user group memberships on mapped authentication"
-rw-r--r--api-ref/source/v3/index.rst8
-rw-r--r--doc/source/admin/federation/configure_federation.rst11
-rw-r--r--keystone/api/discovery.py4
-rw-r--r--keystone/auth/plugins/mapped.py10
-rw-r--r--keystone/federation/core.py2
-rw-r--r--keystone/identity/core.py39
-rw-r--r--keystone/tests/unit/test_backend_sql.py35
-rw-r--r--keystone/tests/unit/test_versions.py4
-rw-r--r--keystone/version.py2
-rw-r--r--releasenotes/notes/bug-1809116-b65502f3b606b060.yaml19
10 files changed, 111 insertions, 23 deletions
diff --git a/api-ref/source/v3/index.rst b/api-ref/source/v3/index.rst
index 5b0e5ef89..98757b328 100644
--- a/api-ref/source/v3/index.rst
+++ b/api-ref/source/v3/index.rst
@@ -26,6 +26,14 @@ For information about Identity API protection, see
<https://docs.openstack.org/keystone/latest/admin/service-api-protection.html>`_
in the OpenStack Cloud Administrator Guide.
+===================================
+What's New in Version 3.14 (Ussuri)
+===================================
+
+- New attribute ``authorization_ttl`` for identity providers
+- New attribute ``membership_expires_at`` when listing groups for a user
+- Ability to persist group memberships carried through mapping for a federated user
+
==================================
What's New in Version 3.13 (Train)
==================================
diff --git a/doc/source/admin/federation/configure_federation.rst b/doc/source/admin/federation/configure_federation.rst
index 92af44e01..e6f1740f5 100644
--- a/doc/source/admin/federation/configure_federation.rst
+++ b/doc/source/admin/federation/configure_federation.rst
@@ -143,6 +143,17 @@ associating them with a local keystone group and inheriting its role
assignments, or dynamically provisioning projects within keystone based on these
rules.
+.. note::
+
+ By default, group memberships that a user gets from a mapping are only valid
+ for the duration of the token. It is possible to persist these groups
+ memberships for a limited period of time. To enable this, either
+ set the ``authorization_ttl` attribute of the identity provider, or the
+ ``[federation] default_authorization_ttl`` in the keystone.conf file. This
+ value is in minutes, and will result in a lag from when a user is removed
+ from a group in the identity provider, and when that will happen in keystone.
+ Please consider your security requirements carefully.
+
An Identity Provider has exactly one mapping specified per protocol.
Mapping objects can be used multiple times by different combinations of Identity
Provider and Protocol.
diff --git a/keystone/api/discovery.py b/keystone/api/discovery.py
index 31f25d40c..7483597b8 100644
--- a/keystone/api/discovery.py
+++ b/keystone/api/discovery.py
@@ -28,9 +28,9 @@ _DISCOVERY_BLUEPRINT = flask.Blueprint('Discovery', __name__)
def _get_versions_list(identity_url):
versions = {}
versions['v3'] = {
- 'id': 'v3.13',
+ 'id': 'v3.14',
'status': 'stable',
- 'updated': '2019-07-19T00:00:00Z',
+ 'updated': '2020-04-07T00:00:00Z',
'links': [{
'rel': 'self',
'href': identity_url,
diff --git a/keystone/auth/plugins/mapped.py b/keystone/auth/plugins/mapped.py
index 36f636620..7a45f109b 100644
--- a/keystone/auth/plugins/mapped.py
+++ b/keystone/auth/plugins/mapped.py
@@ -235,10 +235,12 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
get_user_unique_id_and_display_name(mapped_properties)
)
email = mapped_properties['user'].get('email')
- user = identity_api.shadow_federated_user(identity_provider,
- protocol, unique_id,
- display_name,
- email)
+ user = identity_api.shadow_federated_user(
+ identity_provider,
+ protocol, unique_id,
+ display_name,
+ email,
+ group_ids=mapped_properties['group_ids'])
if 'projects' in mapped_properties:
idp_domain_id = federation_api.get_idp(
diff --git a/keystone/federation/core.py b/keystone/federation/core.py
index 559e2b58a..5c268c5e1 100644
--- a/keystone/federation/core.py
+++ b/keystone/federation/core.py
@@ -182,7 +182,7 @@ class Manager(manager.Manager):
self.driver.delete_protocol(idp_id, protocol_id)
for shadow_user in shadow_users:
- PROVIDERS.identity_api.shadow_federated_user.invalidate(
+ PROVIDERS.identity_api._shadow_federated_user.invalidate(
PROVIDERS.identity_api, shadow_user['idp_id'],
shadow_user['protocol_id'], shadow_user['unique_id'],
shadow_user['display_name'],
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index 4aa489a12..f9af5c4c2 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -1135,7 +1135,7 @@ class Manager(manager.Manager):
self.get_user_by_name.invalidate(self, user_old['name'],
user_old['domain_id'])
for fed_user in fed_users:
- self.shadow_federated_user.invalidate(
+ self._shadow_federated_user.invalidate(
self, fed_user['idp_id'], fed_user['protocol_id'],
fed_user['unique_id'], fed_user['display_name'],
user_old.get('extra', {}).get('email'))
@@ -1402,18 +1402,8 @@ class Manager(manager.Manager):
return PROVIDERS.shadow_users_api.create_nonlocal_user(user)
@MEMOIZE
- def shadow_federated_user(self, idp_id, protocol_id, unique_id,
- display_name, email=None):
- """Map a federated user to a user.
-
- :param idp_id: identity provider id
- :param protocol_id: protocol id
- :param unique_id: unique id for the user within the IdP
- :param display_name: user's display name
- :param email: user's email
-
- :returns: dictionary of the mapped User entity
- """
+ def _shadow_federated_user(self, idp_id, protocol_id, unique_id,
+ display_name, email=None):
user_dict = {}
try:
PROVIDERS.shadow_users_api.update_federated_user_display_name(
@@ -1440,6 +1430,29 @@ class Manager(manager.Manager):
PROVIDERS.shadow_users_api.set_last_active_at(user_dict['id'])
return user_dict
+ def shadow_federated_user(self, idp_id, protocol_id, unique_id,
+ display_name, email=None, group_ids=None):
+ """Map a federated user to a user.
+
+ :param idp_id: identity provider id
+ :param protocol_id: protocol id
+ :param unique_id: unique id for the user within the IdP
+ :param display_name: user's display name
+ :param email: user's email
+ :param group_ids: list of group ids to add the user to
+
+ :returns: dictionary of the mapped User entity
+ """
+ user_dict = self._shadow_federated_user(
+ idp_id, protocol_id, unique_id, display_name, email)
+ # Note(knikolla): The shadowing operation can be cached,
+ # however we need to update the expiring group memberships.
+ if group_ids:
+ for group_id in group_ids:
+ PROVIDERS.shadow_users_api.add_user_to_group_expires(
+ user_dict['id'], group_id)
+ return user_dict
+
class MappingManager(manager.Manager):
"""Default pivot point for the ID Mapping backend."""
diff --git a/keystone/tests/unit/test_backend_sql.py b/keystone/tests/unit/test_backend_sql.py
index c0bff3aaa..82285fcf8 100644
--- a/keystone/tests/unit/test_backend_sql.py
+++ b/keystone/tests/unit/test_backend_sql.py
@@ -672,6 +672,41 @@ class SqlIdentity(SqlTests,
negative_user['id'])
self.assertEqual(0, len(group_refs))
+ def test_add_user_to_group_expiring_mapped(self):
+ self._build_fed_resource()
+ domain = self._get_domain_fixture()
+ self.config_fixture.config(group='federation',
+ default_authorization_ttl=5)
+ time = datetime.datetime.utcnow()
+ tick = datetime.timedelta(minutes=5)
+
+ new_group = unit.new_group_ref(domain_id=domain['id'])
+ new_group = PROVIDERS.identity_api.create_group(new_group)
+
+ fed_dict = unit.new_federated_user_ref()
+ fed_dict['idp_id'] = 'myidp'
+ fed_dict['protocol_id'] = 'mapped'
+
+ with freezegun.freeze_time(time - tick) as frozen_time:
+ user = PROVIDERS.identity_api.shadow_federated_user(
+ **fed_dict, group_ids=[new_group['id']])
+
+ PROVIDERS.identity_api.check_user_in_group(user['id'],
+ new_group['id'])
+
+ # Expiration
+ frozen_time.tick(tick)
+ self.assertRaises(exception.NotFound,
+ PROVIDERS.identity_api.check_user_in_group,
+ user['id'],
+ new_group['id'])
+
+ # Renewal
+ PROVIDERS.identity_api.shadow_federated_user(
+ **fed_dict, group_ids=[new_group['id']])
+ PROVIDERS.identity_api.check_user_in_group(user['id'],
+ new_group['id'])
+
def test_add_user_to_group_expiring(self):
self._build_fed_resource()
domain = self._get_domain_fixture()
diff --git a/keystone/tests/unit/test_versions.py b/keystone/tests/unit/test_versions.py
index 2fcea3f03..b509d2446 100644
--- a/keystone/tests/unit/test_versions.py
+++ b/keystone/tests/unit/test_versions.py
@@ -36,9 +36,9 @@ v3_MEDIA_TYPES = [
]
v3_EXPECTED_RESPONSE = {
- "id": "v3.13",
+ "id": "v3.14",
"status": "stable",
- "updated": "2019-07-19T00:00:00Z",
+ "updated": "2020-04-07T00:00:00Z",
"links": [
{
"rel": "self",
diff --git a/keystone/version.py b/keystone/version.py
index 7937efc0a..3cc1c9758 100644
--- a/keystone/version.py
+++ b/keystone/version.py
@@ -12,4 +12,4 @@
def release_string():
- return 'v3.13'
+ return 'v3.14'
diff --git a/releasenotes/notes/bug-1809116-b65502f3b606b060.yaml b/releasenotes/notes/bug-1809116-b65502f3b606b060.yaml
new file mode 100644
index 000000000..d6067ba8e
--- /dev/null
+++ b/releasenotes/notes/bug-1809116-b65502f3b606b060.yaml
@@ -0,0 +1,19 @@
+---
+features:
+ - |
+ [`bug 1809116 <https://bugs.launchpad.net/keystone/+bug/1809116>`_]
+ It is now possible to have group memberships carried over through mapping
+ persist for a limited time after a user authenticates using federation.
+ The "time to live" of these memberships is specified via the configuration
+ option `[federation] default_authorization_ttl` or for each identity
+ provider by setting `authorization_ttl` on the identity provider. Every
+ time a user authenticates carrying over that membership, it will be
+ renewed.
+security:
+ - |
+ If expiring user group memberships are enabled via the `[federation]
+ default_authorization_ttl` configuration option, or on an idp by idp
+ basis by setting `authorization_ttl`, there will be a lag between when
+ a user is removed from a group in an identity provider, and when that
+ will be reflected in keystone. That amount of time will be equal to
+ the last time the user logged in + idp ttl.