Starting to move to new plugin architecture.
This commit is contained in:
4
lemur/plugins/__init__.py
Normal file
4
lemur/plugins/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from lemur.plugins.base import * # NOQA
|
||||
from lemur.plugins.bases import * # NOQA
|
16
lemur/plugins/base/__init__.py
Normal file
16
lemur/plugins/base/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from lemur.plugins.base.manager import PluginManager
|
||||
from lemur.plugins.base.v1 import * # NOQA
|
||||
|
||||
plugins = PluginManager()
|
||||
register = plugins.register
|
||||
unregister = plugins.unregister
|
59
lemur/plugins/base/manager.py
Normal file
59
lemur/plugins/base/manager.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base.manager
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson (kglisson@netflix.com)
|
||||
"""
|
||||
from flask import current_app
|
||||
from lemur.common.managers import InstanceManager
|
||||
|
||||
|
||||
# inspired by https://github.com/getsentry/sentry
|
||||
class PluginManager(InstanceManager):
|
||||
def __iter__(self):
|
||||
return iter(self.all())
|
||||
|
||||
def __len__(self):
|
||||
return sum(1 for i in self.all())
|
||||
|
||||
def all(self, version=1):
|
||||
for plugin in sorted(super(PluginManager, self).all(), key=lambda x: x.get_title()):
|
||||
if not plugin.is_enabled():
|
||||
continue
|
||||
if version is not None and plugin.__version__ != version:
|
||||
continue
|
||||
yield plugin
|
||||
|
||||
def get(self, slug):
|
||||
for plugin in self.all(version=1):
|
||||
if plugin.slug == slug:
|
||||
return plugin
|
||||
for plugin in self.all(version=2):
|
||||
if plugin.slug == slug:
|
||||
return plugin
|
||||
raise KeyError(slug)
|
||||
|
||||
def first(self, func_name, *args, **kwargs):
|
||||
version = kwargs.pop('version', 1)
|
||||
for plugin in self.all(version=version):
|
||||
try:
|
||||
result = getattr(plugin, func_name)(*args, **kwargs)
|
||||
except Exception as e:
|
||||
current_app.logger.error('Error processing %s() on %r: %s', func_name, plugin.__class__, e, extra={
|
||||
'func_arg': args,
|
||||
'func_kwargs': kwargs,
|
||||
}, exc_info=True)
|
||||
continue
|
||||
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
def register(self, cls):
|
||||
self.add('%s.%s' % (cls.__module__, cls.__name__))
|
||||
return cls
|
||||
|
||||
def unregister(self, cls):
|
||||
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
||||
return cls
|
||||
|
117
lemur/plugins/base/v1.py
Normal file
117
lemur/plugins/base/v1.py
Normal file
@ -0,0 +1,117 @@
|
||||
"""
|
||||
.. module: lemur.plugins.base.v1
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from threading import local
|
||||
|
||||
# stolen from https://github.com/getsentry/sentry/
|
||||
class PluginMount(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
new_cls = type.__new__(cls, name, bases, attrs)
|
||||
if IPlugin in bases:
|
||||
return new_cls
|
||||
if new_cls.title is None:
|
||||
new_cls.title = new_cls.__name__
|
||||
if not new_cls.slug:
|
||||
new_cls.slug = new_cls.title.replace(' ', '-').lower()
|
||||
return new_cls
|
||||
|
||||
|
||||
class IPlugin(local):
|
||||
"""
|
||||
Plugin interface. Should not be inherited from directly.
|
||||
A plugin should be treated as if it were a singleton. The owner does not
|
||||
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||
it will happen, or happen more than once.
|
||||
>>> from lemur.plugins import Plugin
|
||||
>>>
|
||||
>>> class MyPlugin(Plugin):
|
||||
>>> def get_title(self):
|
||||
>>> return 'My Plugin'
|
||||
As a general rule all inherited methods should allow ``**kwargs`` to ensure
|
||||
ease of future compatibility.
|
||||
"""
|
||||
# Generic plugin information
|
||||
title = None
|
||||
slug = None
|
||||
description = None
|
||||
version = None
|
||||
author = None
|
||||
author_url = None
|
||||
resource_links = ()
|
||||
|
||||
# Configuration specifics
|
||||
conf_key = None
|
||||
conf_title = None
|
||||
|
||||
# Global enabled state
|
||||
enabled = True
|
||||
can_disable = True
|
||||
|
||||
def is_enabled(self, project=None):
|
||||
"""
|
||||
Returns a boolean representing if this plugin is enabled.
|
||||
If ``project`` is passed, it will limit the scope to that project.
|
||||
>>> plugin.is_enabled()
|
||||
"""
|
||||
if not self.enabled:
|
||||
return False
|
||||
if not self.can_disable:
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def get_conf_key(self):
|
||||
"""
|
||||
Returns a string representing the configuration keyspace prefix for this plugin.
|
||||
"""
|
||||
if not self.conf_key:
|
||||
self.conf_key = self.get_conf_title().lower().replace(' ', '_')
|
||||
return self.conf_key
|
||||
|
||||
def get_conf_title(self):
|
||||
"""
|
||||
Returns a string representing the title to be shown on the configuration page.
|
||||
"""
|
||||
return self.conf_title or self.get_title()
|
||||
|
||||
def get_title(self):
|
||||
"""
|
||||
Returns the general title for this plugin.
|
||||
>>> plugin.get_title()
|
||||
"""
|
||||
return self.title
|
||||
|
||||
def get_description(self):
|
||||
"""
|
||||
Returns the description for this plugin. This is shown on the plugin configuration
|
||||
page.
|
||||
>>> plugin.get_description()
|
||||
"""
|
||||
return self.description
|
||||
|
||||
def get_resource_links(self):
|
||||
"""
|
||||
Returns a list of tuples pointing to various resources for this plugin.
|
||||
>>> def get_resource_links(self):
|
||||
>>> return [
|
||||
>>> ('Documentation', 'http://sentry.readthedocs.org'),
|
||||
>>> ('Bug Tracker', 'https://github.com/getsentry/sentry/issues'),
|
||||
>>> ('Source', 'https://github.com/getsentry/sentry'),
|
||||
>>> ]
|
||||
"""
|
||||
return self.resource_links
|
||||
|
||||
|
||||
class Plugin(IPlugin):
|
||||
"""
|
||||
A plugin should be treated as if it were a singleton. The owner does not
|
||||
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||
it will happen, or happen more than once.
|
||||
"""
|
||||
__version__ = 1
|
||||
__metaclass__ = PluginMount
|
2
lemur/plugins/bases/__init__.py
Normal file
2
lemur/plugins/bases/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .destination import DestinationPlugin # NOQA
|
||||
from .issuer import IssuerPlugin # NOQA
|
13
lemur/plugins/bases/destination.py
Normal file
13
lemur/plugins/bases/destination.py
Normal file
@ -0,0 +1,13 @@
|
||||
"""
|
||||
.. module: lemur.bases.destination
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from lemur.plugins.base import Plugin
|
||||
|
||||
class DestinationPlugin(Plugin):
|
||||
pass
|
||||
|
27
lemur/plugins/bases/issuer.py
Normal file
27
lemur/plugins/bases/issuer.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""
|
||||
.. module: lemur.bases.issuer
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from lemur.plugins.base import Plugin
|
||||
|
||||
class IssuerPlugin(Plugin):
|
||||
"""
|
||||
This is the base class from which all of the supported
|
||||
issuers will inherit from.
|
||||
"""
|
||||
def create_certificate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def create_authority(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_authorities(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_csr_config(self):
|
||||
raise NotImplementedError
|
||||
|
0
lemur/plugins/bases/source.py
Normal file
0
lemur/plugins/bases/source.py
Normal file
0
lemur/plugins/lemur_aws/__init__.py
Normal file
0
lemur/plugins/lemur_aws/__init__.py
Normal file
0
lemur/plugins/lemur_aws/aws.py
Normal file
0
lemur/plugins/lemur_aws/aws.py
Normal file
5
lemur/plugins/lemur_cloudca/__init__.py
Normal file
5
lemur/plugins/lemur_cloudca/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
try:
|
||||
VERSION = __import__('pkg_resources') \
|
||||
.get_distribution(__name__).version
|
||||
except Exception, e:
|
||||
VERSION = 'unknown'
|
331
lemur/plugins/lemur_cloudca/plugin.py
Normal file
331
lemur/plugins/lemur_cloudca/plugin.py
Normal file
@ -0,0 +1,331 @@
|
||||
"""
|
||||
.. module: lemur.common.services.issuers.plugins.cloudca
|
||||
:platform: Unix
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
|
||||
"""
|
||||
import ssl
|
||||
import base64
|
||||
from json import dumps
|
||||
|
||||
import arrow
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from lemur.exceptions import LemurException
|
||||
from lemur.plugins.bases import IssuerPlugin
|
||||
from lemur.plugins import lemur_cloudca as cloudca
|
||||
|
||||
from lemur.authorities import service as authority_service
|
||||
|
||||
API_ENDPOINT = '/v1/ca/netflix'
|
||||
|
||||
|
||||
class CloudCAException(LemurException):
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
current_app.logger.error(self)
|
||||
|
||||
def __str__(self):
|
||||
return repr("CloudCA request failed: {0}".format(self.message))
|
||||
|
||||
|
||||
class CloudCAHostNameCheckingAdapter(HTTPAdapter):
|
||||
def cert_verify(self, conn, url, verify, cert):
|
||||
super(CloudCAHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert)
|
||||
conn.assert_hostname = False
|
||||
|
||||
|
||||
def remove_none(options):
|
||||
"""
|
||||
Simple function that traverse the options and removed any None items
|
||||
CloudCA really dislikes null values.
|
||||
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
new_dict = {}
|
||||
for k, v in options.items():
|
||||
if v:
|
||||
new_dict[k] = v
|
||||
|
||||
# this is super hacky and gross, cloudca doesn't like null values
|
||||
if new_dict.get('extensions'):
|
||||
if len(new_dict['extensions']['subAltNames']['names']) == 0:
|
||||
del new_dict['extensions']['subAltNames']
|
||||
|
||||
return new_dict
|
||||
|
||||
|
||||
def get_default_issuance(options):
|
||||
"""
|
||||
Gets the default time range for certificates
|
||||
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
if not options.get('validityStart') and not options.get('validityEnd'):
|
||||
start = arrow.utcnow()
|
||||
options['validityStart'] = start.floor('second').isoformat()
|
||||
options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY')).ceil('second').isoformat()
|
||||
return options
|
||||
|
||||
|
||||
def convert_to_pem(der):
|
||||
"""
|
||||
Converts DER to PEM Lemur uses PEM internally
|
||||
|
||||
:param der:
|
||||
:return:
|
||||
"""
|
||||
decoded = base64.b64decode(der)
|
||||
return ssl.DER_cert_to_PEM_cert(decoded)
|
||||
|
||||
|
||||
def convert_date_to_utc_time(date):
|
||||
"""
|
||||
Converts a python `datetime` object to the current date + current time in UTC.
|
||||
|
||||
:param date:
|
||||
:return:
|
||||
"""
|
||||
d = arrow.get(date)
|
||||
return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year).replace(microsecond=0)
|
||||
|
||||
|
||||
def process_response(response):
|
||||
"""
|
||||
Helper function that processes responses from CloudCA.
|
||||
|
||||
:param response:
|
||||
:return: :raise CloudCAException:
|
||||
"""
|
||||
if response.status_code == 200:
|
||||
res = response.json()
|
||||
if res['returnValue'] != 'success':
|
||||
current_app.logger.debug(res)
|
||||
if res.get('data'):
|
||||
raise CloudCAException(" ".join([res['returnMessage'], res['data']['dryRunResultMessage']]))
|
||||
else:
|
||||
raise CloudCAException(res['returnMessage'])
|
||||
else:
|
||||
raise CloudCAException("There was an error with your request: {0}".format(response.status_code))
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_auth_data(ca_name):
|
||||
"""
|
||||
Creates the authentication record needed to authenticate a user request to CloudCA.
|
||||
|
||||
:param ca_name:
|
||||
:return: :raise CloudCAException:
|
||||
"""
|
||||
role = authority_service.get_authority_role(ca_name)
|
||||
if role:
|
||||
return {
|
||||
"authInfo": {
|
||||
"credType": "password",
|
||||
"credentials": {
|
||||
"username": role.username,
|
||||
"password": role.password # we only decrypt when we need to
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
|
||||
|
||||
|
||||
class CloudCAPlugin(IssuerPlugin):
|
||||
title = 'CloudCA'
|
||||
slug = 'cloudca'
|
||||
description = 'Enables the creation of certificates from the cloudca API.'
|
||||
version = cloudca.VERSION
|
||||
|
||||
author = 'Kevin Glisson'
|
||||
author_url = 'https://github.com/netflix/lemur'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.session = requests.Session()
|
||||
self.session.mount('https://', CloudCAHostNameCheckingAdapter())
|
||||
self.url = current_app.config.get('CLOUDCA_URL')
|
||||
|
||||
if current_app.config.get('CLOUDCA_PEM_PATH') and current_app.config.get('CLOUDCA_BUNDLE'):
|
||||
self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
|
||||
self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
|
||||
else:
|
||||
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
||||
|
||||
super(CloudCAPlugin, self).__init__(*args, **kwargs)
|
||||
|
||||
def create_authority(self, options):
|
||||
"""
|
||||
Creates a new certificate authority
|
||||
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
# this is weird and I don't like it
|
||||
endpoint = '{0}/createCA'.format(API_ENDPOINT)
|
||||
options['caDN']['email'] = options['ownerEmail']
|
||||
|
||||
if options['caType'] == 'subca':
|
||||
options = dict(options.items() + self.auth_data(options['caParent']).items())
|
||||
|
||||
options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
|
||||
options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()
|
||||
|
||||
response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10, verify=self.ca_bundle)
|
||||
|
||||
json = process_response(response)
|
||||
roles = []
|
||||
|
||||
for cred in json['data']['authInfo']:
|
||||
role = {
|
||||
'username': cred['credentials']['username'],
|
||||
'password': cred['credentials']['password'],
|
||||
'name': "_".join([options['caName'], cred['credentials']['username']])
|
||||
}
|
||||
roles.append(role)
|
||||
|
||||
if options['caType'] == 'subca':
|
||||
cert = convert_to_pem(json['data']['certificate'])
|
||||
else:
|
||||
cert = convert_to_pem(json['data']['rootCertificate'])
|
||||
|
||||
intermediates = []
|
||||
for i in json['data']['intermediateCertificates']:
|
||||
intermediates.append(convert_to_pem(i))
|
||||
|
||||
return cert, "".join(intermediates), roles,
|
||||
|
||||
def get_authorities(self):
|
||||
"""
|
||||
Retrieves authorities that were made outside of Lemur.
|
||||
|
||||
:return:
|
||||
"""
|
||||
endpoint = '{0}/listCAs'.format(API_ENDPOINT)
|
||||
authorities = []
|
||||
for ca in self.get(endpoint)['data']['caList']:
|
||||
try:
|
||||
authorities.append(ca['caName'])
|
||||
except AttributeError as e:
|
||||
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
||||
|
||||
return authorities
|
||||
|
||||
def create_certificate(self, csr, options):
|
||||
"""
|
||||
Creates a new certificate from cloudca
|
||||
|
||||
If no start and end date are specified the default issue range
|
||||
will be used.
|
||||
|
||||
:param csr:
|
||||
:param options:
|
||||
"""
|
||||
endpoint = '{0}/enroll'.format(API_ENDPOINT)
|
||||
# lets default to two years if it's not specified
|
||||
# we do some last minute data massaging
|
||||
options = get_default_issuance(options)
|
||||
|
||||
cloudca_options = {
|
||||
'extensions': options['extensions'],
|
||||
'validityStart': convert_date_to_utc_time(options['validityStart']).isoformat(),
|
||||
'validityEnd': convert_date_to_utc_time(options['validityEnd']).isoformat(),
|
||||
'creator': options['creator'],
|
||||
'ownerEmail': options['owner'],
|
||||
'caName': options['authority'].name,
|
||||
'csr': csr,
|
||||
'comment': options['description']
|
||||
}
|
||||
|
||||
response = self.post(endpoint, remove_none(cloudca_options))
|
||||
|
||||
# we return a concatenated list of intermediate because that is what aws
|
||||
# expects
|
||||
cert = convert_to_pem(response['data']['certificate'])
|
||||
|
||||
intermediates = [convert_to_pem(response['data']['rootCertificate'])]
|
||||
for i in response['data']['intermediateCertificates']:
|
||||
intermediates.append(convert_to_pem(i))
|
||||
|
||||
return cert, "".join(intermediates),
|
||||
|
||||
def random(self, length=10):
|
||||
"""
|
||||
Uses CloudCA as a decent source of randomness.
|
||||
|
||||
:param length:
|
||||
:return:
|
||||
"""
|
||||
endpoint = '/v1/random/{0}'.format(length)
|
||||
response = self.session.get(self.url + endpoint, verify=self.ca_bundle)
|
||||
return response
|
||||
|
||||
def get_cert(self, ca_name=None, cert_handle=None):
|
||||
"""
|
||||
Returns a given cert from CloudCA.
|
||||
|
||||
:param ca_name:
|
||||
:param cert_handle:
|
||||
:return:
|
||||
"""
|
||||
endpoint = '{0}/getCert'.format(API_ENDPOINT)
|
||||
response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10, verify=self.ca_bundle)
|
||||
raw = process_response(response)
|
||||
|
||||
certs = []
|
||||
for c in raw['data']['certList']:
|
||||
cert = convert_to_pem(c['certValue'])
|
||||
|
||||
intermediates = []
|
||||
for i in c['intermediateCertificates']:
|
||||
intermediates.append(convert_to_pem(i))
|
||||
|
||||
certs.append({
|
||||
'public_certificate': cert,
|
||||
'intermediate_cert': "\n".join(intermediates),
|
||||
'owner': c['ownerEmail']
|
||||
})
|
||||
|
||||
return certs
|
||||
|
||||
def post(self, endpoint, data):
|
||||
"""
|
||||
HTTP POST to CloudCA
|
||||
|
||||
:param endpoint:
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
if self.dry_run:
|
||||
endpoint += '?dry_run=1'
|
||||
|
||||
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
|
||||
|
||||
# we set a low timeout, if cloudca is down it shouldn't bring down
|
||||
# lemur
|
||||
response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)
|
||||
return process_response(response)
|
||||
|
||||
def get(self, endpoint):
|
||||
"""
|
||||
HTTP GET to CloudCA
|
||||
|
||||
:param endpoint:
|
||||
:return:
|
||||
"""
|
||||
if self.dry_run:
|
||||
endpoint += '?dry_run=1'
|
||||
|
||||
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
||||
return process_response(response)
|
||||
|
5
lemur/plugins/lemur_verisign/__init__.py
Normal file
5
lemur/plugins/lemur_verisign/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
try:
|
||||
VERSION = __import__('pkg_resources') \
|
||||
.get_distribution(__name__).version
|
||||
except Exception, e:
|
||||
VERSION = 'unknown'
|
119
lemur/plugins/lemur_verisign/constants.py
Normal file
119
lemur/plugins/lemur_verisign/constants.py
Normal file
@ -0,0 +1,119 @@
|
||||
VERISIGN_INTERMEDIATE = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFFTCCA/2gAwIBAgIQKC4nkXkzkuQo8iGnTsk3rjANBgkqhkiG9w0BAQsFADCB
|
||||
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
|
||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
||||
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5IC0gRzMwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw
|
||||
CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
|
||||
BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs
|
||||
YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb
|
||||
A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW
|
||||
9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu
|
||||
s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T
|
||||
L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK
|
||||
Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBQDCCATwwHQYDVR0O
|
||||
BBYEFF9gz2GQVd+EQxSKYCqy9Xr0QxjvMBIGA1UdEwEB/wQIMAYBAf8CAQAwawYD
|
||||
VR0gBGQwYjBgBgpghkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cu
|
||||
c3ltYXV0aC5jb20vY3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0
|
||||
aC5jb20vcnBhMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNvbS9w
|
||||
Y2EzLWczLmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwxGjAYBgNV
|
||||
BAMTEVN5bWFudGVjUEtJLTEtNTM0MC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcw
|
||||
AYYSaHR0cDovL3Muc3ltY2QuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBbF1K+1lZ7
|
||||
9Pc0CUuWysf2IdBpgO/nmhnoJOJ/2S9h3RPrWmXk4WqQy04q6YoW51KN9kMbRwUN
|
||||
gKOomv4p07wdKNWlStRxPA91xQtzPwBIZXkNq2oeJQzAAt5mrL1LBmuaV4oqgX5n
|
||||
m7pSYHPEFfe7wVDJCKW6V0o6GxBzHOF7tpQDS65RsIJAOloknO4NWF2uuil6yjOe
|
||||
soHCL47BJ89A8AShP/U3wsr8rFNtqVNpT+F2ZAwlgak3A/I5czTSwXx4GByoaxbn
|
||||
5+CdKa/Y5Gk5eZVpuXtcXQGc1PfzSEUTZJXXCm5y2kMiJG8+WnDcwJLgLeVX+OQr
|
||||
J+71/xuzAYN6
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
||||
|
||||
VERISIGN_ROOT = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
|
||||
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
|
||||
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
|
||||
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
|
||||
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
||||
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
|
||||
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
|
||||
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
|
||||
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
|
||||
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
|
||||
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
|
||||
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
|
||||
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
|
||||
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
|
||||
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
|
||||
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
|
||||
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
|
||||
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
|
||||
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
|
||||
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
|
||||
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
||||
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
||||
|
||||
OLD_VERISIGN_INTERMEDIATE = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFlTCCBH2gAwIBAgIQLP62CQ7ireLp/CI3JPG2vzANBgkqhkiG9w0BAQUFADCB
|
||||
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
|
||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
||||
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5IC0gRzMwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
|
||||
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
|
||||
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
|
||||
aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
|
||||
aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
|
||||
5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
|
||||
f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
|
||||
tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
|
||||
GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
|
||||
M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggGIMIIB
|
||||
hDASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4RQEHFwMw
|
||||
VjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczAqBggr
|
||||
BgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMA4GA1UdDwEB
|
||||
/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9naWYwITAf
|
||||
MAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8vbG9nby52
|
||||
ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQ
|
||||
VmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+HSCrJfQBY9i+eaUw
|
||||
NAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy1n
|
||||
My5jcmwwDQYJKoZIhvcNAQEFBQADggEBAHREFQzFWA4YY+3z8CjDeuuSSG/ghSBJ
|
||||
olwwlpIX4IjoeYuzT864Hzk2tTeEeODf4YFIVsSxah8nUsGdpgVTUGPPoUJOMXvn
|
||||
8wJeBSlUDXBwv3td5XbPIPXHy6vmIS6phYRetZUgq1CDTI/pvtWZKXTGM/eYXlLF
|
||||
6QDvXevUHQjfb3cqQvfLljws85xLxbNFmz7cy9YmiLOd5n+gFC6X5hzSDO7+DDMi
|
||||
o//+4Q/nk/UId1UCsobqYWVmqs017AmyiAPO/v3sGncYYQY2BMYgla74dZfeDNu4
|
||||
MXA68Mb6ZdlkhGEmZYVBcOmkaKs+P+SggTofsK27BlpugAtNWjEy5JY=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEOzCCA6SgAwIBAgIQSsnqCI7m94zHpfn6OaSTljANBgkqhkiG9w0BAQUFADBf
|
||||
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
|
||||
LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
|
||||
HhcNMTEwNjA5MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
|
||||
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
|
||||
dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJpU2lnbiwgSW5jLiAtIEZv
|
||||
ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
|
||||
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzMwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLupxS/HgfGh5vGzdzvfjJa5QS
|
||||
ME/wNkf10JEK9RfIpWHBFkBN+4phkOV2IMERBn2rLG6m9RFBjvotrSphWaRnJkzQ
|
||||
6LxSW3AgBFjResmkabyDF2StBYu80FjOjYz16/BCSQudlydnMm7hrpMVHHC8IE0v
|
||||
GN6SiOhshVcRGul+4yYRVKJFllWDyjCJ6NzYo+0qgD9/eWVXPhUgZggvlZO/qkcv
|
||||
qEaX8BLi/sIKK1Hmdua3RrfiDabMqMNMWVWJ5uhTXBzqnfBiFgunyV8M8N7Cds6v
|
||||
92ry+kGmojMUyeV6Y9OeYjfVhWWeDuZTJHQbXh0SU1vHLOeDSTsVropouVeXAgMB
|
||||
AAGjggEGMIIBAjAPBgNVHRMBAf8EBTADAQH/MD0GA1UdIAQ2MDQwMgYEVR0gADAq
|
||||
MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vY3BzMDEGA1Ud
|
||||
HwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4G
|
||||
A1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9n
|
||||
aWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8v
|
||||
bG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjANBgkqhkiG9w0BAQUFAAOBgQBl
|
||||
2Sr58sJgybnqQQfKNrcYL2iu/gMk5mdU7nTDLNn1M8Fetw6Tz3iejrImFBFT0cjC
|
||||
EiG0PXsq2BzUS2TsiU+/lYeH3pVk9HPGF9+9GZCX6GmBEmlmStMkQA5ZdRWwRHQX
|
||||
op4GYNOwg7jdL+afe2dcFqFH284ueQXZ8fT4PuJKoQ==
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
156
lemur/plugins/lemur_verisign/plugin.py
Normal file
156
lemur/plugins/lemur_verisign/plugin.py
Normal file
@ -0,0 +1,156 @@
|
||||
"""
|
||||
.. module: lemur.plugins.lemur_verisign.verisign
|
||||
:platform: Unix
|
||||
:synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
import arrow
|
||||
import requests
|
||||
import xmltodict
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from lemur.plugins.bases import IssuerPlugin
|
||||
from lemur.plugins import lemur_verisign as verisign
|
||||
from lemur.plugins.lemur_verisign import constants
|
||||
|
||||
|
||||
# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
|
||||
VERISIGN_ERRORS = {
|
||||
"0x30c5": "Domain Mismatch when enrolling for an SSL certificate, a domain in your request has not been added to verisign",
|
||||
"0x482d": "Cannot issue SHA1 certificates expiring after 31/12/2016",
|
||||
"0x3a10": "Invalid X509 certificate format.: an unsupported certificate format was submitted",
|
||||
"0x4002": "Internal QM Error. : Internal Database connection error.",
|
||||
"0x3301": "Bad transaction id or parent cert not renewable.: User try to renew a certificate that is not yet ready for renew or the transaction id is wrong",
|
||||
"0x3069": "Challenge phrase mismatch: The challenge phrase submitted does not match the original one",
|
||||
"0x3111": "Unsupported Product: User submitted a wrong product or requested cipher is not supported",
|
||||
"0x30e8": "CN or org does not match the original one.: the submitted CSR contains a common name or org that does not match the original one",
|
||||
"0x1005": "Duplicate certificate: a certificate with the same common name exists already",
|
||||
"0x0194": "Incorrect Signature Algorithm: The requested signature algorithm is not supported for the key type. i.e. an ECDSA is submitted for an RSA key",
|
||||
"0x6000": "parameter missing or incorrect: This is a general error code for missing or incorrect parameters. The reason will be in the response message. i.e. 'CSR is missing, 'Unsupported serverType' when no supported serverType could be found., 'invalid transaction id'",
|
||||
"0x3063": "Certificate not allowed: trying to issue a certificate that is not configured for the account",
|
||||
"0x23df": "No MDS Data Returned: internal connection lost or server not responding. this should be rare",
|
||||
"0x3004": "Invalid Account: The users mpki account associated with the certificate is not valid or not yet active",
|
||||
"0x4101": "Internal Error: internal server error, user should try again later. (Also check that State is spelled out",
|
||||
"0x3101": "Missing admin role: Your account does not have the admin role required to access the webservice API",
|
||||
"0x3085": "Account does not have webservice feature.: Your account does not the the webservice role required to access the webservice API",
|
||||
"0x9511": "Corrupted CSR : the submitted CSR was mal-formed",
|
||||
"0xa001": "Public key format does not match.: The public key format does not match the original cert at certificate renewal or replacement. E.g. if you try to renew or replace an RSA cert with a DSA or ECC key based CSR",
|
||||
"0x0143": "Certificate End Date Error: You are trying to replace a certificate with validity end date exceeding the original cert. or the certificate end date is not valid",
|
||||
"0x482d": "SHA1 validity check error: What error code do we get when we submit the SHA1 SSL requests with the validity more than 12/31/2016?",
|
||||
"0x482e": "What error code do we get when we cannot complete the re-authentication for domains with a newly-approved gTLD 30 days after the gTLD approval",
|
||||
"0x4824": "Per CA/B Forum baseline requirements, non-FQDN certs cannot exceed 11/1/2015. Examples: hostname, foo.cba (.cba is a pending gTLD)",
|
||||
"eE0x48": "Currently the maximum cert validity is 4-years",
|
||||
"0x4826": "OU misleading. See comments",
|
||||
"0x4827": "Org re-auth past due. EV org has to go through re-authentication every 13 months; OV org has to go through re-authentication every 39 months",
|
||||
"0x482a": "Domain re-auth past due. EV domain has to go through re-authentication every 13 months; OV domain has to go through re-authentication every 39 months.",
|
||||
"0x482b": "No org address was set to default, should not happen",
|
||||
"0x482c": "signature algorithm does not match intended key type in the CSR (e.g. CSR has an ECC key, but the signature algorithm is sha1WithRSAEncryption)",
|
||||
"0x600E": "only supports ECC keys with the named curve NIST P-256, aka secp256r1 or prime256v1, other ECC key sizes will get this error ",
|
||||
"0x6013": "only supports DSA keys with (2048, 256) as the bit lengths of the prime parameter pair (p, q), other DSA key sizes will get this error",
|
||||
"0x600d": "RSA key size < 2A048",
|
||||
"0x4828": "Verisign certificates can be at most two years in length",
|
||||
"0x3043": "Certificates must have a validity of at least 1 day"
|
||||
}
|
||||
|
||||
|
||||
class VerisignPlugin(IssuerPlugin):
|
||||
title = 'VeriSign'
|
||||
slug = 'verisign'
|
||||
description = 'Enables the creation of certificates by the VICE2.0 verisign API.'
|
||||
version = verisign.VERSION
|
||||
|
||||
author = 'Kevin Glisson'
|
||||
author_url = 'https://github.com/netflix/lemur'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.session = requests.Session()
|
||||
self.session.cert = current_app.config.get('VERISIGN_PEM_PATH')
|
||||
super(VerisignPlugin, self).__init__(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def handle_response(content):
|
||||
"""
|
||||
Helper function that helps with parsing responses from the Verisign API.
|
||||
:param content:
|
||||
:return: :raise Exception:
|
||||
"""
|
||||
d = xmltodict.parse(content)
|
||||
global VERISIGN_ERRORS
|
||||
if d.get('Error'):
|
||||
status_code = d['Error']['StatusCode']
|
||||
elif d.get('Response'):
|
||||
status_code = d['Response']['StatusCode']
|
||||
if status_code in VERISIGN_ERRORS.keys():
|
||||
raise Exception(VERISIGN_ERRORS[status_code])
|
||||
return d
|
||||
|
||||
def create_certificate(self, csr, issuer_options):
|
||||
"""
|
||||
Creates a Verisign certificate.
|
||||
|
||||
:param csr:
|
||||
:param issuer_options:
|
||||
:return: :raise Exception:
|
||||
"""
|
||||
url = current_app.config.get("VERISIGN_URL") + '/enroll'
|
||||
|
||||
data = {
|
||||
'csr': csr,
|
||||
'challenge': issuer_options['challenge'],
|
||||
'serverType': 'Apache',
|
||||
'certProductType': 'Server',
|
||||
'firstName': current_app.config.get("VERISIGN_FIRST_NAME"),
|
||||
'lastName': current_app.config.get("VERISIGN_LAST_NAME"),
|
||||
'signatureAlgorithm': 'sha256WithRSAEncryption',
|
||||
'email': current_app.config.get("VERISIGN_EMAIL")
|
||||
}
|
||||
|
||||
if issuer_options.get('validityEnd'):
|
||||
data['specificEndDate'] = arrow.get(issuer_options['validityEnd']).replace(days=-1).format("MM/DD/YYYY")
|
||||
|
||||
now = arrow.utcnow()
|
||||
then = arrow.get(issuer_options['validityEnd'])
|
||||
|
||||
if then < now.replace(years=+1):
|
||||
data['validityPeriod'] = '1Y'
|
||||
elif then < now.replace(years=+2):
|
||||
data['validityPeriod'] = '2Y'
|
||||
else:
|
||||
raise Exception("Verisign issued certificates cannot exceed two years in validity")
|
||||
|
||||
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
||||
|
||||
response = self.session.post(url, data=data)
|
||||
cert = self.handle_response(response.content)['Response']['Certificate']
|
||||
return cert, constants.VERISIGN_INTERMEDIATE,
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
"""
|
||||
Creates an authority, this authority is then used by Lemur to allow a user
|
||||
to specify which Certificate Authority they want to sign their certificate.
|
||||
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
role = {'username': '', 'password': '', 'name': 'verisign'}
|
||||
return constants.VERISIGN_ROOT, "", [role]
|
||||
|
||||
def get_available_units(self):
|
||||
"""
|
||||
Uses the Verisign to fetch the number of available unit's left. This can be used to get tabs
|
||||
on the number of certificates that can be issued.
|
||||
|
||||
:return:
|
||||
"""
|
||||
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
||||
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
||||
return self.handle_response(response.content)['Response']['Order']
|
||||
|
||||
def get_authorities(self):
|
||||
pass
|
||||
|
Reference in New Issue
Block a user