Merge "Migrate the keystone.common.cms to keystoneclient"

This commit is contained in:
Jenkins
2013-11-26 08:04:33 +00:00
committed by Gerrit Code Review
5 changed files with 86 additions and 9 deletions

View File

@@ -12,10 +12,20 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # 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 import logging
from keystoneclient import exceptions
subprocess = None subprocess = None
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -38,10 +48,20 @@ def _ensure_subprocess():
import subprocess # noqa 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): def cms_verify(formatted, signing_cert_file_name, ca_file_name):
"""Verifies the signature of the contents IAW CMS syntax. """Verifies the signature of the contents IAW CMS syntax.
:raises: subprocess.CalledProcessError :raises: subprocess.CalledProcessError
:raises: CertificateConfigError if certificate is not configured properly.
""" """
_ensure_subprocess() _ensure_subprocess()
process = subprocess.Popen(["openssl", "cms", "-verify", process = subprocess.Popen(["openssl", "cms", "-verify",
@@ -55,9 +75,23 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name):
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
output, err = process.communicate(formatted) output, err = process.communicate(formatted)
retcode = process.poll() retcode = process.poll()
if retcode:
# Do not log errors, as some happen in the positive thread # Do not log errors, as some happen in the positive thread
# instead, catch them in the calling code and log them there. # 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: # NOTE(dmllr): Python 2.6 compatibility:
# CalledProcessError did not have output keyword argument # CalledProcessError did not have output keyword argument
e = subprocess.CalledProcessError(retcode, "openssl") e = subprocess.CalledProcessError(retcode, "openssl")

View File

@@ -20,3 +20,12 @@ Exception definitions.
#flake8: noqa #flake8: noqa
from keystoneclient.apiclient.exceptions import * 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)

View File

@@ -157,6 +157,7 @@ import netaddr
import six import six
from keystoneclient.common import cms from keystoneclient.common import cms
from keystoneclient import exceptions
from keystoneclient.middleware import memcache_crypt from keystoneclient.middleware import memcache_crypt
from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache from keystoneclient.openstack.common import memorycache
@@ -1158,7 +1159,7 @@ class AuthProtocol(object):
try: try:
output = cms.cms_verify(data, self.signing_cert_file_name, output = cms.cms_verify(data, self.signing_cert_file_name,
self.signing_ca_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, if self.cert_file_missing(err.output,
self.signing_cert_file_name): self.signing_cert_file_name):
self.fetch_signing_cert() self.fetch_signing_cert()
@@ -1167,8 +1168,10 @@ class AuthProtocol(object):
self.signing_ca_file_name): self.signing_ca_file_name):
self.fetch_ca_cert() self.fetch_ca_cert()
continue continue
raise
except cms.subprocess.CalledProcessError as err:
self.LOG.warning('Verify error: %s' % err) self.LOG.warning('Verify error: %s' % err)
raise err raise
return output return output
def verify_signed_token(self, signed_text): def verify_signed_token(self, signed_text):
@@ -1266,8 +1269,10 @@ class AuthProtocol(object):
with open(self.signing_cert_file_name, 'w') as certfile: with open(self.signing_cert_file_name, 'w') as certfile:
certfile.write(data) certfile.write(data)
if response.status_code != 200:
raise exceptions.CertificateConfigError(response.text)
try: try:
#todo check response
try: try:
write_cert_file(response.text) write_cert_file(response.text)
except IOError: except IOError:
@@ -1282,8 +1287,10 @@ class AuthProtocol(object):
path = self.auth_admin_prefix.rstrip('/') + '/v2.0/certificates/ca' path = self.auth_admin_prefix.rstrip('/') + '/v2.0/certificates/ca'
response = self._http_request('GET', path) response = self._http_request('GET', path)
if response.status_code != 200:
raise exceptions.CertificateConfigError(response.text)
try: try:
#todo check response
with open(self.signing_ca_file_name, 'w') as certfile: with open(self.signing_ca_file_name, 'w') as certfile:
certfile.write(response.text) certfile.write(response.text)
except (AssertionError, KeyError): except (AssertionError, KeyError):

View File

@@ -31,6 +31,7 @@ import mock
import webob import webob
from keystoneclient.common import cms from keystoneclient.common import cms
from keystoneclient import exceptions
from keystoneclient.middleware import auth_token from keystoneclient.middleware import auth_token
from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import memorycache from keystoneclient.openstack.common import memorycache
@@ -886,7 +887,7 @@ class CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest):
httpretty.register_uri(httpretty.GET, httpretty.register_uri(httpretty.GET,
"%s/v2.0/certificates/signing" % BASE_URI, "%s/v2.0/certificates/signing" % BASE_URI,
status=404) status=404)
self.assertRaises(cms.subprocess.CalledProcessError, self.assertRaises(exceptions.CertificateConfigError,
self.middleware.verify_signed_token, self.middleware.verify_signed_token,
client_fixtures.SIGNED_TOKEN_SCOPED) client_fixtures.SIGNED_TOKEN_SCOPED)

View File

@@ -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')