diff --git a/.travis.yml b/.travis.yml index d4d11eaa..a0fe4917 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,36 @@ sudo: false + language: python + addons: 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: directories: - node_modules - .pip_download_cache - - "$HOME/virtualenv/python2.7.9" + env: global: - PIP_DOWNLOAD_CACHE=".pip_download_cache" + install: - make dev-postgres + before_script: - psql -c "create database lemur;" -U postgres - psql -c "create user lemur with password 'lemur;'" -U postgres - npm install -g bower + script: - make test diff --git a/lemur/auth/permissions.py b/lemur/auth/permissions.py index 137b650b..7b311874 100644 --- a/lemur/auth/permissions.py +++ b/lemur/auth/permissions.py @@ -1,5 +1,5 @@ """ -.. module: permissions +.. module: lemur.auth.permissions :platform: Unix :synopsis: This module defines all the permission used within Lemur :copyright: (c) 2015 by Netflix Inc., see AUTHORS for more @@ -24,15 +24,15 @@ CertificateOwnerNeed = partial(CertificateOwner, 'certificateView') class ViewKeyPermission(Permission): def __init__(self, role_id, certificate_id): - c_need = CertificateCreatorNeed(unicode(certificate_id)) - o_need = CertificateOwnerNeed(unicode(role_id)) + c_need = CertificateCreatorNeed(str(certificate_id)) + o_need = CertificateOwnerNeed(str(role_id)) super(ViewKeyPermission, self).__init__(o_need, c_need, RoleNeed('admin')) class UpdateCertificatePermission(Permission): def __init__(self, role_id, certificate_id): - c_need = CertificateCreatorNeed(unicode(certificate_id)) - o_need = CertificateOwnerNeed(unicode(role_id)) + c_need = CertificateCreatorNeed(str(certificate_id)) + o_need = CertificateOwnerNeed(str(role_id)) super(UpdateCertificatePermission, self).__init__(o_need, c_need, RoleNeed('admin')) @@ -42,7 +42,7 @@ ViewRoleCredentialsNeed = partial(RoleUser, 'roleView') class ViewRoleCredentialsPermission(Permission): def __init__(self, role_id): - need = ViewRoleCredentialsNeed(unicode(role_id)) + need = ViewRoleCredentialsNeed(str(role_id)) super(ViewRoleCredentialsPermission, self).__init__(need, RoleNeed('admin')) @@ -55,8 +55,8 @@ AuthorityOwnerNeed = partial(AuthorityOwner, 'role') class AuthorityPermission(Permission): def __init__(self, authority_id, roles): - needs = [RoleNeed('admin'), AuthorityCreatorNeed(unicode(authority_id))] + needs = [RoleNeed('admin'), AuthorityCreatorNeed(str(authority_id))] for r in roles: - needs.append(AuthorityOwnerNeed(unicode(r))) + needs.append(AuthorityOwnerNeed(str(r))) super(AuthorityPermission, self).__init__(*needs) diff --git a/lemur/auth/service.py b/lemur/auth/service.py index 807404b9..6f4e29b5 100644 --- a/lemur/auth/service.py +++ b/lemur/auth/service.py @@ -12,6 +12,8 @@ import jwt import json import base64 import binascii +from builtins import str + from functools import wraps from datetime import datetime, timedelta @@ -32,7 +34,7 @@ from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed, def base64url_decode(data): - if isinstance(data, unicode): + if isinstance(data, str): data = str(data) rem = len(data) % 4 @@ -139,7 +141,9 @@ def fetch_token_header(token): try: 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') @@ -161,19 +165,19 @@ def on_identity_loaded(sender, identity): # identity with the roles that the user provides if hasattr(user, 'roles'): for role in user.roles: - identity.provides.add(CertificateOwnerNeed(unicode(role.id))) - identity.provides.add(ViewRoleCredentialsNeed(unicode(role.id))) + identity.provides.add(CertificateOwnerNeed(role.id)) + identity.provides.add(ViewRoleCredentialsNeed(role.id)) identity.provides.add(RoleNeed(role.name)) # apply ownership for authorities if hasattr(user, 'authorities'): for authority in user.authorities: - identity.provides.add(AuthorityCreatorNeed(unicode(authority.id))) + identity.provides.add(AuthorityCreatorNeed(authority.id)) # apply ownership of certificates if hasattr(user, 'certificates'): for certificate in user.certificates: - identity.provides.add(CertificateCreatorNeed(unicode(certificate.id))) + identity.provides.add(CertificateCreatorNeed(certificate.id)) g.user = user diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index 83f3f690..8c254024 100644 --- a/lemur/certificates/models.py +++ b/lemur/certificates/models.py @@ -7,6 +7,7 @@ """ import os import datetime + from cryptography import x509 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(".", "") temp = temp.replace('*', "WILDCARD") - temp = temp.translate(None, disallowed_chars) + + for c in disallowed_chars: + temp = temp.replace(c, "") + # white space is silly too 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()) try: 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: current_app.logger.error("Unable to get issuer! {0}".format(e)) diff --git a/lemur/certificates/service.py b/lemur/certificates/service.py index 886ef165..a49e69b8 100644 --- a/lemur/certificates/service.py +++ b/lemur/certificates/service.py @@ -304,15 +304,15 @@ def create_csr(csr_config): 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 = builder.subject_name(x509.Name([ - x509.NameAttribute(x509.OID_COMMON_NAME, unicode(csr_config['commonName'])), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, unicode(csr_config['organization'])), - x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, unicode(csr_config['organizationalUnit'])), - x509.NameAttribute(x509.OID_COUNTRY_NAME, unicode(csr_config['country'])), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, unicode(csr_config['state'])), - x509.NameAttribute(x509.OID_LOCALITY_NAME, unicode(csr_config['location'])), + x509.NameAttribute(x509.OID_COMMON_NAME, csr_config['commonName']), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, csr_config['organization']), + x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, csr_config['organizationalUnit']), + x509.NameAttribute(x509.OID_COUNTRY_NAME, csr_config['country']), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, csr_config['state']), + x509.NameAttribute(x509.OID_LOCALITY_NAME, csr_config['location']), ])) builder = builder.add_extension( diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index db8772aa..eaf0c53d 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -5,6 +5,8 @@ :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson """ +from builtins import str + from flask import Blueprint, make_response, jsonify from flask.ext.restful import reqparse, Api, fields @@ -75,7 +77,7 @@ def pem_str(value, name): :return: :raise ValueError: """ try: - x509.load_pem_x509_certificate(str(value), default_backend()) + x509.load_pem_x509_certificate(bytes(value), default_backend()) except Exception: raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name)) return value @@ -90,7 +92,7 @@ def private_key_str(value, name): :return: :raise ValueError: """ 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: raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name)) return value diff --git a/lemur/common/utils.py b/lemur/common/utils.py index 8380c579..4b2d0a00 100644 --- a/lemur/common/utils.py +++ b/lemur/common/utils.py @@ -65,7 +65,7 @@ class marshal_items(object): else: return {'message': 'unknown'}, 400 else: - return {'message': e.message}, 400 + return {'message': str(e)}, 400 return wrapper diff --git a/lemur/tests/certs.py b/lemur/tests/certs.py index d71d2064..1704fca9 100644 --- a/lemur/tests/certs.py +++ b/lemur/tests/certs.py @@ -1,7 +1,7 @@ from cryptography import x509 from cryptography.hazmat.backends import default_backend -INTERNAL_VALID_LONG_STR = """ +INTERNAL_VALID_LONG_STR = b""" -----BEGIN CERTIFICATE----- MIID1zCCAr+gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkNBMRAwDgYDVQQHDAdBIHBsYWNlMRcwFQYDVQQDDA5sb25nLmxp @@ -29,7 +29,7 @@ h0S8LN4iv/+vNFPNiM1z9X/SZgfbwZXrLsSi INTERNAL_VALID_LONG_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_LONG_STR, default_backend()) -INTERNAL_INVALID_STR = """ +INTERNAL_INVALID_STR = b""" -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s @@ -58,7 +58,7 @@ kP+oGWtHvhteUAe8Gloo5NchZJ0/BqlYRCD5aAHcmbXRsDid9mO4ADU= INTERNAL_INVALID_CERT = x509.load_pem_x509_certificate(INTERNAL_INVALID_STR, default_backend()) -INTERNAL_VALID_SAN_STR = """ +INTERNAL_VALID_SAN_STR = b""" -----BEGIN CERTIFICATE----- MIIESjCCAzKgAwIBAgICA+kwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT 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_WILDCARD_STR = """ +INTERNAL_VALID_WILDCARD_STR = b""" -----BEGIN CERTIFICATE----- MIIEHDCCAwSgAwIBAgICA+owDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s @@ -117,7 +117,7 @@ S0Xb3ZauZJQI7OdHeUPDRVq+8hcG77sopN9pEYrIH08oxvLX2US3GqrowjOxthRa INTERNAL_VALID_WILDCARD_CERT = x509.load_pem_x509_certificate(INTERNAL_VALID_WILDCARD_STR, default_backend()) -EXTERNAL_VALID_STR = """ +EXTERNAL_VALID_STR = b""" -----BEGIN CERTIFICATE----- MIIFHzCCBAegAwIBAgIQGFWCciDWzbOej/TbAJN0WzANBgkqhkiG9w0BAQsFADCB pDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w @@ -152,7 +152,7 @@ Bs63gULVCqWygt5KEbv990m/XGuRMaXuHzHCHB4v5LRM30FiFmqCzyD8d+btzW9B EXTERNAL_CERT = x509.load_pem_x509_certificate(EXTERNAL_VALID_STR, default_backend()) -PRIVATE_KEY_STR = """ +PRIVATE_KEY_STR = b""" -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAnEjM0cQevlDjT6mDMtTo8N1ovAyKbfVEp0ketCPC4hLkStms q9ETIyyerARIMv4SEhKqS4E7HIg6ccGkwv1ja5E/b2jHMH4ht1dEXnfM2yh0Mwvk @@ -181,41 +181,3 @@ t5Gpocpt77LJnNiszXSerj/KjX2MflY5xUXeekWowLVTBOK5+CZ8+XBIgBt1hIG3 XKxcRgm/Va4QMEAnec0qXfdTVJaJiAW0bdKwKRRrrbwcTdNRGibdng== -----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 -""" diff --git a/lemur/tests/test_certificates.py b/lemur/tests/test_certificates.py index 48ee911e..f3f3bb17 100644 --- a/lemur/tests/test_certificates.py +++ b/lemur/tests/test_certificates.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals # at top of module + import pytest from lemur.certificates.views import * # noqa @@ -21,13 +23,13 @@ def test_private_key_str(): def test_create_basic_csr(): from lemur.certificates.service import create_csr csr_config = dict( - commonName=u'example.com', - organization=u'Example, Inc.', - organizationalUnit=u'Operations', - country=u'US', - state=u'CA', - location=u'A place', - extensions=dict(names=dict(subAltNames=[u'test.example.com', u'test2.example.com'])) + commonName='example.com', + organization='Example, Inc.', + organizationalUnit='Operations', + country='US', + state='CA', + location='A place', + extensions=dict(names=dict(subAltNames=['test.example.com', 'test2.example.com'])) ) csr, pem = create_csr(csr_config) diff --git a/setup.py b/setup.py index b508687f..6b4282e8 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,8 @@ install_requires = [ 'pyopenssl==0.15.1', 'pyjwt==1.0.1', 'xmltodict==0.9.2', - 'lockfile==0.10.2' + 'lockfile==0.10.2', + 'future==0.15.0', ] tests_require = [ diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..d4a3e59e --- /dev/null +++ b/tox.ini @@ -0,0 +1,2 @@ +[tox] +envlist = py27,pypy,py33,py34