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
|
# 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")
|
||||||
|
@@ -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)
|
||||||
|
@@ -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):
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
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