Merge pull request #30 from kevgliss/py3

Making Lemur py3 compatible
This commit is contained in:
kevgliss 2015-08-03 21:20:59 -07:00
commit 3ebbbd21e7
16 changed files with 81 additions and 87 deletions

View File

@ -1,23 +1,36 @@
sudo: false sudo: false
language: python language: python
addons: addons:
postgresql: "9.4" postgresql: "9.4"
python:
- "2.7" matrix:
include:
- python: "2.7"
env: TOXENV=py27
- python: "3.3"
env: TOXENV=py33
- python: "3.4"
env: TOXENV=py34
cache: cache:
directories: directories:
- node_modules - node_modules
- .pip_download_cache - .pip_download_cache
- "$HOME/virtualenv/python2.7.9"
env: env:
global: global:
- PIP_DOWNLOAD_CACHE=".pip_download_cache" - PIP_DOWNLOAD_CACHE=".pip_download_cache"
install: install:
- make dev-postgres - make dev-postgres
before_script: before_script:
- psql -c "create database lemur;" -U postgres - psql -c "create database lemur;" -U postgres
- psql -c "create user lemur with password 'lemur;'" -U postgres - psql -c "create user lemur with password 'lemur;'" -U postgres
- npm install -g bower - npm install -g bower
script: script:
- make test - make test

View File

@ -1,5 +1,5 @@
""" """
.. module: permissions .. module: lemur.auth.permissions
:platform: Unix :platform: Unix
:synopsis: This module defines all the permission used within Lemur :synopsis: This module defines all the permission used within Lemur
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
@ -24,15 +24,15 @@ CertificateOwnerNeed = partial(CertificateOwner, 'certificateView')
class ViewKeyPermission(Permission): class ViewKeyPermission(Permission):
def __init__(self, role_id, certificate_id): def __init__(self, role_id, certificate_id):
c_need = CertificateCreatorNeed(unicode(certificate_id)) c_need = CertificateCreatorNeed(str(certificate_id))
o_need = CertificateOwnerNeed(unicode(role_id)) o_need = CertificateOwnerNeed(str(role_id))
super(ViewKeyPermission, self).__init__(o_need, c_need, RoleNeed('admin')) super(ViewKeyPermission, self).__init__(o_need, c_need, RoleNeed('admin'))
class UpdateCertificatePermission(Permission): class UpdateCertificatePermission(Permission):
def __init__(self, role_id, certificate_id): def __init__(self, role_id, certificate_id):
c_need = CertificateCreatorNeed(unicode(certificate_id)) c_need = CertificateCreatorNeed(str(certificate_id))
o_need = CertificateOwnerNeed(unicode(role_id)) o_need = CertificateOwnerNeed(str(role_id))
super(UpdateCertificatePermission, self).__init__(o_need, c_need, RoleNeed('admin')) super(UpdateCertificatePermission, self).__init__(o_need, c_need, RoleNeed('admin'))
@ -42,7 +42,7 @@ ViewRoleCredentialsNeed = partial(RoleUser, 'roleView')
class ViewRoleCredentialsPermission(Permission): class ViewRoleCredentialsPermission(Permission):
def __init__(self, role_id): def __init__(self, role_id):
need = ViewRoleCredentialsNeed(unicode(role_id)) need = ViewRoleCredentialsNeed(str(role_id))
super(ViewRoleCredentialsPermission, self).__init__(need, RoleNeed('admin')) super(ViewRoleCredentialsPermission, self).__init__(need, RoleNeed('admin'))
@ -55,8 +55,8 @@ AuthorityOwnerNeed = partial(AuthorityOwner, 'role')
class AuthorityPermission(Permission): class AuthorityPermission(Permission):
def __init__(self, authority_id, roles): def __init__(self, authority_id, roles):
needs = [RoleNeed('admin'), AuthorityCreatorNeed(unicode(authority_id))] needs = [RoleNeed('admin'), AuthorityCreatorNeed(str(authority_id))]
for r in roles: for r in roles:
needs.append(AuthorityOwnerNeed(unicode(r))) needs.append(AuthorityOwnerNeed(str(r)))
super(AuthorityPermission, self).__init__(*needs) super(AuthorityPermission, self).__init__(*needs)

View File

@ -12,6 +12,8 @@ import jwt
import json import json
import base64 import base64
import binascii import binascii
from builtins import str
from functools import wraps from functools import wraps
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -32,7 +34,7 @@ from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed,
def base64url_decode(data): def base64url_decode(data):
if isinstance(data, unicode): if isinstance(data, str):
data = str(data) data = str(data)
rem = len(data) % 4 rem = len(data) % 4
@ -139,7 +141,9 @@ def fetch_token_header(token):
try: try:
return json.loads(base64url_decode(header_segment)) return json.loads(base64url_decode(header_segment))
except TypeError, binascii.Error: except TypeError:
raise jwt.DecodeError('Invalid header padding')
except binascii.Error:
raise jwt.DecodeError('Invalid header padding') raise jwt.DecodeError('Invalid header padding')
@ -161,19 +165,19 @@ def on_identity_loaded(sender, identity):
# identity with the roles that the user provides # identity with the roles that the user provides
if hasattr(user, 'roles'): if hasattr(user, 'roles'):
for role in user.roles: for role in user.roles:
identity.provides.add(CertificateOwnerNeed(unicode(role.id))) identity.provides.add(CertificateOwnerNeed(role.id))
identity.provides.add(ViewRoleCredentialsNeed(unicode(role.id))) identity.provides.add(ViewRoleCredentialsNeed(role.id))
identity.provides.add(RoleNeed(role.name)) identity.provides.add(RoleNeed(role.name))
# apply ownership for authorities # apply ownership for authorities
if hasattr(user, 'authorities'): if hasattr(user, 'authorities'):
for authority in user.authorities: for authority in user.authorities:
identity.provides.add(AuthorityCreatorNeed(unicode(authority.id))) identity.provides.add(AuthorityCreatorNeed(authority.id))
# apply ownership of certificates # apply ownership of certificates
if hasattr(user, 'certificates'): if hasattr(user, 'certificates'):
for certificate in user.certificates: for certificate in user.certificates:
identity.provides.add(CertificateCreatorNeed(unicode(certificate.id))) identity.provides.add(CertificateCreatorNeed(certificate.id))
g.user = user g.user = user

View File

@ -7,6 +7,7 @@
""" """
import os import os
import datetime import datetime
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -56,7 +57,10 @@ def create_name(issuer, not_before, not_after, subject, san):
disallowed_chars = disallowed_chars.replace("-", "") disallowed_chars = disallowed_chars.replace("-", "")
disallowed_chars = disallowed_chars.replace(".", "") disallowed_chars = disallowed_chars.replace(".", "")
temp = temp.replace('*', "WILDCARD") temp = temp.replace('*', "WILDCARD")
temp = temp.translate(None, disallowed_chars)
for c in disallowed_chars:
temp = temp.replace(c, "")
# white space is silly too # white space is silly too
return temp.replace(" ", "-") return temp.replace(" ", "-")
@ -151,7 +155,9 @@ def cert_get_issuer(cert):
delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum()) delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
try: try:
issuer = str(cert.issuer.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)[0].value) issuer = str(cert.issuer.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)[0].value)
return issuer.translate(None, delchars) for c in delchars:
issuer = issuer.replace(c, "")
return issuer
except Exception as e: except Exception as e:
current_app.logger.error("Unable to get issuer! {0}".format(e)) current_app.logger.error("Unable to get issuer! {0}".format(e))

View File

@ -304,15 +304,15 @@ def create_csr(csr_config):
backend=default_backend() backend=default_backend()
) )
# TODO When we figure out a better way to validate these options they should be parsed as unicode # TODO When we figure out a better way to validate these options they should be parsed as str
builder = x509.CertificateSigningRequestBuilder() builder = x509.CertificateSigningRequestBuilder()
builder = builder.subject_name(x509.Name([ builder = builder.subject_name(x509.Name([
x509.NameAttribute(x509.OID_COMMON_NAME, unicode(csr_config['commonName'])), x509.NameAttribute(x509.OID_COMMON_NAME, csr_config['commonName']),
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, unicode(csr_config['organization'])), x509.NameAttribute(x509.OID_ORGANIZATION_NAME, csr_config['organization']),
x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, unicode(csr_config['organizationalUnit'])), x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, csr_config['organizationalUnit']),
x509.NameAttribute(x509.OID_COUNTRY_NAME, unicode(csr_config['country'])), x509.NameAttribute(x509.OID_COUNTRY_NAME, csr_config['country']),
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, unicode(csr_config['state'])), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, csr_config['state']),
x509.NameAttribute(x509.OID_LOCALITY_NAME, unicode(csr_config['location'])), x509.NameAttribute(x509.OID_LOCALITY_NAME, csr_config['location']),
])) ]))
builder = builder.add_extension( builder = builder.add_extension(

View File

@ -5,6 +5,8 @@
:license: Apache, see LICENSE for more details. :license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com> .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
""" """
from builtins import str
from flask import Blueprint, make_response, jsonify from flask import Blueprint, make_response, jsonify
from flask.ext.restful import reqparse, Api, fields from flask.ext.restful import reqparse, Api, fields
@ -75,7 +77,7 @@ def pem_str(value, name):
:return: :raise ValueError: :return: :raise ValueError:
""" """
try: try:
x509.load_pem_x509_certificate(str(value), default_backend()) x509.load_pem_x509_certificate(bytes(value), default_backend())
except Exception: except Exception:
raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name)) raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name))
return value return value
@ -90,7 +92,7 @@ def private_key_str(value, name):
:return: :raise ValueError: :return: :raise ValueError:
""" """
try: try:
serialization.load_pem_private_key(str(value), None, backend=default_backend()) serialization.load_pem_private_key(bytes(value), None, backend=default_backend())
except Exception: except Exception:
raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name)) raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name))
return value return value

View File

@ -65,7 +65,7 @@ class marshal_items(object):
else: else:
return {'message': 'unknown'}, 400 return {'message': 'unknown'}, 400
else: else:
return {'message': e.message}, 400 return {'message': str(e)}, 400
return wrapper return wrapper

View File

@ -3,6 +3,8 @@
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details. :license: Apache, see LICENSE for more details.
""" """
from builtins import str
from datetime import timedelta from datetime import timedelta
from flask import make_response, request, current_app from flask import make_response, request, current_app
@ -16,10 +18,10 @@ def crossdomain(origin=None, methods=None, headers=None,
if methods is not None: if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods)) methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring): if headers is not None and not isinstance(headers, str):
headers = ', '.join(x.upper() for x in headers) headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring): if not isinstance(origin, str):
origin = ', '.join(origin) origin = ', '.join(origin)
if isinstance(max_age, timedelta): if isinstance(max_age, timedelta):

View File

@ -1,5 +1,5 @@
try: try:
VERSION = __import__('pkg_resources') \ VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version .get_distribution(__name__).version
except Exception, e: except Exception as e:
VERSION = 'unknown' VERSION = 'unknown'

View File

@ -1,5 +1,5 @@
try: try:
VERSION = __import__('pkg_resources') \ VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version .get_distribution(__name__).version
except Exception, e: except Exception as e:
VERSION = 'unknown' VERSION = 'unknown'

View File

@ -1,5 +1,5 @@
try: try:
VERSION = __import__('pkg_resources') \ VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version .get_distribution(__name__).version
except Exception, e: except Exception as e:
VERSION = 'unknown' VERSION = 'unknown'

View File

@ -1,5 +1,5 @@
try: try:
VERSION = __import__('pkg_resources') \ VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version .get_distribution(__name__).version
except Exception, e: except Exception as e:
VERSION = 'unknown' VERSION = 'unknown'

View File

@ -1,7 +1,7 @@
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
INTERNAL_VALID_LONG_STR = """ INTERNAL_VALID_LONG_STR = b"""
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIID1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMx MIID1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAkNBMRAwDgYDVQQHDAdBIHBsYWNlMRcwFQYDVQQDDA5sb25nLmxp CzAJBgNVBAgMAkNBMRAwDgYDVQQHDAdBIHBsYWNlMRcwFQYDVQQDDA5sb25nLmxp
@ -29,7 +29,7 @@ h0S8LN4iv/+vNFPNiM1z9X/SZgfbwZXrLsSi
INTERNAL_VALID_LONG_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_LONG_STR, default_backend()) INTERNAL_VALID_LONG_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_LONG_STR, default_backend())
INTERNAL_INVALID_STR = """ INTERNAL_INVALID_STR = b"""
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
@ -58,7 +58,7 @@ kP+oGWtHvhteUAe8Gloo5NchZJ0/BqlYRCD5aAHcmbXRsDid9mO4ADU=
INTERNAL_INVALID_CERT = x509.load_pem_x509_certificate(INTERNAL_INVALID_STR, default_backend()) INTERNAL_INVALID_CERT = x509.load_pem_x509_certificate(INTERNAL_INVALID_STR, default_backend())
INTERNAL_VALID_SAN_STR = """ INTERNAL_VALID_SAN_STR = b"""
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIESjCCAzKgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MIIESjCCAzKgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
@ -88,7 +88,7 @@ YBrY/duF15YpoMKAlFhDBh6R9/nb5kI2n3pY6I5h6LEYfLStazXbIu61M8zu9TM/
INTERNAL_VALID_SAN_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_SAN_STR, default_backend()) INTERNAL_VALID_SAN_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_SAN_STR, default_backend())
INTERNAL_VALID_WILDCARD_STR = """ INTERNAL_VALID_WILDCARD_STR = b"""
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIEHDCCAwSgAwIBAgICA+owDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MIIEHDCCAwSgAwIBAgICA+owDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s
@ -117,7 +117,7 @@ S0Xb3ZauZJQI7OdHeUPDRVq+8hcG77sopN9pEYrIH08oxvLX2US3GqrowjOxthRa
INTERNAL_VALID_WILDCARD_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_WILDCARD_STR, default_backend()) INTERNAL_VALID_WILDCARD_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_WILDCARD_STR, default_backend())
EXTERNAL_VALID_STR = """ EXTERNAL_VALID_STR = b"""
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFHzCCBAegAwIBAgIQGFWCciDWzbOej/TbAJN0WzANBgkqhkiG9w0BAQsFADCB MIIFHzCCBAegAwIBAgIQGFWCciDWzbOej/TbAJN0WzANBgkqhkiG9w0BAQsFADCB
pDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w pDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w
@ -152,7 +152,7 @@ Bs63gULVCqWygt5KEbv990m/XGuRMaXuHzHCHB4v5LRM30FiFmqCzyD8d+btzW9B
EXTERNAL_CERT = x509.load_pem_x509_certificate(EXTERNAL_VALID_STR, default_backend()) EXTERNAL_CERT = x509.load_pem_x509_certificate(EXTERNAL_VALID_STR, default_backend())
PRIVATE_KEY_STR = """ PRIVATE_KEY_STR = b"""
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAnEjM0cQevlDjT6mDMtTo8N1ovAyKbfVEp0ketCPC4hLkStms MIIEpAIBAAKCAQEAnEjM0cQevlDjT6mDMtTo8N1ovAyKbfVEp0ketCPC4hLkStms
q9ETIyyerARIMv4SEhKqS4E7HIg6ccGkwv1ja5E/b2jHMH4ht1dEXnfM2yh0Mwvk q9ETIyyerARIMv4SEhKqS4E7HIg6ccGkwv1ja5E/b2jHMH4ht1dEXnfM2yh0Mwvk
@ -181,41 +181,3 @@ t5Gpocpt77LJnNiszXSerj/KjX2MflY5xUXeekWowLVTBOK5+CZ8+XBIgBt1hIG3
XKxcRgm/Va4QMEAnec0qXfdTVJaJiAW0bdKwKRRrrbwcTdNRGibdng== XKxcRgm/Va4QMEAnec0qXfdTVJaJiAW0bdKwKRRrrbwcTdNRGibdng==
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
""" """
CSR_CONFIG = """
# Configuration for standard CSR generation for Netflix
# Used for procuring VeriSign certificates
# Author: jbob
# Contact: security@example.com
[ req ]
# Use a 2048 bit private key
default_bits = 2048
default_keyfile = key.pem
prompt = no
encrypt_key = no
# base request
distinguished_name = req_distinguished_name
# extensions
# Uncomment the following line if you are requesting a SAN cert
#req_extensions = req_ext
# distinguished_name
[ req_distinguished_name ]
countryName = "US" # C=
stateOrProvinceName = "CALIFORNIA" # ST=
localityName = "A place" # L=
organizationName = "Example, Inc." # O=
organizationalUnitName = "Operations" # OU=
# This is the hostname/subject name on the certificate
commonName = "example.net" # CN=
[ req_ext ]
# Uncomment the following line if you are requesting a SAN cert
#subjectAltName = @alt_names
[alt_names]
# Put your SANs here
"""

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals # at top of module
import pytest import pytest
from lemur.certificates.views import * # noqa from lemur.certificates.views import * # noqa
@ -21,13 +23,13 @@ def test_private_key_str():
def test_create_basic_csr(): def test_create_basic_csr():
from lemur.certificates.service import create_csr from lemur.certificates.service import create_csr
csr_config = dict( csr_config = dict(
commonName=u'example.com', commonName='example.com',
organization=u'Example, Inc.', organization='Example, Inc.',
organizationalUnit=u'Operations', organizationalUnit='Operations',
country=u'US', country='US',
state=u'CA', state='CA',
location=u'A place', location='A place',
extensions=dict(names=dict(subAltNames=[u'test.example.com', u'test2.example.com'])) extensions=dict(names=dict(subAltNames=['test.example.com', 'test2.example.com']))
) )
csr, pem = create_csr(csr_config) csr, pem = create_csr(csr_config)

View File

@ -43,7 +43,8 @@ install_requires = [
'pyopenssl==0.15.1', 'pyopenssl==0.15.1',
'pyjwt==1.0.1', 'pyjwt==1.0.1',
'xmltodict==0.9.2', 'xmltodict==0.9.2',
'lockfile==0.10.2' 'lockfile==0.10.2',
'future==0.15.0',
] ]
tests_require = [ tests_require = [

2
tox.ini Normal file
View File

@ -0,0 +1,2 @@
[tox]
envlist = py27,pypy,py33,py34