summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-02-26 06:12:53 +0000
committerGerrit Code Review <review@openstack.org>2014-02-26 06:12:53 +0000
commitccee49acfee0e276d731029830b1db010a153550 (patch)
treee3112741e4d583579e569af52a92dfeeeac7e926
parent8c8f776b6d38c8c9d5966ab02acaa763af7ab9b3 (diff)
parentae4626a857b3fec39a7a538f0ddbfad22cde5e34 (diff)
downloadkeystone-ccee49acfee0e276d731029830b1db010a153550.tar.gz
Merge "Remove duplicated cms file"
-rw-r--r--keystone/auth/controllers.py2
-rw-r--r--keystone/common/cms.py173
-rw-r--r--keystone/tests/test_token_provider.py54
-rw-r--r--keystone/tests/test_v3_auth.py3
-rw-r--r--keystone/token/controllers.py2
-rw-r--r--keystone/token/core.py2
-rw-r--r--keystone/token/providers/pki.py3
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