Browse Source

Remove support for PKI and PKIz tokens

This is the first step of several to remove PKI token support in
keystone. A large issue in removing PKI support is support for the
revocation list must be maintained.

This patch removes support for the token format, it's surrounding tests
and examples that are generated. Additionally, some wording has been
changed around the CLI and config options to make the distinction
between keys and certs used for PKI tokens and those used for getting
the revocation list (a list of tokens that are revoked, which is signed).

Future patches will:

- Remove the keystone-manage commands for generating certs

- Modify the revocation list (at /auth/tokens/OS-PKI/revoked) to return
a 403 if pki is not configured (instead of raising a 500). We cannot
remove the API as that would break an API contract.

- Options to configure PKI will be marked as deprecated

- If PKI is configured a normal signed list will be returned (same
behavior as today)

- Follow up patch to keystonemiddleware will make sure auth_token does
not rely on the revocation api at all.

Related-Bug: 1626778
Related-Bug: 1626779

Co-Authored-By: Boris Bobrov <bbobrov@mirantis.com>
bp removed-as-of-ocata
Change-Id: Icf1ebced44a675c88fb66a6c0431208ff5181574
changes/79/374479/20
Steve Martinelli 5 years ago
committed by Lance Bragstad
parent
commit
8a66ef6354
  1. 31
      doc/source/configuration.rst
  2. 2
      doc/source/man/keystone-manage.rst
  3. 22
      doc/source/token-support-matrix.ini
  4. 85
      examples/pki/cms/auth_token_revoked.json
  5. 44
      examples/pki/cms/auth_token_revoked.pem
  6. 85
      examples/pki/cms/auth_token_scoped.json
  7. 44
      examples/pki/cms/auth_token_scoped.pem
  8. 23
      examples/pki/cms/auth_token_unscoped.json
  9. 19
      examples/pki/cms/auth_token_unscoped.pem
  10. 8
      examples/pki/cms/revocation_list.json
  11. 15
      examples/pki/cms/revocation_list.pem
  12. 233
      examples/pki/gen_pki.sh
  13. 2
      keystone/cmd/cli.py
  14. 6
      keystone/cmd/doctor/tokens.py
  15. 7
      keystone/common/utils.py
  16. 7
      keystone/conf/constants.py
  17. 12
      keystone/conf/default.py
  18. 30
      keystone/conf/token.py
  19. 6
      keystone/exception.py
  20. 4
      keystone/models/token_model.py
  21. 7
      keystone/tests/unit/common/test_utils.py
  22. 26
      keystone/tests/unit/test_auth.py
  23. 27
      keystone/tests/unit/test_backend_sql.py
  24. 28
      keystone/tests/unit/test_cert_setup.py
  25. 10
      keystone/tests/unit/test_revoke.py
  26. 50
      keystone/tests/unit/test_token_provider.py
  27. 85
      keystone/tests/unit/test_v2.py
  28. 164
      keystone/tests/unit/test_v3_auth.py
  29. 34
      keystone/tests/unit/token/test_backends.py
  30. 26
      keystone/tests/unit/token/test_pki_provider.py
  31. 26
      keystone/tests/unit/token/test_pkiz_provider.py
  32. 18
      keystone/token/persistence/core.py
  33. 6
      keystone/token/provider.py
  34. 67
      keystone/token/providers/pki.py
  35. 65
      keystone/token/providers/pkiz.py
  36. 31
      keystone/token/utils.py
  37. 2
      setup.cfg
  38. 2
      tox.ini

31
doc/source/configuration.rst

@ -437,8 +437,8 @@ configuring the following property.
:class:`keystone.token.providers.uuid.Provider`
UUID, PKI, PKIZ, or Fernet?
^^^^^^^^^^^^^^^^^^^^^^^^^^^
UUID or Fernet?
^^^^^^^^^^^^^^^
Each token format uses different technologies to achieve various performance,
scaling and architectural requirements.
@ -449,29 +449,6 @@ transport and are thus URL-friendly. They must be persisted by the identity
service in order to be later validated. Revoking them is simply a matter of
deleting them from the token persistence backend.
Both PKI and PKIZ tokens contain JSON payloads that represent the entire token
validation response that would normally be retrieved from keystone. The payload
is then signed using `Cryptographic Message Syntax (CMS)
<http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax>`_. The combination
of CMS and the exhaustive payload allows PKI and PKIZ tokens to be verified
offline using keystone's public signing key. The only reason for them to be
persisted by the identity service is to later build token revocation *lists*
(explicit lists of tokens that have been revoked), otherwise they are
theoretically ephemeral when supported by token revocation *events* (which
describe invalidated tokens rather than enumerate them). PKIZ tokens add zlib
compression after signing to achieve a smaller overall token size. To make them
URL-friendly, PKI tokens are base64 encoded and then arbitrarily manipulated to
replace unsafe characters with safe ones whereas PKIZ tokens use conventional
base64url encoding. Due to the size of the payload and the overhead incurred by
the CMS format, both PKI and PKIZ tokens may be too long to fit in either
headers or URLs if they contain extensive service catalogs or other additional
attributes. Some third-party applications such as web servers and clients may
need to be recompiled from source to customize the limitations that PKI and
PKIZ tokens would otherwise exceed). Both PKI and PKIZ tokens require signing
certificates which may be created using ``keystone-manage pki_setup`` for
demonstration purposes (this is not recommended for production deployments: use
certificates issued by an trusted CA instead).
Fernet tokens contain a limited amount of identity and authorization data in a
`MessagePacked <http://msgpack.org/>`_ payload. The payload is then wrapped as
a `Fernet <https://github.com/fernet/spec>`_ message for transport, where
@ -481,7 +458,7 @@ established using ``keystone-manage fernet_setup`` and periodically rotated
using ``keystone-manage fernet_rotate``.
.. WARNING::
UUID, PKI, PKIZ, and Fernet tokens are all bearer tokens, meaning that they
UUID and Fernet tokens are both bearer tokens, meaning that they
must be protected from unnecessary disclosure to prevent unauthorized
access.
@ -1338,7 +1315,7 @@ through the normal REST API. At the moment, the following calls are supported:
* ``mapping_engine``: Test your federation mapping rules.
* ``mapping_populate``: Prepare domain-specific LDAP backend
* ``mapping_purge``: Purge the identity mapping table.
* ``pki_setup``: Initialize the certificates used to sign tokens.
* ``pki_setup``: Initialize the certificates used to sign revocation lists.
* ``saml_idp_metadata``: Generate identity provider metadata.
* ``token_flush``: Purge expired tokens

2
doc/source/man/keystone-manage.rst

@ -53,7 +53,7 @@ Available commands:
* ``mapping_populate``: Prepare domain-specific LDAP backend.
* ``mapping_purge``: Purge the identity mapping table.
* ``mapping_engine``: Test your federation mapping rules.
* ``pki_setup``: Initialize the certificates used to sign tokens. **deprecated**
* ``pki_setup``: Initialize the certificates used to sign revocation lists. **deprecated**
* ``saml_idp_metadata``: Generate identity provider metadata.
* ``token_flush``: Purge expired tokens.

22
doc/source/token-support-matrix.ini

@ -56,8 +56,6 @@
# drivers should maintain their own equivalent document, and merge it with this
# when their code merges into core.
driver-impl-uuid=UUID tokens
driver-impl-pki=PKI tokens
driver-impl-pkiz=PKIZ tokens
driver-impl-fernet=Fernet tokens
[operation.create_unscoped_token]
@ -68,8 +66,6 @@ notes=All token providers must be capable of issuing tokens without an explicit
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
--os-password=<password> token issue
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[operation.create_project_scoped_token]
@ -80,8 +76,6 @@ cli=openstack --os-username=<username> --os-user-domain-name=<domain>
--os-password=<password> --os-project-name=<project>
--os-project-domain-name=<domain> token issue
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[operation.create_domain_scoped_token]
@ -92,8 +86,6 @@ notes=Domain-scoped tokens are not required for all use cases, and for some use
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
--os-password=<password> --os-domain-name=<domain> token issue
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[operation.create_trust_scoped_token]
@ -104,8 +96,6 @@ notes=Tokens scoped to a trust convey only the user impersonation and
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
--os-password=<password> --os-trust-id=<trust> token issue
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[operation.create_token_using_oauth]
@ -114,8 +104,6 @@ status=optional
notes=OAuth access tokens can be exchanged for keystone tokens.
cli=
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[operation.create_token_with_bind]
@ -125,8 +113,6 @@ notes=Tokens can express a binding to an additional authentication method, such
as kerberos or x509.
cli=
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=missing
[operation.revoke_token]
@ -138,8 +124,6 @@ notes=Tokens may be individually revoked, such as when a user logs out of
revoked token was previously used to create additional tokens).
cli=openstack token revoke
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[feature.online_validation]
@ -149,8 +133,6 @@ notes=Keystone must be able to validate the tokens that it issues when
presented with a token that it previously issued.
cli=
driver-impl-uuid=complete
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=complete
[feature.offline_validation]
@ -161,8 +143,6 @@ notes=Services using Keystone for authentication may want to validate tokens
performance and scalability.
cli=
driver-impl-uuid=missing
driver-impl-pki=complete
driver-impl-pkiz=complete
driver-impl-fernet=missing
[feature.non_persistent]
@ -174,6 +154,4 @@ notes=If a token format does not require persistence (such as to a SQL
operations such as `keystone-manage token_flush`.
cli=
driver-impl-uuid=missing
driver-impl-pki=partial
driver-impl-pkiz=partial
driver-impl-fernet=complete

85
examples/pki/cms/auth_token_revoked.json

@ -1,85 +0,0 @@
{
"access": {
"serviceCatalog": [
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"endpoints_links": [],
"type": "volume",
"name": "volume"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:9292/v1",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:9292/v1",
"publicURL": "http://127.0.0.1:9292/v1"
}
],
"endpoints_links": [],
"type": "image",
"name": "glance"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"endpoints_links": [],
"type": "compute",
"name": "nova"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:35357/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}
],
"endpoints_links": [],
"type": "identity",
"name": "keystone"
}
],
"token": {
"expires": "2012-06-02T14:47:34Z",
"id": "placeholder",
"tenant": {
"enabled": true,
"description": null,
"name": "tenant_name1",
"id": "tenant_id1"
}
},
"user": {
"username": "revoked_username1",
"roles_links": [
"role1",
"role2"
],
"id": "revoked_user_id1",
"roles": [
{
"name": "role1"
},
{
"name": "role2"
}
],
"name": "revoked_username1"
}
}
}

44
examples/pki/cms/auth_token_revoked.pem

@ -1,44 +0,0 @@
-----BEGIN CMS-----
MIIH1wYJKoZIhvcNAQcCoIIHyDCCB8QCAQExCTAHBgUrDgMCGjCCBeQGCSqGSIb3
DQEHAaCCBdUEggXReyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k
cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx
LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy
ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2
L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS
TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh
NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi
OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si
YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6
ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi
fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt
ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw
Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog
Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw
ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu
ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi
bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu
MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi
LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJyZXZv
a2VkX3VzZXJuYW1lMSIsICJyb2xlc19saW5rcyI6IFsicm9sZTEiLCJyb2xlMiJd
LCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAi
cm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLCAibmFtZSI6ICJyZXZva2VkX3Vz
ZXJuYW1lMSJ9fX0NCjGCAcowggHGAgEBMIGkMIGeMQowCAYDVQQFEwE1MQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTESMBAGA1UE
ChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMGCSqGSIb3DQEJARYW
a2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2VsZiBTaWduZWQCAREw
BwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAXY8JvllpyctcNlJByPLxhgLyRfFo
Ew+8Yq3O4FxOyfVkINvOz4EHTipY0M/K8OLwfxpRt7o/iGLGRDBTI6Dd+erXsus8
NecnNxcWN9RUE2CZhoGj/0nhnNEGF+9Mlv3tMBngwoUJg2paSw/Vn2Q7RaqbOC05
aZOSDoSX7Zf0DIS/T0ZPnmOUb9+N25M20ctMHksPMEq0qyf2oove0O+WMa/cA8JT
c2EAhew4WSD0Zv0GOAP30GS+hkNfA1GZTrvCQrpRs9jXhK4dR2bBsnUFVix1BEZ0
sDhI8cXLvm16IpOO8ov6002ZoZhPn6Qo+0J8QOfdnjiwNnxLOEbuOIwPeQ==
-----END CMS-----

85
examples/pki/cms/auth_token_scoped.json

@ -1,85 +0,0 @@
{
"access": {
"serviceCatalog": [
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"endpoints_links": [],
"type": "volume",
"name": "volume"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:9292/v1",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:9292/v1",
"publicURL": "http://127.0.0.1:9292/v1"
}
],
"endpoints_links": [],
"type": "image",
"name": "glance"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"
}
],
"endpoints_links": [],
"type": "compute",
"name": "nova"
},
{
"endpoints": [
{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:35357/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}
],
"endpoints_links": [],
"type": "identity",
"name": "keystone"
}
],
"token": {
"expires": "2012-06-02T14:47:34Z",
"id": "placeholder",
"tenant": {
"enabled": true,
"description": null,
"name": "tenant_name1",
"id": "tenant_id1"
}
},
"user": {
"username": "user_name1",
"roles_links": [
"role1",
"role2"
],
"id": "user_id1",
"roles": [
{
"name": "role1"
},
{
"name": "role2"
}
],
"name": "user_name1"
}
}
}

44
examples/pki/cms/auth_token_scoped.pem

@ -1,44 +0,0 @@
-----BEGIN CMS-----
MIIHwQYJKoZIhvcNAQcCoIIHsjCCB64CAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3
DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k
cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx
LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy
ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2
L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS
TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh
NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi
OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si
YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6
ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi
fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt
ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw
Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog
Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw
ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu
ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi
bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu
MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi
LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJ1c2Vy
X25hbWUxIiwgInJvbGVzX2xpbmtzIjogWyJyb2xlMSIsInJvbGUyIl0sICJpZCI6
ICJ1c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1l
IjogInJvbGUyIn1dLCAibmFtZSI6ICJ1c2VyX25hbWUxIn19fQ0KMYIByjCCAcYC
AQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTES
MBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsT
CEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3Jn
MRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkqhkiG9w0BAQEF
AASCAQCAtuVtqTU9h1uaRrYU1eusSnHwD6jizp/xltTrYTyFPfYjhJdglS+bjSeS
Iau9pN3Tfug98ozUTJ5ByNepAQtxBxPz5bDXhBmAbU6ywaolqRAG+b/s2ShNGQ2a
tn80NeZmDNbtoqdHVAkD3EZXjsEKr2w+3JTTF2indzczyGe5EeSfNUaT+ZhNEmPR
Urob62t8atW+zehCSurpaa8pC5m1NcbK8Uu6Y+qO2m08KU9w5kmbOQtWAGCmtpIx
F2yM1AbSgd90yzen7dv5mNkgZyzQ6SYgRUvkKOKnCyBb97EZK3ZR4qUxQzRYM++8
g8HdaIfoYVPoPHqODet8Xmhw/Wtp
-----END CMS-----

23
examples/pki/cms/auth_token_unscoped.json

@ -1,23 +0,0 @@
{
"access": {
"token": {
"expires": "2012-08-17T15:35:34Z",
"id": "01e032c996ef4406b144335915a41e79"
},
"serviceCatalog": {},
"user": {
"username": "user_name1",
"roles_links": [],
"id": "c9c89e3be3ee453fbf00c7966f6d3fbd",
"roles": [
{
"name": "role1"
},
{
"name": "role2"
}
],
"name": "user_name1"
}
}
}

19
examples/pki/cms/auth_token_unscoped.pem

@ -1,19 +0,0 @@
-----BEGIN CMS-----
MIIDKAYJKoZIhvcNAQcCoIIDGTCCAxUCAQExCTAHBgUrDgMCGjCCATUGCSqGSIb3
DQEHAaCCASYEggEieyJhY2Nlc3MiOiB7InRva2VuIjogeyJleHBpcmVzIjogIjIw
MTItMDgtMTdUMTU6MzU6MzRaIiwgImlkIjogIjAxZTAzMmM5OTZlZjQ0MDZiMTQ0
MzM1OTE1YTQxZTc5In0sICJzZXJ2aWNlQ2F0YWxvZyI6IHt9LCAidXNlciI6IHsi
dXNlcm5hbWUiOiAidXNlcl9uYW1lMSIsICJyb2xlc19saW5rcyI6IFtdLCAiaWQi
OiAiYzljODllM2JlM2VlNDUzZmJmMDBjNzk2NmY2ZDNmYmQiLCAicm9sZXMiOiBb
eyduYW1lJzogJ3JvbGUxJ30seyduYW1lJzogJ3JvbGUyJ30sXSwgIm5hbWUiOiAi
dXNlcl9uYW1lMSJ9fX0xggHKMIIBxgIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkG
A1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNV
BAoTCU9wZW5TdGFjazERMA8GA1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEW
FmtleXN0b25lQG9wZW5zdGFjay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgER
MAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIIBAFyD9IH2bXsafCTyHEWS28zBuq03
ZNWXV4+0BfdMbX1ONkaQ7mLGRmfabLHwfE5RaSASFh/Doq7KTc8XrBVfTm9HQPGr
TLZUawdYlyBFVq0PEE1cPvO9Blz4X/2Awcp/Q67YRd/oLCY2dFWMClMroXu1fy3P
oFlpWPPhURrbU1GjhUgPIz0IxNGjfWEHVsb5kz7Bo4E8J3pgIkccm97XZZtiCwf7
DVNj+Eb5mRegGG6IgSSRpZULgnCmSofQ3RnW3jSCkDxLXDQm9IsaaLJsuUFLylGs
mB/98w9mP192IGl5MVr8/tANXwb5ok2VatUp/Ww1U0IlWbhN374PbK76vcE=
-----END CMS-----

8
examples/pki/cms/revocation_list.json

@ -1,8 +0,0 @@
{
"revoked": [
{
"id": "7acfcfdaf6a14aebe97c61c5947bc4d3",
"expires": "2012-08-14T17:58:48Z"
}
]
}

15
examples/pki/cms/revocation_list.pem

@ -1,15 +0,0 @@
-----BEGIN CMS-----
MIICWgYJKoZIhvcNAQcCoIICSzCCAkcCAQExCTAHBgUrDgMCGjBpBgkqhkiG9w0B
BwGgXARaeyJyZXZva2VkIjpbeyJpZCI6IjdhY2ZjZmRhZjZhMTRhZWJlOTdjNjFj
NTk0N2JjNGQzIiwiZXhwaXJlcyI6IjIwMTItMDgtMTRUMTc6NTg6NDhaIn1dfQ0K
MYIByjCCAcYCAQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYD
VQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sx
ETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVu
c3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkq
hkiG9w0BAQEFAASCAQC2f05VHM7zjNT3TBO80AmZ00n7AEWUjbFe5nqIM8kWGM83
01Bi3uU/nQ0daAd3tqCmDL2EfETAjD+xnIzjlN6eIA74Vy51wFD/KiyWYPWzw8mH
WcATHmE4E8kLdt8NhUodCY9TCFxcHJNDR1Eai/U7hH+5O4p9HcmMjv/GWegZL6HB
Up9Cxu6haxvPFmYylzM6Qt0Ad/WiO/JZLPTA4qXJEJSa9EMFMb0c2wSDSn30swJe
7J79VTFktTr2djv8KFvaHr4vLFYv2Y3ZkTeHqam0m91vllxLZJUP5QTSHjjY6LFE
5eEjIlOv9wOOm1uTtPIq6pxCugU1Wm7gstkqr55R
-----END CMS-----

233
examples/pki/gen_pki.sh

@ -1,233 +0,0 @@
#!/bin/bash
# Copyright 2012 OpenStack Foundation
#
# 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.
# This script generates the crypto necessary for the SSL tests.
DIR=`dirname "$0"`
CURRENT_DIR=`cd "$DIR" && pwd`
CERTS_DIR=$CURRENT_DIR/certs
PRIVATE_DIR=$CURRENT_DIR/private
CMS_DIR=$CURRENT_DIR/cms
function rm_old {
rm -rf $CERTS_DIR/*.pem
rm -rf $PRIVATE_DIR/*.pem
}
function cleanup {
rm -rf *.conf > /dev/null 2>&1
rm -rf index* > /dev/null 2>&1
rm -rf *.crt > /dev/null 2>&1
rm -rf newcerts > /dev/null 2>&1
rm -rf *.pem > /dev/null 2>&1
rm -rf serial* > /dev/null 2>&1
}
function generate_ca_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = cakey.pem
default_md = default
prompt = no
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
[ ca_distinguished_name ]
serialNumber = 5
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
emailAddress = keystone@openstack.org
commonName = Self Signed
[ ca_extensions ]
basicConstraints = critical,CA:true
' > ca.conf
}
function generate_ssl_req_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = keystonekey.pem
default_md = default
prompt = no
distinguished_name = distinguished_name
[ distinguished_name ]
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
commonName = localhost
emailAddress = keystone@openstack.org
' > ssl_req.conf
}
function generate_cms_signing_req_conf {
echo '
[ req ]
default_bits = 2048
default_keyfile = keystonekey.pem
default_md = default
prompt = no
distinguished_name = distinguished_name
[ distinguished_name ]
countryName = US
stateOrProvinceName = CA
localityName = Sunnyvale
organizationName = OpenStack
organizationalUnitName = Keystone
commonName = Keystone
emailAddress = keystone@openstack.org
' > cms_signing_req.conf
}
function generate_signing_conf {
echo '
[ ca ]
default_ca = signing_ca
[ signing_ca ]
dir = .
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/certs/cacert.pem
serial = $dir/serial
private_key = $dir/private/cakey.pem
default_days = 21360
default_crl_days = 30
default_md = default
policy = policy_any
[ policy_any ]
countryName = supplied
stateOrProvinceName = supplied
localityName = optional
organizationName = supplied
organizationalUnitName = supplied
emailAddress = supplied
commonName = supplied
' > signing.conf
}
function setup {
touch index.txt
echo '10' > serial
generate_ca_conf
mkdir newcerts
}
function check_error {
if [ $1 != 0 ] ; then
echo "Failed! rc=${1}"
echo 'Bailing ...'
cleanup
exit $1
else
echo 'Done'
fi
}
function generate_ca {
echo 'Generating New CA Certificate ...'
openssl req -x509 -newkey rsa:2048 -days 21360 -out $CERTS_DIR/cacert.pem \
-keyout $PRIVATE_DIR/cakey.pem -outform PEM -config ca.conf -nodes
check_error $?
}
function ssl_cert_req {
echo 'Generating SSL Certificate Request ...'
generate_ssl_req_conf
openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/ssl_key.pem \
-keyform PEM -out ssl_req.pem -outform PEM -config ssl_req.conf -nodes
check_error $?
#openssl req -in req.pem -text -noout
}
function cms_signing_cert_req {
echo 'Generating CMS Signing Certificate Request ...'
generate_cms_signing_req_conf
openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/signing_key.pem \
-keyform PEM -out cms_signing_req.pem -outform PEM \
-config cms_signing_req.conf -nodes
check_error $?
#openssl req -in req.pem -text -noout
}
function issue_certs {
generate_signing_conf
echo 'Issuing SSL Certificate ...'
openssl ca -in ssl_req.pem -config signing.conf -batch
check_error $?
openssl x509 -in $CURRENT_DIR/newcerts/10.pem -out $CERTS_DIR/ssl_cert.pem
check_error $?
echo 'Issuing CMS Signing Certificate ...'
openssl ca -in cms_signing_req.pem -config signing.conf -batch
check_error $?
openssl x509 -in $CURRENT_DIR/newcerts/11.pem \
-out $CERTS_DIR/signing_cert.pem
check_error $?
}
function create_middleware_cert {
cp $CERTS_DIR/ssl_cert.pem $CERTS_DIR/middleware.pem
cat $PRIVATE_DIR/ssl_key.pem >> $CERTS_DIR/middleware.pem
}
function check_openssl {
echo 'Checking openssl availability ...'
which openssl
check_error $?
}
function gen_sample_cms {
FILES="${CMS_DIR}/auth_token_revoked.json"
FILES+=" ${CMS_DIR}/auth_token_unscoped.json"
FILES+=" ${CMS_DIR}/auth_token_scoped.json"
FILES+=" ${CMS_DIR}/revocation_list.json"
for json_file in $FILES; do
openssl cms -sign -in $json_file -nosmimecap \
-signer $CERTS_DIR/signing_cert.pem \
-inkey $PRIVATE_DIR/signing_key.pem -outform PEM -nodetach \
-nocerts -noattr -out ${json_file/.json/.pem}
done
}
check_openssl
rm_old
cleanup
setup
generate_ca
ssl_cert_req
cms_signing_cert_req
issue_certs
create_middleware_cert
gen_sample_cms
cleanup

2
keystone/cmd/cli.py

@ -518,7 +518,7 @@ class BaseCertificateSetup(BasePermissionsSetup):
class PKISetup(BaseCertificateSetup):
"""Set up Key pairs and certificates for token signing and verification.
"""Setup keys and certificates for signing and verifying revocation lists.
This is NOT intended for production use, see Keystone Configuration
documentation for details. As of the Mitaka release, this command has

6
keystone/cmd/doctor/tokens.py

@ -28,10 +28,6 @@ def symptom_unreasonable_max_token_size():
- For UUID, set `keystone.conf [DEFAULT] max_token_size = 32`, because UUID
tokens are always exactly 32 characters.
- For PKI and PKIZ, set `keystone.conf [DEFAULT] max_token_size = 8192`,
because PKI and PKIZ tokens can be quite large, but any larger than 8192
and they tend to break certain implementations of HTTP.
- For Fernet, set `keystone.conf [DEFAULT] max_token_size = 255`, because
Fernet tokens should never exceed this length in most deployments.
However, if you are also using `keystone.conf [identity] driver = ldap`,
@ -41,6 +37,4 @@ def symptom_unreasonable_max_token_size():
"""
return (
'uuid' in CONF.token.provider and CONF.max_token_size != 32
or 'pki' in CONF.token.provider and CONF.max_token_size < 8192
or 'pkiz' in CONF.token.provider and CONF.max_token_size < 8192
or 'fernet' in CONF.token.provider and CONF.max_token_size > 255)

7
keystone/common/utils.py

@ -95,13 +95,6 @@ class SmarterEncoder(jsonutils.json.JSONEncoder):
return super(SmarterEncoder, self).default(obj)
class PKIEncoder(SmarterEncoder):
"""Special encoder to make token JSON a bit shorter."""
item_separator = ','
key_separator = ':'
def verify_length_and_trunc_password(password):
"""Verify and truncate the provided password to the max_password_length."""
max_length = CONF.identity.max_password_length

7
keystone/conf/constants.py

@ -16,15 +16,8 @@ package.
"""
from keystone.conf import utils
_DEFAULT_AUTH_METHODS = ['external', 'password', 'token', 'oauth1']
_CERTFILE = '/etc/keystone/ssl/certs/signing_cert.pem'
_KEYFILE = '/etc/keystone/ssl/private/signing_key.pem'
_DEPRECATE_PKI_MSG = utils.fmt("""
PKI token support has been deprecated in the M release and will be removed in
the O release. Fernet or UUID tokens are recommended.
""")

12
keystone/conf/default.py

@ -82,16 +82,16 @@ max_param_size = cfg.IntOpt(
Limit the sizes of user & project ID/names.
"""))
# we allow tokens to be a bit larger to accommodate PKI
# NOTE(breton): 255 is the size of the database columns used for ID fields.
# This size is picked so that the tokens can be indexed in-place as opposed to
# being entries in a string table. Thus, this is a performance decision.
max_token_size = cfg.IntOpt(
'max_token_size',
default=8192,
default=255,
help=utils.fmt("""
Similar to `[DEFAULT] max_param_size`, but provides an exception for token
values. With PKI / PKIZ tokens, this needs to be set close to 8192 (any higher,
and other HTTP implementations may break), depending on the size of your
service catalog and other factors. With Fernet tokens, this can be set as low
as 255. With UUID tokens, this should be set to 32).
values. With Fernet tokens, this can be set as low as 255. With UUID tokens,
this should be set to 32).
"""))
member_role_id = cfg.StrOpt(

30
keystone/conf/token.py

@ -10,13 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import sys
from oslo_config import cfg
from oslo_log import versionutils
from keystone.conf import constants
from keystone.conf import utils
@ -66,14 +63,12 @@ provider = cfg.StrOpt(
help=utils.fmt("""
Entry point for the token provider in the `keystone.token.provider` namespace.
The token provider controls the token construction, validation, and revocation
operations. Keystone includes `fernet`, `pkiz`, `pki`, and `uuid` token
operations. Keystone includes `fernet` and `uuid` token
providers. `uuid` tokens must be persisted (using the backend specified in the
`[token] driver` option), but do not require any extra configuration or setup.
`fernet` tokens do not need to be persisted at all, but require that you run
`keystone-manage fernet_setup` (also see the `keystone-manage fernet_rotate`
command). `pki` and `pkiz` tokens can be validated offline, without making HTTP
calls to keystone, but require that certificates be installed and distributed
to facilitate signing tokens and later validating those signatures.
command).
"""))
driver = cfg.StrOpt(
@ -128,26 +123,6 @@ for tokens with a more specific scope) or to provide their credentials in every
request for a scoped token to avoid re-scoping altogether.
"""))
# This attribute only exists in Python 2.7.8+ or 3.2+
hash_choices = getattr(hashlib, 'algorithms_guaranteed', None)
hash_choices = sorted(hash_choices) if hash_choices else None
hash_algorithm = cfg.StrOpt(
'hash_algorithm',
default='md5',
choices=hash_choices,
deprecated_for_removal=True,
deprecated_reason=constants._DEPRECATE_PKI_MSG,
deprecated_since=versionutils.deprecated.MITAKA,
help=utils.fmt("""
This controls the hash algorithm to use to uniquely identify PKI tokens without
having to transmit the entire token to keystone (which may be several
kilobytes). This can be set to any algorithm that hashlib supports. WARNING:
Before changing this value, the `auth_token` middleware protecting all other
services must be configured with the set of hash algorithms to expect from
keystone (both your old and new value for this option), otherwise token
revocation will not be processed correctly.
"""))
infer_roles = cfg.BoolOpt(
'infer_roles',
default=True,
@ -176,7 +151,6 @@ ALL_OPTS = [
cache_time,
revoke_by_id,
allow_rescope_scoped_token,
hash_algorithm,
infer_roles,
cache_on_issue,
]

6
keystone/exception.py

@ -176,12 +176,6 @@ class RegionDeletionError(ForbiddenNotSecurity):
"its child regions have associated endpoints.")
class PKITokenExpected(ForbiddenNotSecurity):
message_format = _('The certificates you requested are not available. '
'It is likely that this server does not use PKI tokens '
'otherwise this is the result of misconfiguration.')
class SecurityError(Error):
"""Security error exception.

4
keystone/models/token_model.py

@ -12,7 +12,6 @@
"""Unified in-memory token model."""
from keystoneclient.common import cms
from oslo_utils import reflection
from oslo_utils import timeutils
import six
@ -51,8 +50,7 @@ class KeystoneToken(dict):
super(KeystoneToken, self).__init__(**token_data['token'])
except KeyError:
raise exception.UnsupportedTokenVersionException()
self.short_id = cms.cms_hash_token(token_id,
mode=CONF.token.hash_algorithm)
self.token_id = token_id
if self.project_scoped and self.domain_scoped:
raise exception.UnexpectedError(_('Found invalid token: scoped to '

7
keystone/tests/unit/common/test_utils.py

@ -18,7 +18,6 @@ import uuid
import freezegun
from oslo_config import fixture as config_fixture
from oslo_log import log
from oslo_serialization import jsonutils
import six
from keystone.common import fernet_utils
@ -196,12 +195,6 @@ class UtilsTestCase(unit.BaseTestCase):
TZ = 'UTC' + d
_test_unixtime()
def test_pki_encoder(self):
data = {'field': 'value'}
json = jsonutils.dumps(data, cls=common_utils.PKIEncoder)
expected_json = '{"field":"value"}'
self.assertEqual(expected_json, json)
def test_url_safe_check(self):
base_str = 'i am safe'
self.assertFalse(common_utils.is_not_url_safe(base_str))

26
keystone/tests/unit/test_auth.py

@ -664,18 +664,6 @@ class FernetAuthWithToken(AuthWithToken, AuthTest):
self.skip_test_overrides('Fernet with v2.0 and revocation is broken')
class PKIAuthWithToken(AuthWithToken, AuthTest):
def config_overrides(self):
super(PKIAuthWithToken, self).config_overrides()
self.config_fixture.config(group='token', provider='pki')
class PKIZAuthWithToken(AuthWithToken, AuthTest):
def config_overrides(self):
super(PKIZAuthWithToken, self).config_overrides()
self.config_fixture.config(group='token', provider='pkiz')
class AuthWithPasswordCredentials(AuthTest):
def test_auth_invalid_user(self):
"""Verify exception is raised if invalid user."""
@ -880,20 +868,6 @@ class UUIDAuthWithRemoteUser(AuthWithRemoteUser, AuthTest):
self.config_fixture.config(group='token', provider='uuid')
class PKIAuthWithRemoteUser(AuthWithRemoteUser, AuthTest):
def config_overrides(self):
super(PKIAuthWithRemoteUser, self).config_overrides()
self.config_fixture.config(group='token', provider='pki')
class PKIZAuthWithRemoteUser(AuthWithRemoteUser, AuthTest):
def config_overrides(self):
super(PKIZAuthWithRemoteUser, self).config_overrides()
self.config_fixture.config(group='token', provider='pkiz')
class AuthWithTrust(object):
def setUp(self):
super(AuthWithTrust, self).setUp()

27
keystone/tests/unit/test_backend_sql.py

@ -854,33 +854,6 @@ class SqlTokenCacheInvalidationWithUUID(SqlTests,
self.config_fixture.config(group='token', provider='uuid')
class SqlTokenCacheInvalidationWithPKI(SqlTests,
token_tests.TokenCacheInvalidation):
def setUp(self):
super(SqlTokenCacheInvalidationWithPKI, self).setUp()
self._create_test_data()
def config_overrides(self):
super(SqlTokenCacheInvalidationWithPKI, self).config_overrides()
# NOTE(lbragstad): The TokenCacheInvalidation tests are coded to work
# against a persistent token backend. Only run these with token
# providers that issue persistent tokens.
self.config_fixture.config(group='token', provider='pki')
class SqlTokenCacheInvalidationWithPKIZ(SqlTests,
token_tests.TokenCacheInvalidation):
def setUp(self):
super(SqlTokenCacheInvalidationWithPKIZ, self).setUp()
self._create_test_data()
def config_overrides(self):
super(SqlTokenCacheInvalidationWithPKIZ, self).config_overrides()
# NOTE(lbragstad): The TokenCacheInvalidation tests are coded to work
# against a persistent token backend. Only run these with token
# providers that issue persistent tokens.
self.config_fixture.config(group='token', provider='pkiz')
# NOTE(lbragstad): The Fernet token provider doesn't persist tokens in a
# backend, so running the TokenCacheInvalidation tests here doesn't make sense.

28
keystone/tests/unit/test_cert_setup.py

@ -22,10 +22,9 @@ from six.moves import http_client
from testtools import matchers
from keystone.common import openssl
from keystone import exception
from keystone.tests import unit
from keystone.tests.unit import ksfixtures
from keystone.tests.unit import rest
from keystone import token
SSLDIR = unit.dirs.tmp('ssl')
@ -60,23 +59,14 @@ class CertSetupTestCase(rest.RestfulTestCase):
ca_certs=ca_certs,
ca_key=ca_key,
keyfile=os.path.join(KEYDIR, 'signing_key.pem'))
self.config_fixture.config(group='token', provider='pkiz')
def test_can_handle_missing_certs(self):
controller = token.controllers.Auth()
self.config_fixture.config(group='signing', certfile='invalid')
user = unit.create_user(self.identity_api,
domain_id=CONF.identity.default_domain_id)
body_dict = {
'passwordCredentials': {
'userId': user['id'],
'password': user['password'],
},
}
self.assertRaises(exception.UnexpectedError,
controller.authenticate,
self.make_request(), body_dict)
self.config_fixture.config(group='token', provider='fernet')
self.useFixture(
ksfixtures.KeyRepository(
self.config_fixture,
'fernet_tokens',
CONF.fernet_tokens.max_active_keys
)
)
def test_create_pki_certs(self, rebuild=False):
pki = openssl.ConfigurePKI(None, None, rebuild=rebuild)

10
keystone/tests/unit/test_revoke.py

@ -23,7 +23,6 @@ from keystone import exception
from keystone.models import revoke_model
from keystone.revoke.backends import sql
from keystone.tests import unit
from keystone.tests.unit import test_backend_sql
from keystone.token.providers import common
@ -367,15 +366,6 @@ class RevokeTests(object):
token_values)
class SqlRevokeTests(test_backend_sql.SqlTests, RevokeTests):
def config_overrides(self):
super(SqlRevokeTests, self).config_overrides()
self.config_fixture.config(
group='token',
provider='pki',
revoke_by_id=False)
def add_event(events, event):
events.append(event)
return event

50
keystone/tests/unit/test_token_provider.py

@ -15,7 +15,6 @@
import datetime
from oslo_utils import timeutils
from six.moves import reload_module
from keystone.common import dependency
from keystone.common import utils
@ -26,8 +25,6 @@ from keystone.tests.unit import ksfixtures
from keystone.tests.unit.ksfixtures import database
from keystone import token
from keystone.token.providers import fernet
from keystone.token.providers import pki
from keystone.token.providers import pkiz
from keystone.token.providers import uuid
@ -759,14 +756,6 @@ class TestTokenProvider(unit.TestCase):
self.config_fixture.config(group='token', provider='uuid')
self.assertIsInstance(token.provider.Manager().driver, uuid.Provider)
dependency.reset()
self.config_fixture.config(group='token', provider='pki')
self.assertIsInstance(token.provider.Manager().driver, pki.Provider)
dependency.reset()
self.config_fixture.config(group='token', provider='pkiz')
self.assertIsInstance(token.provider.Manager().driver, pkiz.Provider)
dependency.reset()
self.config_fixture.config(group='token', provider='fernet')
self.assertIsInstance(token.provider.Manager().driver, fernet.Provider)
@ -797,42 +786,3 @@ class TestTokenProvider(unit.TestCase):
exception.TokenNotFound,
self.token_provider_api.validate_token,
None)
# NOTE(ayoung): renamed to avoid automatic test detection
class PKIProviderTests(object):
def setUp(self):
super(PKIProviderTests, self).setUp()
from keystoneclient.common import cms
self.cms = cms
old_cms_subprocess = cms.subprocess
self.addCleanup(setattr, cms, 'subprocess', old_cms_subprocess)
self.cms.subprocess = self.target_subprocess
# force module reload so the imports get re-evaluated
reload_module(pki)
def test_get_token_id_error_handling(self):
# cause command-line failure
self.config_fixture.config(group='signing',
keyfile='--please-break-me')
provider = pki.Provider()
token_data = {}
self.assertRaises(exception.UnexpectedError,
provider._get_token_id,
token_data)
class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase):
def setUp(self):
# force keystoneclient.common.cms to use the stdlib subprocess
import subprocess
self.target_subprocess = subprocess
super(TestPKIProviderWithStdlib, self).setUp()

85
keystone/tests/unit/test_v2.py

@ -12,14 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import time
import uuid
from keystoneclient.common import cms
from oslo_serialization import jsonutils
import six
from six.moves import http_client
from testtools import matchers
from keystone.common import extension as keystone_extension
import keystone.conf
@ -1226,68 +1222,6 @@ class V2TestCase(object):
def assertValidRevocationListResponse(self, response):
self.assertIsNotNone(response.result['signed'])
def _fetch_parse_revocation_list(self):
token1 = self.get_scoped_token()
# TODO(morganfainberg): Because this is making a restful call to the
# app a change to UTCNOW via mock.patch will not affect the returned
# token. The only surefire way to ensure there is not a transient bug
# based upon when the second token is issued is with a sleep. This
# issue all stems from the limited resolution (no microseconds) on the
# expiry time of tokens and the way revocation events utilizes token
# expiry to revoke individual tokens. This is a stop-gap until all
# associated issues with resolution on expiration and revocation events
# are resolved.
time.sleep(1)
token2 = self.get_scoped_token()
self.admin_request(method='DELETE',
path='/v2.0/tokens/%s' % token2,
token=token1)
r = self.admin_request(
method='GET',
path='/v2.0/tokens/revoked',
token=token1,
expected_status=http_client.OK)
signed_text = r.result['signed']
data_json = cms.cms_verify(signed_text, CONF.signing.certfile,
CONF.signing.ca_certs)
data = jsonutils.loads(data_json)
return (data, token2)
def test_fetch_revocation_list_md5(self):
"""Hash for tokens in revocation list and server config should match.
If the server is configured for md5, then the revocation list has
tokens hashed with MD5.
"""
# The default hash algorithm is md5.
hash_algorithm = 'md5'
(data, token) = self._fetch_parse_revocation_list()
token_hash = cms.cms_hash_token(token, mode=hash_algorithm)
self.assertThat(token_hash, matchers.Equals(data['revoked'][0]['id']))
def test_fetch_revocation_list_sha256(self):
"""Hash for tokens in revocation list and server config should match.
If the server is configured for sha256, then the revocation list has
tokens hashed with SHA256.
"""
hash_algorithm = 'sha256'
self.config_fixture.config(group='token',
hash_algorithm=hash_algorithm)
(data, token) = self._fetch_parse_revocation_list()
token_hash = cms.cms_hash_token(token, mode=hash_algorithm)
self.assertThat(token_hash, matchers.Equals(data['revoked'][0]['id']))
def test_create_update_user_invalid_enabled_type(self):
# Enforce usage of boolean for 'enabled' field
token = self.get_scoped_token()
@ -1467,25 +1401,6 @@ class V2TestCaseFernet(V2TestCase, RestfulTestCase, CoreApiTests,
self.skipTest('Revocation lists do not support Fernet')
class RevokeApiTestCase(V2TestCase, RestfulTestCase, CoreApiTests,
LegacyV2UsernameTests):
def config_overrides(self):
super(RevokeApiTestCase, self).config_overrides()
self.config_fixture.config(
group='token',
provider='pki',
revoke_by_id=False)
def test_fetch_revocation_list_admin_200(self):
self.skip_test_overrides('Revoke API disables revocation_list.')
def test_fetch_revocation_list_md5(self):
self.skip_test_overrides('Revoke API disables revocation_list.')
def test_fetch_revocation_list_sha256(self):
self.skip_test_overrides('Revoke API disables revocation_list.')
class TestFernetTokenProviderV2(RestfulTestCase):
def setUp(self):

164
keystone/tests/unit/test_v3_auth.py

@ -1409,8 +1409,6 @@ class TokenAPITests(object):
v3_token_data['token']['expires_at'])
def test_v3_v2_token_intermix(self):
# FIXME(gyee): PKI tokens are not interchangeable because token
# data is baked into the token itself.
r = self.v3_create_token(self.build_authentication_request(
user_id=self.default_domain_user['id'],
password=self.default_domain_user['password'],
@ -2426,74 +2424,6 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase):
expected_status=http_client.FORBIDDEN)
class TestPKITokenAPIs(test_v3.RestfulTestCase, TokenAPITests, TokenDataTests):
def config_overrides(self):
super(TestPKITokenAPIs, self).config_overrides()
self.config_fixture.config(group='token', provider='pki')
def setUp(self):
super(TestPKITokenAPIs, self).setUp()
self.doSetUp()
def verify_token(self, *args, **kwargs):
return cms.verify_token(*args, **kwargs)
def test_v3_token_id(self):
auth_data = self.build_authentication_request(
user_id=self.user['id'],
password=self.user['password'])
resp = self.v3_create_token(auth_data)
token_data = resp.result
token_id = resp.headers.get('X-Subject-Token')
self.assertIn('expires_at', token_data['token'])
decoded_token = self.verify_token(token_id, CONF.signing.certfile,
CONF.signing.ca_certs)
decoded_token_dict = json.loads(decoded_token)
token_resp_dict = json.loads(resp.body)
self.assertEqual(decoded_token_dict, token_resp_dict)
# should be able to validate hash PKI token as well
hash_token_id = cms.cms_hash_token(token_id)
headers = {'X-Subject-Token': hash_token_id}
resp = self.get('/auth/tokens', headers=headers)
expected_token_data = resp.result
self.assertDictEqual(expected_token_data, token_data)
def test_v3_v2_hashed_pki_token_intermix(self):
auth_data = self.build_authentication_request(
user_id=self.default_domain_user['id'],
password=self.default_domain_user['password'],
project_id=self.default_domain_project['id'])
resp = self.v3_create_token(auth_data)
token_data = resp.result
token = resp.headers.get('X-Subject-Token')
# should be able to validate a hash PKI token in v2 too
token = cms.cms_hash_token(token)
path = '/v2.0/tokens/%s' % (token)
resp = self.admin_request(path=path,
token=self.get_admin_token(),
method='GET')
v2_token = resp.result
self.assertEqual(v2_token['access']['user']['id'],
token_data['token']['user']['id'])
self.assertTimestampEqual(v2_token['access']['token']['expires'],
token_data['token']['expires_at'])
self.assertEqual(v2_token['access']['user']['roles'][0]['name'],
token_data['token']['roles'][0]['name'])
class TestPKIZTokenAPIs(TestPKITokenAPIs):
def config_overrides(self):
super(TestPKIZTokenAPIs, self).config_overrides()
self.config_fixture.config(group='token', provider='pkiz')
def verify_token(self, *args, **kwargs):
return cms.pkiz_verify(*args, **kwargs)
class TestUUIDTokenAPIs(test_v3.RestfulTestCase, TokenAPITests,
TokenDataTests):
def config_overrides(self):
@ -2510,9 +2440,7 @@ class TestUUIDTokenAPIs(test_v3.RestfulTestCase, TokenAPITests,
password=self.user['password'])
resp = self.v3_create_token(auth_data)
token_data = resp.result
token_id = resp.headers.get('X-Subject-Token')
self.assertIn('expires_at', token_data['token'])
self.assertFalse(cms.is_asn1_token(token_id))
class TestFernetTokenAPIs(test_v3.RestfulTestCase, TokenAPITests,
@ -2761,8 +2689,15 @@ class TestTokenRevokeById(test_v3.RestfulTestCase):
super(TestTokenRevokeById, self).config_overrides()
self.config_fixture.config(
group='token',
provider='pki',
provider='fernet',
revoke_by_id=False)
self.useFixture(
ksfixtures.KeyRepository(
self.config_fixture,
'fernet_tokens',
CONF.fernet_tokens.max_active_keys
)
)
def setUp(self):
"""Setup for Token Revoking Test Cases.
@ -3507,8 +3442,15 @@ class TestTokenRevokeApi(TestTokenRevokeById):
super(TestTokenRevokeApi, self).config_overrides()
self.config_fixture.config(
group='token',
provider='pki',
provider='fernet',
revoke_by_id=False)
self.useFixture(
ksfixtures.KeyRepository(
self.config_fixture,
'fernet_tokens',
CONF.fernet_tokens.max_active_keys
)
)
def assertValidDeletedProjectResponse(self, events_response, project_id):
events = events_response['events']
@ -3772,24 +3714,6 @@ class TestAuthExternalDomainBehaviorWithUUID(AuthExternalDomainBehavior,
self.config_fixture.config(group='token', provider='uuid')
class TestAuthExternalDomainBehaviorWithPKI(AuthExternalDomainBehavior,
test_v3.RestfulTestCase):
def config_overrides(self):
super(TestAuthExternalDomainBehaviorWithPKI, self).config_overrides()
self.kerberos = False
self.auth_plugin_config_override(external='Domain')
self.config_fixture.config(group='token', provider='pki')
class TestAuthExternalDomainBehaviorWithPKIZ(AuthExternalDomainBehavior,
test_v3.RestfulTestCase):
def config_overrides(self):
super(TestAuthExternalDomainBehaviorWithPKIZ, self).config_overrides()
self.kerberos = False
self.auth_plugin_config_override(external='Domain')
self.config_fixture.config(group='token', provider='pkiz')
# NOTE(lbragstad): The Fernet token provider doesn't support bind
# authentication so we don't inhereit TestAuthExternalDomain here to test it.
@ -3858,22 +3782,6 @@ class UUIDAuthExternalDefaultDomain(TestAuthExternalDefaultDomain,
self.config_fixture.config(group='token', provider='uuid')
class PKIAuthExternalDefaultDomain(TestAuthExternalDefaultDomain,
test_v3.RestfulTestCase):
def config_overrides(self):
super(PKIAuthExternalDefaultDomain, self).config_overrides()
self.config_fixture.config(group='token', provider='pki')
class PKIZAuthExternalDefaultDomain(TestAuthExternalDefaultDomain,