Merge "Migrate the keystone.common.cms to keystoneclient"
This commit is contained in:
@@ -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")
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
@@ -1158,7 +1159,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()
|
||||
@@ -1167,8 +1168,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):
|
||||
@@ -1266,8 +1269,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:
|
||||
@@ -1282,8 +1287,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):
|
||||
|
@@ -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
|
||||
@@ -886,7 +887,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)
|
||||
|
||||
|
26
keystoneclient/tests/test_cms.py
Normal file
26
keystoneclient/tests/test_cms.py
Normal 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')
|
Reference in New Issue
Block a user