diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-02-26 06:12:53 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-02-26 06:12:53 +0000 |
commit | ccee49acfee0e276d731029830b1db010a153550 (patch) | |
tree | e3112741e4d583579e569af52a92dfeeeac7e926 | |
parent | 8c8f776b6d38c8c9d5966ab02acaa763af7ab9b3 (diff) | |
parent | ae4626a857b3fec39a7a538f0ddbfad22cde5e34 (diff) | |
download | keystone-ccee49acfee0e276d731029830b1db010a153550.tar.gz |
Merge "Remove duplicated cms file"
-rw-r--r-- | keystone/auth/controllers.py | 2 | ||||
-rw-r--r-- | keystone/common/cms.py | 173 | ||||
-rw-r--r-- | keystone/tests/test_token_provider.py | 54 | ||||
-rw-r--r-- | keystone/tests/test_v3_auth.py | 3 | ||||
-rw-r--r-- | keystone/token/controllers.py | 2 | ||||
-rw-r--r-- | keystone/token/core.py | 2 | ||||
-rw-r--r-- | keystone/token/providers/pki.py | 3 |
7 files changed, 61 insertions, 178 deletions
diff --git a/keystone/auth/controllers.py b/keystone/auth/controllers.py index b9771c975..0fc44451c 100644 --- a/keystone/auth/controllers.py +++ b/keystone/auth/controllers.py @@ -14,9 +14,9 @@ import json +from keystoneclient.common import cms import six -from keystone.common import cms from keystone.common import controller from keystone.common import dependency from keystone.common import wsgi diff --git a/keystone/common/cms.py b/keystone/common/cms.py deleted file mode 100644 index 917de3d86..000000000 --- a/keystone/common/cms.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2013 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. - -import hashlib - -from keystone.common import environment -from keystone.openstack.common import log - - -LOG = log.getLogger(__name__) -PKI_ANS1_PREFIX = 'MII' - - -def cms_verify(formatted, signing_cert_file_name, ca_file_name): - """Verifies the signature of the contents IAW CMS syntax.""" - process = environment.subprocess.Popen(["openssl", "cms", "-verify", - "-certfile", - signing_cert_file_name, - "-CAfile", ca_file_name, - "-inform", "PEM", - "-nosmimecap", "-nodetach", - "-nocerts", "-noattr"], - stdin=environment.subprocess.PIPE, - stdout=environment.subprocess.PIPE, - stderr=environment.subprocess.PIPE) - output, err = process.communicate(formatted) - retcode = process.poll() - if retcode: - LOG.error(_('Verify error: %s'), err) - raise environment.subprocess.CalledProcessError(retcode, - "openssl", output=err) - return output - - -def token_to_cms(signed_text): - copy_of_text = signed_text.replace('-', '/') - - formatted = "-----BEGIN CMS-----\n" - line_length = 64 - while len(copy_of_text) > 0: - if (len(copy_of_text) > line_length): - formatted += copy_of_text[:line_length] - copy_of_text = copy_of_text[line_length:] - else: - formatted += copy_of_text - copy_of_text = "" - formatted += "\n" - - formatted += "-----END CMS-----\n" - - return formatted - - -def verify_token(token, signing_cert_file_name, ca_file_name): - return cms_verify(token_to_cms(token), - signing_cert_file_name, - ca_file_name) - - -def is_ans1_token(token): - """Determine if a token appears to be PKI-based. - - thx to ayoung for sorting this out. - - | base64 decoded hex representation of MII is 3082 - | In [3]: binascii.hexlify(base64.b64decode('MII=')) - | Out[3]: '3082' - - re: http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf - - | pg4: For tags from 0 to 30 the first octet is the identifier - | pg10: Hex 30 means sequence, followed by the length of that sequence. - | pg5: Second octet is the length octet - | first bit indicates short or long form, next 7 bits encode the - number of subsequent octets that make up the content length octets - as an unsigned binary int - | - | 82 = 10000010 (first bit indicates long form) - | 0000010 = 2 octets of content length - | so read the next 2 octets to get the length of the content. - - In the case of a very large content length there could be a requirement to - have more than 2 octets to designate the content length, therefore - requiring us to check for MIM, MIQ, etc. - - | In [4]: base64.b64encode(binascii.a2b_hex('3083')) - | Out[4]: 'MIM=' - | In [5]: base64.b64encode(binascii.a2b_hex('3084')) - | Out[5]: 'MIQ=' - | Checking for MI would become invalid at 16 octets of content length - | 10010000 = 90 - | In [6]: base64.b64encode(binascii.a2b_hex('3090')) - | Out[6]: 'MJA=' - | Checking for just M is insufficient - - But we will only check for MII: - Max length of the content using 2 octets is 7FFF or 32767. - It's not practical to support a token of this length or greater in http; - therefore, we will check for MII only and ignore the case of larger tokens - """ - return token[:3] == PKI_ANS1_PREFIX - - -def cms_sign_text(text, signing_cert_file_name, signing_key_file_name): - """Uses OpenSSL to sign a document - Produces a Base64 encoding of a DER formatted CMS Document - http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax - """ - process = environment.subprocess.Popen(["openssl", "cms", "-sign", - "-signer", signing_cert_file_name, - "-inkey", signing_key_file_name, - "-outform", "PEM", - "-nosmimecap", "-nodetach", - "-nocerts", "-noattr"], - stdin=environment.subprocess.PIPE, - stdout=environment.subprocess.PIPE, - stderr=environment.subprocess.PIPE) - output, err = process.communicate(text) - retcode = process.poll() - if retcode or "Error" in err: - if retcode == 3: - LOG.error(_("Signing error: Unable to load certificate - " - "ensure you've configured PKI with " - "'keystone-manage pki_setup'")) - else: - LOG.error(_('Signing error: %s'), err) - raise environment.subprocess.CalledProcessError(retcode, "openssl") - return output - - -def cms_sign_token(text, signing_cert_file_name, signing_key_file_name): - output = cms_sign_text(text, signing_cert_file_name, signing_key_file_name) - return cms_to_token(output) - - -def cms_to_token(cms_text): - - start_delim = "-----BEGIN CMS-----" - end_delim = "-----END CMS-----" - signed_text = cms_text - signed_text = signed_text.replace('/', '-') - signed_text = signed_text.replace(start_delim, '') - signed_text = signed_text.replace(end_delim, '') - signed_text = signed_text.replace('\n', '') - - return signed_text - - -def cms_hash_token(token_id): - """Hash PKI tokens. - - return: for ans1_token, returns the hash of the passed in token - otherwise, returns what it was passed in. - """ - if token_id is None: - return None - if is_ans1_token(token_id): - hasher = hashlib.md5() - hasher.update(token_id) - return hasher.hexdigest() - else: - return token_id diff --git a/keystone/tests/test_token_provider.py b/keystone/tests/test_token_provider.py index a3fafbe28..6f1593e2d 100644 --- a/keystone/tests/test_token_provider.py +++ b/keystone/tests/test_token_provider.py @@ -20,6 +20,7 @@ from keystone.openstack.common import timeutils from keystone import tests from keystone.tests import default_fixtures from keystone import token +from keystone.token.providers import pki CONF = config.CONF @@ -832,3 +833,56 @@ class TestTokenProvider(tests.TestCase): self.assertRaises(exception.Forbidden, driver.issue_v3_token, self.user_foo['id'], ['oauth1']) + + +class TestPKIProvider(object): + + def setUp(self): + super(TestPKIProvider, self).setUp() + + from keystoneclient.common import cms + self.cms = cms + + from keystone.common import environment + self.environment = environment + + old_cms_subprocess = cms.subprocess + self.addCleanup(setattr, cms, 'subprocess', old_cms_subprocess) + + old_env_subprocess = environment.subprocess + self.addCleanup(setattr, environment, 'subprocess', old_env_subprocess) + + self.cms.subprocess = self.target_subprocess + self.environment.subprocess = self.target_subprocess + + reload(pki) # force module reload so the imports get re-evaluated + + def test_get_token_id_error_handling(self): + # cause command-line failure + self.opt_in_group('signing', keyfile='--please-break-me') + + provider = pki.Provider() + token_data = {} + self.assertRaises(exception.UnexpectedError, + provider._get_token_id, + token_data) + + +class TestPKIProviderWithEventlet(TestPKIProvider, tests.TestCase): + + def setUp(self): + # force keystoneclient.common.cms to use eventlet's subprocess + from eventlet.green import subprocess + self.target_subprocess = subprocess + + super(TestPKIProviderWithEventlet, self).setUp() + + +class TestPKIProviderWithStdlib(TestPKIProvider, tests.TestCase): + + def setUp(self): + # force keystoneclient.common.cms to use the stdlib subprocess + import subprocess + self.target_subprocess = subprocess + + super(TestPKIProviderWithStdlib, self).setUp() diff --git a/keystone/tests/test_v3_auth.py b/keystone/tests/test_v3_auth.py index fbd21168b..b6bbebd32 100644 --- a/keystone/tests/test_v3_auth.py +++ b/keystone/tests/test_v3_auth.py @@ -15,8 +15,9 @@ import json import uuid +from keystoneclient.common import cms + from keystone import auth -from keystone.common import cms from keystone import config from keystone import exception from keystone import tests diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py index f71cad0c4..3327e87b5 100644 --- a/keystone/token/controllers.py +++ b/keystone/token/controllers.py @@ -14,9 +14,9 @@ import json +from keystoneclient.common import cms import six -from keystone.common import cms from keystone.common import controller from keystone.common import dependency from keystone.common import wsgi diff --git a/keystone/token/core.py b/keystone/token/core.py index adfc56d6c..420b75f66 100644 --- a/keystone/token/core.py +++ b/keystone/token/core.py @@ -18,10 +18,10 @@ import abc import copy import datetime +from keystoneclient.common import cms import six from keystone.common import cache -from keystone.common import cms from keystone.common import dependency from keystone.common import manager from keystone import config diff --git a/keystone/token/providers/pki.py b/keystone/token/providers/pki.py index 72ba009b6..e5aeeb49e 100644 --- a/keystone/token/providers/pki.py +++ b/keystone/token/providers/pki.py @@ -16,7 +16,8 @@ import json -from keystone.common import cms +from keystoneclient.common import cms + from keystone.common import environment from keystone import config from keystone import exception |