From 02452d02c44971a71ceee4699af5d7fe56280b91 Mon Sep 17 00:00:00 2001 From: Ronald De Rose Date: Wed, 7 Sep 2016 22:03:11 +0000 Subject: [PATCH] Return password_expires_at during auth The new user attribute, password_expires_at, is not being returned during auth; this patch adds it. bp password-expires-validation Change-Id: I1f17a849d9da4067d6be7d612c5a561bcb247ebb --- .../admin/auth-password-explicit-unscoped-response.json | 3 ++- .../admin/auth-password-project-scoped-response.json | 3 ++- .../samples/admin/auth-password-unscoped-response.json | 3 ++- .../v3/samples/admin/auth-token-scoped-response.json | 3 ++- .../v3/samples/admin/auth-token-unscoped-response.json | 3 ++- keystone/models/token_model.py | 9 +++++++++ keystone/tests/unit/test_token_provider.py | 3 ++- keystone/tests/unit/test_v3.py | 6 +++++- keystone/tests/unit/token/test_fernet_provider.py | 1 + keystone/tests/unit/token/test_token_model.py | 3 +++ keystone/token/providers/common.py | 3 ++- .../notes/bp-password_expired_at-4b32fe7032595932.yaml | 8 ++++++++ 12 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/bp-password_expired_at-4b32fe7032595932.yaml diff --git a/api-ref/source/v3/samples/admin/auth-password-explicit-unscoped-response.json b/api-ref/source/v3/samples/admin/auth-password-explicit-unscoped-response.json index b50d0f4afc..2d6a05d01a 100644 --- a/api-ref/source/v3/samples/admin/auth-password-explicit-unscoped-response.json +++ b/api-ref/source/v3/samples/admin/auth-password-explicit-unscoped-response.json @@ -11,7 +11,8 @@ "name": "Default" }, "id": "ee4dfb6e5540447cb3741905149d9b6e", - "name": "admin" + "name": "admin", + "password_expires_at": null }, "audit_ids": [ "lC2Wj1jbQe-dLjLyOx4qPQ" diff --git a/api-ref/source/v3/samples/admin/auth-password-project-scoped-response.json b/api-ref/source/v3/samples/admin/auth-password-project-scoped-response.json index f0b110bd7f..232b41d60a 100644 --- a/api-ref/source/v3/samples/admin/auth-password-project-scoped-response.json +++ b/api-ref/source/v3/samples/admin/auth-password-project-scoped-response.json @@ -392,7 +392,8 @@ "name": "Default" }, "id": "ee4dfb6e5540447cb3741905149d9b6e", - "name": "admin" + "name": "admin", + "password_expires_at": "2016-11-06T15:32:17.000000" }, "audit_ids": [ "3T2dc1CGQxyJsHdDu1xkcw" diff --git a/api-ref/source/v3/samples/admin/auth-password-unscoped-response.json b/api-ref/source/v3/samples/admin/auth-password-unscoped-response.json index c012a3ee9b..9f7ba48296 100644 --- a/api-ref/source/v3/samples/admin/auth-password-unscoped-response.json +++ b/api-ref/source/v3/samples/admin/auth-password-unscoped-response.json @@ -11,7 +11,8 @@ "name": "Default" }, "id": "423f19a4ac1e4f48bbb4180756e6eb6c", - "name": "admin" + "name": "admin", + "password_expires_at": null }, "audit_ids": [ "ZzZwkUflQfygX7pdYDBCQQ" diff --git a/api-ref/source/v3/samples/admin/auth-token-scoped-response.json b/api-ref/source/v3/samples/admin/auth-token-scoped-response.json index dc7e33d029..afd3dd0642 100644 --- a/api-ref/source/v3/samples/admin/auth-token-scoped-response.json +++ b/api-ref/source/v3/samples/admin/auth-token-scoped-response.json @@ -392,7 +392,8 @@ "name": "Default" }, "id": "10a2e6e717a245d9acad3e5f97aeca3d", - "name": "admin" + "name": "admin", + "password_expires_at": "2016-11-06T15:32:17.000000" }, "audit_ids": [ "wLc7nDMsQiKqf8VFU4ySpg" diff --git a/api-ref/source/v3/samples/admin/auth-token-unscoped-response.json b/api-ref/source/v3/samples/admin/auth-token-unscoped-response.json index 3224a0e538..83b81791ff 100644 --- a/api-ref/source/v3/samples/admin/auth-token-unscoped-response.json +++ b/api-ref/source/v3/samples/admin/auth-token-unscoped-response.json @@ -11,7 +11,8 @@ "name": "Default" }, "id": "10a2e6e717a245d9acad3e5f97aeca3d", - "name": "admin" + "name": "admin", + "password_expires_at": null }, "audit_ids": [ "mAjXQhiYRyKwkB4qygdLVg" diff --git a/keystone/models/token_model.py b/keystone/models/token_model.py index 0bf02e56fd..6932365bb3 100644 --- a/keystone/models/token_model.py +++ b/keystone/models/token_model.py @@ -106,6 +106,15 @@ class KeystoneToken(dict): pass raise exception.UnexpectedError() + @property + def user_password_expires_at(self): + try: + return self['user']['password_expires_at'] + except KeyError: + # Do not raise KeyError, raise UnexpectedError + pass + raise exception.UnexpectedError() + @property def user_domain_id(self): try: diff --git a/keystone/tests/unit/test_token_provider.py b/keystone/tests/unit/test_token_provider.py index e1e468100d..8fcbf8a47e 100644 --- a/keystone/tests/unit/test_token_provider.py +++ b/keystone/tests/unit/test_token_provider.py @@ -335,7 +335,8 @@ SAMPLE_V3_TOKEN = { "name": "Default" }, "id": "f19ddbe2c53c46f189fe66d0a7a9c9ce", - "name": "nova" + "name": "nova", + "password_expires_at": "2013-08-21T00:02:43.941473Z" }, "OS-TRUST:trust": { "id": "abc123", diff --git a/keystone/tests/unit/test_v3.py b/keystone/tests/unit/test_v3.py index d6b7a734d5..f163f1ea55 100644 --- a/keystone/tests/unit/test_v3.py +++ b/keystone/tests/unit/test_v3.py @@ -161,7 +161,7 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, }, 'user': { 'type': 'object', - 'required': ['id', 'name', 'domain'], + 'required': ['id', 'name', 'domain', 'password_expires_at'], 'properties': { 'id': {'type': 'string'}, 'name': {'type': 'string'}, @@ -173,6 +173,10 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, }, 'required': ['id', 'name'], 'additonalProperties': False, + }, + 'password_expires_at': { + 'type': ['string', 'null'], + 'pattern': unit.TIME_FORMAT_REGEX, } }, 'additionalProperties': False, diff --git a/keystone/tests/unit/token/test_fernet_provider.py b/keystone/tests/unit/token/test_fernet_provider.py index d99330e456..83823f07a8 100644 --- a/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone/tests/unit/token/test_fernet_provider.py @@ -120,6 +120,7 @@ class TestValidate(unit.TestCase): 'id': domain_ref['id'], 'name': domain_ref['name'], }, + 'password_expires_at': user_ref['password_expires_at'] } self.assertEqual(exp_user_info, token['user']) diff --git a/keystone/tests/unit/token/test_token_model.py b/keystone/tests/unit/token/test_token_model.py index df7c8c3acf..3ddc7231c8 100644 --- a/keystone/tests/unit/token/test_token_model.py +++ b/keystone/tests/unit/token/test_token_model.py @@ -48,6 +48,9 @@ class TestKeystoneTokenModel(core.TestCase): token_data.user_id) self.assertEqual(self.v3_sample_token['token']['user']['name'], token_data.user_name) + self.assertEqual( + self.v3_sample_token['token']['user']['password_expires_at'], + token_data.user_password_expires_at) self.assertEqual(self.v3_sample_token['token']['user']['domain']['id'], token_data.user_domain_id) self.assertEqual( diff --git a/keystone/token/providers/common.py b/keystone/token/providers/common.py index 5fc5fd6d18..6166a13ce8 100644 --- a/keystone/token/providers/common.py +++ b/keystone/token/providers/common.py @@ -421,7 +421,8 @@ class V3TokenDataHelper(object): filtered_user = { 'id': user_ref['id'], 'name': user_ref['name'], - 'domain': self._get_filtered_domain(user_ref['domain_id'])} + 'domain': self._get_filtered_domain(user_ref['domain_id']), + 'password_expires_at': user_ref['password_expires_at']} token_data['user'] = filtered_user def _populate_oauth_section(self, token_data, access_token): diff --git a/releasenotes/notes/bp-password_expired_at-4b32fe7032595932.yaml b/releasenotes/notes/bp-password_expired_at-4b32fe7032595932.yaml new file mode 100644 index 0000000000..ae78a0745b --- /dev/null +++ b/releasenotes/notes/bp-password_expired_at-4b32fe7032595932.yaml @@ -0,0 +1,8 @@ +--- +features: + - > + Token responses will now have a ``password_expires_at`` + field under the ``user`` dictionary. + If PCI support is enabled, the ``password_expires_at`` + field will be populated. Otherwise, it will default + to ``null``.