summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLei Zhang <zhang.lei.fly@gmail.com>2013-10-15 11:21:56 +0800
committerLei Zhang <zhang.lei.fly@gmail.com>2013-10-28 23:35:56 +0800
commit0c8faa3efc81ea4d2d93f64c118a965091bdf5b4 (patch)
treef9dd16a352cdfd02e6e3dfe4079c367bce0f0903
parentcc0e06ff87495178051d107534402dc42ad39c45 (diff)
downloadpython-keystoneclient-0c8faa3efc81ea4d2d93f64c118a965091bdf5b4.tar.gz
Migrate the keystone.common.cms to keystoneclient
- Add checking the openssl return code 2, related to following review https://review.openstack.org/#/c/22716/ - Add support set subprocess to the cms, when we already know which subprocess to use. Closes-Bug: #1142574 Change-Id: I3f86e6ca8bb7738f57051ce7f0f5662b20e7a22b
-rw-r--r--keystoneclient/common/cms.py42
-rw-r--r--keystoneclient/exceptions.py9
-rw-r--r--keystoneclient/middleware/auth_token.py15
-rw-r--r--keystoneclient/tests/test_auth_token_middleware.py3
-rw-r--r--keystoneclient/tests/test_cms.py26
5 files changed, 86 insertions, 9 deletions
diff --git a/keystoneclient/common/cms.py b/keystoneclient/common/cms.py
index 8bc24f9..da04029 100644
--- a/keystoneclient/common/cms.py
+++ b/keystoneclient/common/cms.py
@@ -12,10 +12,20 @@
# License for the specific language governing permissions and limitations
# under the License.
-import hashlib
+"""Certificate signing functions.
+
+Call set_subprocess() with the subprocess module. Either Python's
+subprocess or eventlet.green.subprocess can be used.
+
+If set_subprocess() is not called, this module will pick Python's subprocess
+or eventlet.green.subprocess based on if os module is patched by eventlet.
+"""
+import hashlib
import logging
+from keystoneclient import exceptions
+
subprocess = None
LOG = logging.getLogger(__name__)
@@ -38,10 +48,20 @@ def _ensure_subprocess():
import subprocess # noqa
+def set_subprocess(_subprocess=None):
+ """Set subprocess module to use.
+ The subprocess could be eventlet.green.subprocess if using eventlet,
+ or Python's subprocess otherwise.
+ """
+ global subprocess
+ subprocess = _subprocess
+
+
def cms_verify(formatted, signing_cert_file_name, ca_file_name):
"""Verifies the signature of the contents IAW CMS syntax.
:raises: subprocess.CalledProcessError
+ :raises: CertificateConfigError if certificate is not configured properly.
"""
_ensure_subprocess()
process = subprocess.Popen(["openssl", "cms", "-verify",
@@ -55,9 +75,23 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name):
stderr=subprocess.PIPE)
output, err = process.communicate(formatted)
retcode = process.poll()
- if retcode:
- # Do not log errors, as some happen in the positive thread
- # instead, catch them in the calling code and log them there.
+
+ # Do not log errors, as some happen in the positive thread
+ # instead, catch them in the calling code and log them there.
+
+ # When invoke the openssl with not exist file, return code 2
+ # and error msg will be returned.
+ # You can get more from
+ # http://www.openssl.org/docs/apps/cms.html#EXIT_CODES
+ #
+ # $ openssl cms -verify -certfile not_exist_file -CAfile \
+ # not_exist_file -inform PEM -nosmimecap -nodetach \
+ # -nocerts -noattr
+ # Error opening certificate file not_exist_file
+ #
+ if retcode == 2:
+ raise exceptions.CertificateConfigError(err)
+ elif retcode:
# NOTE(dmllr): Python 2.6 compatibility:
# CalledProcessError did not have output keyword argument
e = subprocess.CalledProcessError(retcode, "openssl")
diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py
index 2bd6c37..a03ef80 100644
--- a/keystoneclient/exceptions.py
+++ b/keystoneclient/exceptions.py
@@ -20,3 +20,12 @@ Exception definitions.
#flake8: noqa
from keystoneclient.apiclient.exceptions import *
+
+
+class CertificateConfigError(Exception):
+ """Error reading the certificate"""
+ def __init__(self, output):
+ self.output = output
+ msg = ("Unable to load certificate. "
+ "Ensure your system is configured properly.")
+ super(CertificateConfigError, self).__init__(msg)
diff --git a/keystoneclient/middleware/auth_token.py b/keystoneclient/middleware/auth_token.py
index ea62db3..78f29bb 100644
--- a/keystoneclient/middleware/auth_token.py
+++ b/keystoneclient/middleware/auth_token.py
@@ -157,6 +157,7 @@ import netaddr
import six
from keystoneclient.common import cms
+from keystoneclient import exceptions
from keystoneclient.middleware import memcache_crypt
from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache
@@ -1147,7 +1148,7 @@ class AuthProtocol(object):
try:
output = cms.cms_verify(data, self.signing_cert_file_name,
self.signing_ca_file_name)
- except cms.subprocess.CalledProcessError as err:
+ except exceptions.CertificateConfigError as err:
if self.cert_file_missing(err.output,
self.signing_cert_file_name):
self.fetch_signing_cert()
@@ -1156,8 +1157,10 @@ class AuthProtocol(object):
self.signing_ca_file_name):
self.fetch_ca_cert()
continue
+ raise
+ except cms.subprocess.CalledProcessError as err:
self.LOG.warning('Verify error: %s' % err)
- raise err
+ raise
return output
def verify_signed_token(self, signed_text):
@@ -1255,8 +1258,10 @@ class AuthProtocol(object):
with open(self.signing_cert_file_name, 'w') as certfile:
certfile.write(data)
+ if response.status_code != 200:
+ raise exceptions.CertificateConfigError(response.text)
+
try:
- #todo check response
try:
write_cert_file(response.text)
except IOError:
@@ -1271,8 +1276,10 @@ class AuthProtocol(object):
path = self.auth_admin_prefix.rstrip('/') + '/v2.0/certificates/ca'
response = self._http_request('GET', path)
+ if response.status_code != 200:
+ raise exceptions.CertificateConfigError(response.text)
+
try:
- #todo check response
with open(self.signing_ca_file_name, 'w') as certfile:
certfile.write(response.text)
except (AssertionError, KeyError):
diff --git a/keystoneclient/tests/test_auth_token_middleware.py b/keystoneclient/tests/test_auth_token_middleware.py
index 588c91f..4e2c2a3 100644
--- a/keystoneclient/tests/test_auth_token_middleware.py
+++ b/keystoneclient/tests/test_auth_token_middleware.py
@@ -31,6 +31,7 @@ import mock
import webob
from keystoneclient.common import cms
+from keystoneclient import exceptions
from keystoneclient.middleware import auth_token
from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache
@@ -876,7 +877,7 @@ class CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest):
httpretty.register_uri(httpretty.GET,
"%s/v2.0/certificates/signing" % BASE_URI,
status=404)
- self.assertRaises(cms.subprocess.CalledProcessError,
+ self.assertRaises(exceptions.CertificateConfigError,
self.middleware.verify_signed_token,
client_fixtures.SIGNED_TOKEN_SCOPED)
diff --git a/keystoneclient/tests/test_cms.py b/keystoneclient/tests/test_cms.py
new file mode 100644
index 0000000..8f9cb73
--- /dev/null
+++ b/keystoneclient/tests/test_cms.py
@@ -0,0 +1,26 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.
+
+from keystoneclient.common import cms
+from keystoneclient import exceptions
+from keystoneclient.tests import utils
+
+
+class CMSTest(utils.TestCase):
+ def test_cms_verify(self):
+ self.assertRaises(exceptions.CertificateConfigError,
+ cms.cms_verify,
+ 'data',
+ 'no_exist_cert_file',
+ 'no_exist_ca_file')