We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Common name field needs additional validation on the UI and API
It looks like 64 character limit

http://stackoverflow.com/questions/5136198/what-strings-are-allowed-in-the-common-name-attribute-in-an-x-509-certificate


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lemur/plugins/lemur_cloudca/plugin.py`
Content:
```
1 """
2 .. module: lemur.common.services.issuers.plugins.cloudca
3     :platform: Unix
4     :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
5     :license: Apache, see LICENSE for more details.
6 
7 .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
8 
9 """
10 import ssl
11 import base64
12 from json import dumps
13 
14 import arrow
15 import requests
16 from requests.adapters import HTTPAdapter
17 from requests.exceptions import ConnectionError
18 
19 from flask import current_app
20 
21 from lemur.exceptions import LemurException
22 from lemur.plugins.bases import IssuerPlugin, SourcePlugin
23 from lemur.plugins import lemur_cloudca as cloudca
24 
25 from lemur.authorities import service as authority_service
26 
27 
28 class CloudCAException(LemurException):
29     def __init__(self, message):
30         self.message = message
31         current_app.logger.error(self)
32 
33     def __str__(self):
34         return repr("CloudCA request failed: {0}".format(self.message))
35 
36 
37 class CloudCAHostNameCheckingAdapter(HTTPAdapter):
38     def cert_verify(self, conn, url, verify, cert):
39         super(CloudCAHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert)
40         conn.assert_hostname = False
41 
42 
43 def remove_none(options):
44     """
45     Simple function that traverse the options and removed any None items
46     CloudCA really dislikes null values.
47 
48     :param options:
49     :return:
50     """
51     new_dict = {}
52     for k, v in options.items():
53         if v:
54             new_dict[k] = v
55 
56     # this is super hacky and gross, cloudca doesn't like null values
57     if new_dict.get('extensions'):
58         if len(new_dict['extensions']['subAltNames']['names']) == 0:
59             del new_dict['extensions']['subAltNames']
60 
61     return new_dict
62 
63 
64 def get_default_issuance(options):
65     """
66     Gets the default time range for certificates
67 
68     :param options:
69     :return:
70     """
71     if not options.get('validityStart') and not options.get('validityEnd'):
72         start = arrow.utcnow()
73         options['validityStart'] = start.floor('second').isoformat()
74         options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY'))\
75             .ceil('second').isoformat()
76     return options
77 
78 
79 def convert_to_pem(der):
80     """
81     Converts DER to PEM Lemur uses PEM internally
82 
83     :param der:
84     :return:
85     """
86     decoded = base64.b64decode(der)
87     return ssl.DER_cert_to_PEM_cert(decoded)
88 
89 
90 def convert_date_to_utc_time(date):
91     """
92     Converts a python `datetime` object to the current date + current time in UTC.
93 
94     :param date:
95     :return:
96     """
97     d = arrow.get(date)
98     return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year)\
99         .replace(microsecond=0)
100 
101 
102 def process_response(response):
103     """
104     Helper function that processes responses from CloudCA.
105 
106     :param response:
107     :return: :raise CloudCAException:
108     """
109     if response.status_code == 200:
110         res = response.json()
111         if res['returnValue'] != 'success':
112             current_app.logger.debug(res)
113             if res.get('data'):
114                 raise CloudCAException(" ".join([res['returnMessage'], res['data']['dryRunResultMessage']]))
115             else:
116                 raise CloudCAException(res['returnMessage'])
117     else:
118         raise CloudCAException("There was an error with your request: {0}".format(response.status_code))
119 
120     return response.json()
121 
122 
123 def get_auth_data(ca_name):
124     """
125     Creates the authentication record needed to authenticate a user request to CloudCA.
126 
127     :param ca_name:
128     :return: :raise CloudCAException:
129     """
130     role = authority_service.get_authority_role(ca_name)
131     if role:
132         return {
133             "authInfo": {
134                 "credType": "password",
135                 "credentials": {
136                     "username": role.username,
137                     "password": role.password  # we only decrypt when we need to
138                 }
139 
140             }
141         }
142 
143     raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
144 
145 
146 class CloudCA(object):
147     def __init__(self, *args, **kwargs):
148         self.session = requests.Session()
149         self.session.mount('https://', CloudCAHostNameCheckingAdapter())
150         self.url = current_app.config.get('CLOUDCA_URL')
151 
152         if current_app.config.get('CLOUDCA_PEM_PATH') and current_app.config.get('CLOUDCA_BUNDLE'):
153             self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
154             self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
155         else:
156             current_app.logger.warning(
157                 "No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA"
158             )
159 
160         super(CloudCA, self).__init__(*args, **kwargs)
161 
162     def post(self, endpoint, data):
163         """
164         HTTP POST to CloudCA
165 
166         :param endpoint:
167         :param data:
168         :return:
169         """
170         data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
171 
172         # we set a low timeout, if cloudca is down it shouldn't bring down
173         # lemur
174         try:
175             response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)
176         except ConnectionError:
177             raise Exception("Could not talk to CloudCA, is it up?")
178 
179         return process_response(response)
180 
181     def get(self, endpoint):
182         """
183         HTTP GET to CloudCA
184 
185         :param endpoint:
186         :return:
187         """
188         try:
189             response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
190         except ConnectionError:
191             raise Exception("Could not talk to CloudCA, is it up?")
192 
193         return process_response(response)
194 
195     def random(self, length=10):
196         """
197         Uses CloudCA as a decent source of randomness.
198 
199         :param length:
200         :return:
201         """
202         endpoint = '/v1/random/{0}'.format(length)
203         response = self.session.get(self.url + endpoint, verify=self.ca_bundle)
204         return response
205 
206     def get_authorities(self):
207         """
208         Retrieves authorities that were made outside of Lemur.
209 
210         :return:
211         """
212         endpoint = '{0}/listCAs'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
213         authorities = []
214         for ca in self.get(endpoint)['data']['caList']:
215             try:
216                 authorities.append(ca['caName'])
217             except AttributeError:
218                 current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
219 
220         return authorities
221 
222 
223 class CloudCAIssuerPlugin(IssuerPlugin, CloudCA):
224     title = 'CloudCA'
225     slug = 'cloudca-issuer'
226     description = 'Enables the creation of certificates from the cloudca API.'
227     version = cloudca.VERSION
228 
229     author = 'Kevin Glisson'
230     author_url = 'https://github.com/netflix/lemur'
231 
232     def create_authority(self, options):
233         """
234         Creates a new certificate authority
235 
236         :param options:
237         :return:
238         """
239         # this is weird and I don't like it
240         endpoint = '{0}/createCA'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
241         options['caDN']['email'] = options['ownerEmail']
242 
243         if options['caType'] == 'subca':
244             options = dict(options.items() + self.auth_data(options['caParent']).items())
245 
246         options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
247         options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()
248 
249         try:
250             response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,
251                                          verify=self.ca_bundle)
252         except ConnectionError:
253             raise Exception("Could not communicate with CloudCA, is it up?")
254 
255         json = process_response(response)
256         roles = []
257 
258         for cred in json['data']['authInfo']:
259             role = {
260                 'username': cred['credentials']['username'],
261                 'password': cred['credentials']['password'],
262                 'name': "_".join([options['caName'], cred['credentials']['username']])
263             }
264             roles.append(role)
265 
266         if options['caType'] == 'subca':
267             cert = convert_to_pem(json['data']['certificate'])
268         else:
269             cert = convert_to_pem(json['data']['rootCertificate'])
270 
271         intermediates = []
272         for i in json['data']['intermediateCertificates']:
273             intermediates.append(convert_to_pem(i))
274 
275         return cert, "".join(intermediates), roles,
276 
277     def create_certificate(self, csr, options):
278         """
279         Creates a new certificate from cloudca
280 
281         If no start and end date are specified the default issue range
282         will be used.
283 
284         :param csr:
285         :param options:
286         """
287         endpoint = '{0}/enroll'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
288         # lets default to two years if it's not specified
289         # we do some last minute data massaging
290         options = get_default_issuance(options)
291 
292         cloudca_options = {
293             'extensions': options['extensions'],
294             'validityStart': convert_date_to_utc_time(options['validityStart']).isoformat(),
295             'validityEnd': convert_date_to_utc_time(options['validityEnd']).isoformat(),
296             'creator': options['creator'],
297             'ownerEmail': options['owner'],
298             'caName': options['authority'].name,
299             'csr': csr,
300             'comment': options['description']
301         }
302 
303         response = self.post(endpoint, remove_none(cloudca_options))
304 
305         # we return a concatenated list of intermediate because that is what aws
306         # expects
307         cert = convert_to_pem(response['data']['certificate'])
308 
309         intermediates = [convert_to_pem(response['data']['rootCertificate'])]
310         for i in response['data']['intermediateCertificates']:
311             intermediates.append(convert_to_pem(i))
312 
313         return cert, "".join(intermediates),
314 
315 
316 class CloudCASourcePlugin(SourcePlugin, CloudCA):
317     title = 'CloudCA'
318     slug = 'cloudca-source'
319     description = 'Discovers all SSL certificates in CloudCA'
320     version = cloudca.VERSION
321 
322     author = 'Kevin Glisson'
323     author_url = 'https://github.com/netflix/lemur'
324 
325     options = {
326         'pollRate': {'type': 'int', 'default': '60'}
327     }
328 
329     def get_certificates(self, options, **kwargs):
330         certs = []
331         for authority in self.get_authorities():
332             certs += self.get_cert(ca_name=authority)
333         return certs
334 
335     def get_cert(self, ca_name=None, cert_handle=None):
336         """
337         Returns a given cert from CloudCA.
338 
339         :param ca_name:
340         :param cert_handle:
341         :return:
342         """
343         endpoint = '{0}/getCert'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))
344         response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10,
345                                      verify=self.ca_bundle)
346         raw = process_response(response)
347 
348         certs = []
349         for c in raw['data']['certList']:
350             cert = convert_to_pem(c['certValue'])
351 
352             intermediates = []
353             for i in c['intermediateCertificates']:
354                 intermediates.append(convert_to_pem(i))
355 
356             certs.append({
357                 'public_certificate': cert,
358                 'intermediate_certificate': "\n".join(intermediates),
359                 'owner': c['ownerEmail']
360             })
361 
362         return certs
363
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lemur/plugins/lemur_cloudca/plugin.py b/lemur/plugins/lemur_cloudca/plugin.py
--- a/lemur/plugins/lemur_cloudca/plugin.py
+++ b/lemur/plugins/lemur_cloudca/plugin.py
@@ -7,6 +7,7 @@
 .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
 
 """
+import re
 import ssl
 import base64
 from json import dumps
@@ -245,6 +246,7 @@
 
         options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
         options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()
+        options['description'] = re.sub(r'[^a-zA-Z0-9]', '', options['caDescription'])
 
         try:
             response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,
@@ -297,7 +299,7 @@
             'ownerEmail': options['owner'],
             'caName': options['authority'].name,
             'csr': csr,
-            'comment': options['description']
+            'comment': re.sub(r'[^a-zA-Z0-9]', '', options['description'])
         }
 
         response = self.post(endpoint, remove_none(cloudca_options))

{"golden_diff": "diff --git a/lemur/plugins/lemur_cloudca/plugin.py b/lemur/plugins/lemur_cloudca/plugin.py\n--- a/lemur/plugins/lemur_cloudca/plugin.py\n+++ b/lemur/plugins/lemur_cloudca/plugin.py\n@@ -7,6 +7,7 @@\n .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>\n \n \"\"\"\n+import re\n import ssl\n import base64\n from json import dumps\n@@ -245,6 +246,7 @@\n \n         options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()\n         options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()\n+        options['description'] = re.sub(r'[^a-zA-Z0-9]', '', options['caDescription'])\n \n         try:\n             response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,\n@@ -297,7 +299,7 @@\n             'ownerEmail': options['owner'],\n             'caName': options['authority'].name,\n             'csr': csr,\n-            'comment': options['description']\n+            'comment': re.sub(r'[^a-zA-Z0-9]', '', options['description'])\n         }\n \n         response = self.post(endpoint, remove_none(cloudca_options))\n", "issue": "Common name field needs additional validation on the UI and API\nIt looks like 64 character limit\n\nhttp://stackoverflow.com/questions/5136198/what-strings-are-allowed-in-the-common-name-attribute-in-an-x-509-certificate\n\n", "before_files": [{"content": "\"\"\"\n.. module: lemur.common.services.issuers.plugins.cloudca\n    :platform: Unix\n    :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more\n    :license: Apache, see LICENSE for more details.\n\n.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>\n\n\"\"\"\nimport ssl\nimport base64\nfrom json import dumps\n\nimport arrow\nimport requests\nfrom requests.adapters import HTTPAdapter\nfrom requests.exceptions import ConnectionError\n\nfrom flask import current_app\n\nfrom lemur.exceptions import LemurException\nfrom lemur.plugins.bases import IssuerPlugin, SourcePlugin\nfrom lemur.plugins import lemur_cloudca as cloudca\n\nfrom lemur.authorities import service as authority_service\n\n\nclass CloudCAException(LemurException):\n    def __init__(self, message):\n        self.message = message\n        current_app.logger.error(self)\n\n    def __str__(self):\n        return repr(\"CloudCA request failed: {0}\".format(self.message))\n\n\nclass CloudCAHostNameCheckingAdapter(HTTPAdapter):\n    def cert_verify(self, conn, url, verify, cert):\n        super(CloudCAHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert)\n        conn.assert_hostname = False\n\n\ndef remove_none(options):\n    \"\"\"\n    Simple function that traverse the options and removed any None items\n    CloudCA really dislikes null values.\n\n    :param options:\n    :return:\n    \"\"\"\n    new_dict = {}\n    for k, v in options.items():\n        if v:\n            new_dict[k] = v\n\n    # this is super hacky and gross, cloudca doesn't like null values\n    if new_dict.get('extensions'):\n        if len(new_dict['extensions']['subAltNames']['names']) == 0:\n            del new_dict['extensions']['subAltNames']\n\n    return new_dict\n\n\ndef get_default_issuance(options):\n    \"\"\"\n    Gets the default time range for certificates\n\n    :param options:\n    :return:\n    \"\"\"\n    if not options.get('validityStart') and not options.get('validityEnd'):\n        start = arrow.utcnow()\n        options['validityStart'] = start.floor('second').isoformat()\n        options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY'))\\\n            .ceil('second').isoformat()\n    return options\n\n\ndef convert_to_pem(der):\n    \"\"\"\n    Converts DER to PEM Lemur uses PEM internally\n\n    :param der:\n    :return:\n    \"\"\"\n    decoded = base64.b64decode(der)\n    return ssl.DER_cert_to_PEM_cert(decoded)\n\n\ndef convert_date_to_utc_time(date):\n    \"\"\"\n    Converts a python `datetime` object to the current date + current time in UTC.\n\n    :param date:\n    :return:\n    \"\"\"\n    d = arrow.get(date)\n    return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year)\\\n        .replace(microsecond=0)\n\n\ndef process_response(response):\n    \"\"\"\n    Helper function that processes responses from CloudCA.\n\n    :param response:\n    :return: :raise CloudCAException:\n    \"\"\"\n    if response.status_code == 200:\n        res = response.json()\n        if res['returnValue'] != 'success':\n            current_app.logger.debug(res)\n            if res.get('data'):\n                raise CloudCAException(\" \".join([res['returnMessage'], res['data']['dryRunResultMessage']]))\n            else:\n                raise CloudCAException(res['returnMessage'])\n    else:\n        raise CloudCAException(\"There was an error with your request: {0}\".format(response.status_code))\n\n    return response.json()\n\n\ndef get_auth_data(ca_name):\n    \"\"\"\n    Creates the authentication record needed to authenticate a user request to CloudCA.\n\n    :param ca_name:\n    :return: :raise CloudCAException:\n    \"\"\"\n    role = authority_service.get_authority_role(ca_name)\n    if role:\n        return {\n            \"authInfo\": {\n                \"credType\": \"password\",\n                \"credentials\": {\n                    \"username\": role.username,\n                    \"password\": role.password  # we only decrypt when we need to\n                }\n\n            }\n        }\n\n    raise CloudCAException(\"You do not have the required role to issue certificates from {0}\".format(ca_name))\n\n\nclass CloudCA(object):\n    def __init__(self, *args, **kwargs):\n        self.session = requests.Session()\n        self.session.mount('https://', CloudCAHostNameCheckingAdapter())\n        self.url = current_app.config.get('CLOUDCA_URL')\n\n        if current_app.config.get('CLOUDCA_PEM_PATH') and current_app.config.get('CLOUDCA_BUNDLE'):\n            self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')\n            self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')\n        else:\n            current_app.logger.warning(\n                \"No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA\"\n            )\n\n        super(CloudCA, self).__init__(*args, **kwargs)\n\n    def post(self, endpoint, data):\n        \"\"\"\n        HTTP POST to CloudCA\n\n        :param endpoint:\n        :param data:\n        :return:\n        \"\"\"\n        data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))\n\n        # we set a low timeout, if cloudca is down it shouldn't bring down\n        # lemur\n        try:\n            response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not talk to CloudCA, is it up?\")\n\n        return process_response(response)\n\n    def get(self, endpoint):\n        \"\"\"\n        HTTP GET to CloudCA\n\n        :param endpoint:\n        :return:\n        \"\"\"\n        try:\n            response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not talk to CloudCA, is it up?\")\n\n        return process_response(response)\n\n    def random(self, length=10):\n        \"\"\"\n        Uses CloudCA as a decent source of randomness.\n\n        :param length:\n        :return:\n        \"\"\"\n        endpoint = '/v1/random/{0}'.format(length)\n        response = self.session.get(self.url + endpoint, verify=self.ca_bundle)\n        return response\n\n    def get_authorities(self):\n        \"\"\"\n        Retrieves authorities that were made outside of Lemur.\n\n        :return:\n        \"\"\"\n        endpoint = '{0}/listCAs'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        authorities = []\n        for ca in self.get(endpoint)['data']['caList']:\n            try:\n                authorities.append(ca['caName'])\n            except AttributeError:\n                current_app.logger.error(\"No authority has been defined for {}\".format(ca['caName']))\n\n        return authorities\n\n\nclass CloudCAIssuerPlugin(IssuerPlugin, CloudCA):\n    title = 'CloudCA'\n    slug = 'cloudca-issuer'\n    description = 'Enables the creation of certificates from the cloudca API.'\n    version = cloudca.VERSION\n\n    author = 'Kevin Glisson'\n    author_url = 'https://github.com/netflix/lemur'\n\n    def create_authority(self, options):\n        \"\"\"\n        Creates a new certificate authority\n\n        :param options:\n        :return:\n        \"\"\"\n        # this is weird and I don't like it\n        endpoint = '{0}/createCA'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        options['caDN']['email'] = options['ownerEmail']\n\n        if options['caType'] == 'subca':\n            options = dict(options.items() + self.auth_data(options['caParent']).items())\n\n        options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()\n        options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()\n\n        try:\n            response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,\n                                         verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not communicate with CloudCA, is it up?\")\n\n        json = process_response(response)\n        roles = []\n\n        for cred in json['data']['authInfo']:\n            role = {\n                'username': cred['credentials']['username'],\n                'password': cred['credentials']['password'],\n                'name': \"_\".join([options['caName'], cred['credentials']['username']])\n            }\n            roles.append(role)\n\n        if options['caType'] == 'subca':\n            cert = convert_to_pem(json['data']['certificate'])\n        else:\n            cert = convert_to_pem(json['data']['rootCertificate'])\n\n        intermediates = []\n        for i in json['data']['intermediateCertificates']:\n            intermediates.append(convert_to_pem(i))\n\n        return cert, \"\".join(intermediates), roles,\n\n    def create_certificate(self, csr, options):\n        \"\"\"\n        Creates a new certificate from cloudca\n\n        If no start and end date are specified the default issue range\n        will be used.\n\n        :param csr:\n        :param options:\n        \"\"\"\n        endpoint = '{0}/enroll'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        # lets default to two years if it's not specified\n        # we do some last minute data massaging\n        options = get_default_issuance(options)\n\n        cloudca_options = {\n            'extensions': options['extensions'],\n            'validityStart': convert_date_to_utc_time(options['validityStart']).isoformat(),\n            'validityEnd': convert_date_to_utc_time(options['validityEnd']).isoformat(),\n            'creator': options['creator'],\n            'ownerEmail': options['owner'],\n            'caName': options['authority'].name,\n            'csr': csr,\n            'comment': options['description']\n        }\n\n        response = self.post(endpoint, remove_none(cloudca_options))\n\n        # we return a concatenated list of intermediate because that is what aws\n        # expects\n        cert = convert_to_pem(response['data']['certificate'])\n\n        intermediates = [convert_to_pem(response['data']['rootCertificate'])]\n        for i in response['data']['intermediateCertificates']:\n            intermediates.append(convert_to_pem(i))\n\n        return cert, \"\".join(intermediates),\n\n\nclass CloudCASourcePlugin(SourcePlugin, CloudCA):\n    title = 'CloudCA'\n    slug = 'cloudca-source'\n    description = 'Discovers all SSL certificates in CloudCA'\n    version = cloudca.VERSION\n\n    author = 'Kevin Glisson'\n    author_url = 'https://github.com/netflix/lemur'\n\n    options = {\n        'pollRate': {'type': 'int', 'default': '60'}\n    }\n\n    def get_certificates(self, options, **kwargs):\n        certs = []\n        for authority in self.get_authorities():\n            certs += self.get_cert(ca_name=authority)\n        return certs\n\n    def get_cert(self, ca_name=None, cert_handle=None):\n        \"\"\"\n        Returns a given cert from CloudCA.\n\n        :param ca_name:\n        :param cert_handle:\n        :return:\n        \"\"\"\n        endpoint = '{0}/getCert'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10,\n                                     verify=self.ca_bundle)\n        raw = process_response(response)\n\n        certs = []\n        for c in raw['data']['certList']:\n            cert = convert_to_pem(c['certValue'])\n\n            intermediates = []\n            for i in c['intermediateCertificates']:\n                intermediates.append(convert_to_pem(i))\n\n            certs.append({\n                'public_certificate': cert,\n                'intermediate_certificate': \"\\n\".join(intermediates),\n                'owner': c['ownerEmail']\n            })\n\n        return certs\n", "path": "lemur/plugins/lemur_cloudca/plugin.py"}], "after_files": [{"content": "\"\"\"\n.. module: lemur.common.services.issuers.plugins.cloudca\n    :platform: Unix\n    :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more\n    :license: Apache, see LICENSE for more details.\n\n.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>\n\n\"\"\"\nimport re\nimport ssl\nimport base64\nfrom json import dumps\n\nimport arrow\nimport requests\nfrom requests.adapters import HTTPAdapter\nfrom requests.exceptions import ConnectionError\n\nfrom flask import current_app\n\nfrom lemur.exceptions import LemurException\nfrom lemur.plugins.bases import IssuerPlugin, SourcePlugin\nfrom lemur.plugins import lemur_cloudca as cloudca\n\nfrom lemur.authorities import service as authority_service\n\n\nclass CloudCAException(LemurException):\n    def __init__(self, message):\n        self.message = message\n        current_app.logger.error(self)\n\n    def __str__(self):\n        return repr(\"CloudCA request failed: {0}\".format(self.message))\n\n\nclass CloudCAHostNameCheckingAdapter(HTTPAdapter):\n    def cert_verify(self, conn, url, verify, cert):\n        super(CloudCAHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert)\n        conn.assert_hostname = False\n\n\ndef remove_none(options):\n    \"\"\"\n    Simple function that traverse the options and removed any None items\n    CloudCA really dislikes null values.\n\n    :param options:\n    :return:\n    \"\"\"\n    new_dict = {}\n    for k, v in options.items():\n        if v:\n            new_dict[k] = v\n\n    # this is super hacky and gross, cloudca doesn't like null values\n    if new_dict.get('extensions'):\n        if len(new_dict['extensions']['subAltNames']['names']) == 0:\n            del new_dict['extensions']['subAltNames']\n\n    return new_dict\n\n\ndef get_default_issuance(options):\n    \"\"\"\n    Gets the default time range for certificates\n\n    :param options:\n    :return:\n    \"\"\"\n    if not options.get('validityStart') and not options.get('validityEnd'):\n        start = arrow.utcnow()\n        options['validityStart'] = start.floor('second').isoformat()\n        options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY'))\\\n            .ceil('second').isoformat()\n    return options\n\n\ndef convert_to_pem(der):\n    \"\"\"\n    Converts DER to PEM Lemur uses PEM internally\n\n    :param der:\n    :return:\n    \"\"\"\n    decoded = base64.b64decode(der)\n    return ssl.DER_cert_to_PEM_cert(decoded)\n\n\ndef convert_date_to_utc_time(date):\n    \"\"\"\n    Converts a python `datetime` object to the current date + current time in UTC.\n\n    :param date:\n    :return:\n    \"\"\"\n    d = arrow.get(date)\n    return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year)\\\n        .replace(microsecond=0)\n\n\ndef process_response(response):\n    \"\"\"\n    Helper function that processes responses from CloudCA.\n\n    :param response:\n    :return: :raise CloudCAException:\n    \"\"\"\n    if response.status_code == 200:\n        res = response.json()\n        if res['returnValue'] != 'success':\n            current_app.logger.debug(res)\n            if res.get('data'):\n                raise CloudCAException(\" \".join([res['returnMessage'], res['data']['dryRunResultMessage']]))\n            else:\n                raise CloudCAException(res['returnMessage'])\n    else:\n        raise CloudCAException(\"There was an error with your request: {0}\".format(response.status_code))\n\n    return response.json()\n\n\ndef get_auth_data(ca_name):\n    \"\"\"\n    Creates the authentication record needed to authenticate a user request to CloudCA.\n\n    :param ca_name:\n    :return: :raise CloudCAException:\n    \"\"\"\n    role = authority_service.get_authority_role(ca_name)\n    if role:\n        return {\n            \"authInfo\": {\n                \"credType\": \"password\",\n                \"credentials\": {\n                    \"username\": role.username,\n                    \"password\": role.password  # we only decrypt when we need to\n                }\n\n            }\n        }\n\n    raise CloudCAException(\"You do not have the required role to issue certificates from {0}\".format(ca_name))\n\n\nclass CloudCA(object):\n    def __init__(self, *args, **kwargs):\n        self.session = requests.Session()\n        self.session.mount('https://', CloudCAHostNameCheckingAdapter())\n        self.url = current_app.config.get('CLOUDCA_URL')\n\n        if current_app.config.get('CLOUDCA_PEM_PATH') and current_app.config.get('CLOUDCA_BUNDLE'):\n            self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')\n            self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')\n        else:\n            current_app.logger.warning(\n                \"No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA\"\n            )\n\n        super(CloudCA, self).__init__(*args, **kwargs)\n\n    def post(self, endpoint, data):\n        \"\"\"\n        HTTP POST to CloudCA\n\n        :param endpoint:\n        :param data:\n        :return:\n        \"\"\"\n        data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))\n\n        # we set a low timeout, if cloudca is down it shouldn't bring down\n        # lemur\n        try:\n            response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not talk to CloudCA, is it up?\")\n\n        return process_response(response)\n\n    def get(self, endpoint):\n        \"\"\"\n        HTTP GET to CloudCA\n\n        :param endpoint:\n        :return:\n        \"\"\"\n        try:\n            response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not talk to CloudCA, is it up?\")\n\n        return process_response(response)\n\n    def random(self, length=10):\n        \"\"\"\n        Uses CloudCA as a decent source of randomness.\n\n        :param length:\n        :return:\n        \"\"\"\n        endpoint = '/v1/random/{0}'.format(length)\n        response = self.session.get(self.url + endpoint, verify=self.ca_bundle)\n        return response\n\n    def get_authorities(self):\n        \"\"\"\n        Retrieves authorities that were made outside of Lemur.\n\n        :return:\n        \"\"\"\n        endpoint = '{0}/listCAs'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        authorities = []\n        for ca in self.get(endpoint)['data']['caList']:\n            try:\n                authorities.append(ca['caName'])\n            except AttributeError:\n                current_app.logger.error(\"No authority has been defined for {}\".format(ca['caName']))\n\n        return authorities\n\n\nclass CloudCAIssuerPlugin(IssuerPlugin, CloudCA):\n    title = 'CloudCA'\n    slug = 'cloudca-issuer'\n    description = 'Enables the creation of certificates from the cloudca API.'\n    version = cloudca.VERSION\n\n    author = 'Kevin Glisson'\n    author_url = 'https://github.com/netflix/lemur'\n\n    def create_authority(self, options):\n        \"\"\"\n        Creates a new certificate authority\n\n        :param options:\n        :return:\n        \"\"\"\n        # this is weird and I don't like it\n        endpoint = '{0}/createCA'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        options['caDN']['email'] = options['ownerEmail']\n\n        if options['caType'] == 'subca':\n            options = dict(options.items() + self.auth_data(options['caParent']).items())\n\n        options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()\n        options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()\n        options['description'] = re.sub(r'[^a-zA-Z0-9]', '', options['caDescription'])\n\n        try:\n            response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,\n                                         verify=self.ca_bundle)\n        except ConnectionError:\n            raise Exception(\"Could not communicate with CloudCA, is it up?\")\n\n        json = process_response(response)\n        roles = []\n\n        for cred in json['data']['authInfo']:\n            role = {\n                'username': cred['credentials']['username'],\n                'password': cred['credentials']['password'],\n                'name': \"_\".join([options['caName'], cred['credentials']['username']])\n            }\n            roles.append(role)\n\n        if options['caType'] == 'subca':\n            cert = convert_to_pem(json['data']['certificate'])\n        else:\n            cert = convert_to_pem(json['data']['rootCertificate'])\n\n        intermediates = []\n        for i in json['data']['intermediateCertificates']:\n            intermediates.append(convert_to_pem(i))\n\n        return cert, \"\".join(intermediates), roles,\n\n    def create_certificate(self, csr, options):\n        \"\"\"\n        Creates a new certificate from cloudca\n\n        If no start and end date are specified the default issue range\n        will be used.\n\n        :param csr:\n        :param options:\n        \"\"\"\n        endpoint = '{0}/enroll'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        # lets default to two years if it's not specified\n        # we do some last minute data massaging\n        options = get_default_issuance(options)\n\n        cloudca_options = {\n            'extensions': options['extensions'],\n            'validityStart': convert_date_to_utc_time(options['validityStart']).isoformat(),\n            'validityEnd': convert_date_to_utc_time(options['validityEnd']).isoformat(),\n            'creator': options['creator'],\n            'ownerEmail': options['owner'],\n            'caName': options['authority'].name,\n            'csr': csr,\n            'comment': re.sub(r'[^a-zA-Z0-9]', '', options['description'])\n        }\n\n        response = self.post(endpoint, remove_none(cloudca_options))\n\n        # we return a concatenated list of intermediate because that is what aws\n        # expects\n        cert = convert_to_pem(response['data']['certificate'])\n\n        intermediates = [convert_to_pem(response['data']['rootCertificate'])]\n        for i in response['data']['intermediateCertificates']:\n            intermediates.append(convert_to_pem(i))\n\n        return cert, \"\".join(intermediates),\n\n\nclass CloudCASourcePlugin(SourcePlugin, CloudCA):\n    title = 'CloudCA'\n    slug = 'cloudca-source'\n    description = 'Discovers all SSL certificates in CloudCA'\n    version = cloudca.VERSION\n\n    author = 'Kevin Glisson'\n    author_url = 'https://github.com/netflix/lemur'\n\n    options = {\n        'pollRate': {'type': 'int', 'default': '60'}\n    }\n\n    def get_certificates(self, options, **kwargs):\n        certs = []\n        for authority in self.get_authorities():\n            certs += self.get_cert(ca_name=authority)\n        return certs\n\n    def get_cert(self, ca_name=None, cert_handle=None):\n        \"\"\"\n        Returns a given cert from CloudCA.\n\n        :param ca_name:\n        :param cert_handle:\n        :return:\n        \"\"\"\n        endpoint = '{0}/getCert'.format(current_app.config.get('CLOUDCA_API_ENDPOINT'))\n        response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10,\n                                     verify=self.ca_bundle)\n        raw = process_response(response)\n\n        certs = []\n        for c in raw['data']['certList']:\n            cert = convert_to_pem(c['certValue'])\n\n            intermediates = []\n            for i in c['intermediateCertificates']:\n                intermediates.append(convert_to_pem(i))\n\n            certs.append({\n                'public_certificate': cert,\n                'intermediate_certificate': \"\\n\".join(intermediates),\n                'owner': c['ownerEmail']\n            })\n\n        return certs\n", "path": "lemur/plugins/lemur_cloudca/plugin.py"}]}