* Closes #147

* Fixing tests

* Ensuring we can validate max dates.
This commit is contained in:
kevgliss 2016-05-23 11:28:25 -07:00
parent bd727b825d
commit 656269ff17
22 changed files with 334 additions and 200 deletions

View File

@ -6,49 +6,35 @@
:license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Text, func, ForeignKey, DateTime, PassiveDefault, Boolean
from sqlalchemy.dialects.postgresql import JSON
from lemur.database import db
from lemur.models import roles_authorities
from lemur.common import defaults
class Authority(db.Model):
__tablename__ = 'authorities'
id = Column(Integer, primary_key=True)
owner = Column(String(128))
owner = Column(String(128), nullable=False)
name = Column(String(128), unique=True)
body = Column(Text())
chain = Column(Text())
bits = Column(Integer())
cn = Column(String(128))
not_before = Column(DateTime)
not_after = Column(DateTime)
active = Column(Boolean, default=True)
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
plugin_name = Column(String(64))
description = Column(Text)
options = Column(JSON)
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
roles = relationship('Role', secondary=roles_authorities, passive_deletes=True, backref=db.backref('authority'), lazy='dynamic')
user_id = Column(Integer, ForeignKey('users.id'))
certificates = relationship("Certificate", backref='authority')
authority_certificate = relationship("Certificate", backref='root_authority', uselist=False, foreign_keys='Certificate.root_authority_id')
certificates = relationship("Certificate", backref='authority', foreign_keys='Certificate.authority_id')
def __init__(self, name, owner, plugin_name, body, roles=None, chain=None, description=None):
cert = x509.load_pem_x509_certificate(bytes(body), default_backend())
self.name = name
self.body = body
self.chain = chain
self.owner = owner
self.description = description
self.plugin_name = plugin_name
self.cn = defaults.common_name(cert)
self.not_before = defaults.not_before(cert)
self.not_after = defaults.not_after(cert)
if roles:
self.roles = roles
def __init__(self, **kwargs):
self.owner = kwargs['owner']
self.roles = kwargs.get('roles', [])
self.name = kwargs.get('name')
self.description = kwargs.get('description')
self.authority_certificate = kwargs['authority_certificate']
self.plugin_name = kwargs['plugin']['slug']

View File

@ -12,6 +12,7 @@ from marshmallow import validate
from marshmallow.exceptions import ValidationError
from lemur.schemas import PluginInputSchema, PluginOutputSchema, ExtensionSchema, AssociatedAuthoritySchema, AssociatedRoleSchema
from lemur.users.schemas import UserNestedOutputSchema
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.common import validators
@ -61,25 +62,39 @@ class AuthorityInputSchema(LemurInputSchema):
class AuthorityUpdateSchema(LemurInputSchema):
owner = fields.Email()
owner = fields.Email(required=True)
description = fields.String()
active = fields.Boolean()
roles = fields.Nested(AssociatedRoleSchema(many=True))
class RootAuthorityCertificateOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
active = fields.Boolean()
bits = fields.Integer()
body = fields.String()
chain = fields.String()
description = fields.String()
name = fields.String()
cn = fields.String()
not_after = fields.DateTime()
not_before = fields.DateTime()
owner = fields.Email()
status = fields.Boolean()
user = fields.Nested(UserNestedOutputSchema)
class AuthorityOutputSchema(LemurOutputSchema):
id = fields.Integer()
description = fields.String()
name = fields.String()
owner = fields.Email()
not_before = fields.DateTime()
not_after = fields.DateTime()
plugin = fields.Nested(PluginOutputSchema)
body = fields.String()
chain = fields.String()
active = fields.Boolean()
options = fields.Dict()
roles = fields.List(fields.Nested(AssociatedRoleSchema))
authority_certificate = fields.Nested(RootAuthorityCertificateOutputSchema)
class AuthorityNestedOutputSchema(LemurOutputSchema):
@ -87,13 +102,8 @@ class AuthorityNestedOutputSchema(LemurOutputSchema):
description = fields.String()
name = fields.String()
owner = fields.Email()
not_before = fields.DateTime()
not_after = fields.DateTime()
plugin = fields.Nested(PluginOutputSchema)
body = fields.String()
chain = fields.String()
active = fields.Boolean()
options = fields.Dict()
authority_update_schema = AuthorityUpdateSchema()

View File

@ -9,14 +9,13 @@
"""
from flask import g
from flask import current_app
from lemur import database
from lemur.extensions import metrics
from lemur.authorities.models import Authority
from lemur.roles import service as role_service
from lemur.notifications import service as notification_service
from lemur.certificates.models import Certificate
from lemur.certificates.service import upload
def update(authority_id, description=None, owner=None, active=None, roles=None):
@ -40,42 +39,28 @@ def update(authority_id, description=None, owner=None, active=None, roles=None):
return database.update(authority)
def create(kwargs):
def mint(**kwargs):
"""
Create a new authority.
Creates the authority based on the plugin provided.
"""
issuer = kwargs['plugin']['plugin_object']
body, chain, roles = issuer.create_authority(kwargs)
return body, chain, roles
def create_authority_roles(roles, **kwargs):
"""
Creates all of the necessary authority roles.
:param roles:
:param kwargs:
:return:
"""
issuer = kwargs['plugin']['plugin_object']
kwargs['creator'] = g.current_user.email
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
cert = Certificate(body=cert_body, chain=intermediate, **kwargs)
if kwargs['type'] == 'subca':
cert.description = "This is the ROOT certificate for the {0} sub certificate authority the parent \
authority is {1}.".format(kwargs.get('name'), kwargs.get('parent'))
else:
cert.description = "This is the ROOT certificate for the {0} certificate authority.".format(
kwargs.get('name')
)
cert.user = g.current_user
cert.notifications = notification_service.create_default_expiration_notifications(
'DEFAULT_SECURITY',
current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL')
)
# we create and attach any roles that the issuer gives us
role_objs = []
for r in issuer_roles:
for r in roles:
role = role_service.create(
r['name'],
password=r['password'],
description="{0} auto generated role".format(issuer.title),
description="Auto generated role for {0}".format(kwargs['plugin']['plugin_object'].title),
username=r['username'])
# the user creating the authority should be able to administer it
@ -93,22 +78,39 @@ def create(kwargs):
)
role_objs.append(owner_role)
return role_objs
authority = Authority(
kwargs.get('name'),
kwargs['owner'],
issuer.slug,
cert_body,
description=kwargs['description'],
chain=intermediate,
roles=role_objs
def create(**kwargs):
"""
Creates a new authority.
"""
kwargs['creator'] = g.user.email
body, chain, roles = mint(**kwargs)
kwargs['body'] = body
kwargs['chain'] = chain
kwargs['roles'] = create_authority_roles(roles, **kwargs)
if kwargs['type'] == 'subca':
description = "This is the ROOT certificate for the {0} sub certificate authority the parent \
authority is {1}.".format(kwargs.get('name'), kwargs.get('parent'))
else:
description = "This is the ROOT certificate for the {0} certificate authority.".format(
kwargs.get('name')
)
database.update(cert)
kwargs['description'] = description
cert = upload(**kwargs)
kwargs['authority_certificate'] = cert
authority = Authority(**kwargs)
authority = database.create(authority)
g.user.authorities.append(authority)
g.current_user.authorities.append(authority)
metrics.send('authority_created', 'counter', 1, metric_tags=dict(owner=authority.owner))
return authority

View File

@ -167,7 +167,7 @@ class AuthoritiesList(AuthenticatedResource):
:statuscode 403: unauthenticated
:statuscode 200: no error
"""
return service.create(data)
return service.create(**data)
class Authorities(AuthenticatedResource):

View File

@ -36,7 +36,7 @@ class Certificate(db.Model):
__tablename__ = 'certificates'
id = Column(Integer, primary_key=True)
owner = Column(String(128), nullable=False)
name = Column(String(128), unique=True)
name = Column(String(128)) # , unique=True) TODO make all names unique
description = Column(String(1024))
active = Column(Boolean, default=True)
@ -59,7 +59,9 @@ class Certificate(db.Model):
san = Column(String(1024)) # TODO this should be migrated to boolean
user_id = Column(Integer, ForeignKey('users.id'))
authority_id = Column(Integer, ForeignKey('authorities.id'))
authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
root_authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
notifications = relationship("Notification", secondary=certificate_notification_associations, backref='certificate')
destinations = relationship("Destination", secondary=certificate_destination_associations, backref='certificate')
sources = relationship("Source", secondary=certificate_source_associations, backref='certificate')

View File

@ -6,7 +6,6 @@
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from flask import current_app
from marshmallow import fields, validates_schema, post_load
from marshmallow.exceptions import ValidationError
@ -17,7 +16,7 @@ from lemur.authorities.schemas import AuthorityNestedOutputSchema
from lemur.destinations.schemas import DestinationNestedOutputSchema
from lemur.notifications.schemas import NotificationNestedOutputSchema
from lemur.roles.schemas import RoleNestedOutputSchema
# from lemur.domains.schemas import DomainNestedOutputSchema
from lemur.domains.schemas import DomainNestedOutputSchema
from lemur.users.schemas import UserNestedOutputSchema
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
@ -115,7 +114,7 @@ class CertificateOutputSchema(LemurOutputSchema):
signing_algorithm = fields.String()
status = fields.Boolean()
user = fields.Nested(UserNestedOutputSchema)
# domains = fields.Nested(DomainNestedOutputSchema)
domains = fields.Nested(DomainNestedOutputSchema)
destinations = fields.Nested(DestinationNestedOutputSchema, many=True)
notifications = fields.Nested(NotificationNestedOutputSchema, many=True)
replaces = fields.Nested(CertificateNestedOutputSchema, many=True)

View File

@ -120,7 +120,6 @@ def mint(**kwargs):
Minting is slightly different for each authority.
Support for multiple authorities is handled by individual plugins.
:param issuer_options:
"""
authority = kwargs['authority']

View File

@ -8,7 +8,7 @@
"""
from functools import wraps
from flask import request
from flask import request, current_app
from sqlalchemy.orm.collections import InstrumentedList
@ -135,6 +135,7 @@ def validate_schema(input_schema, output_schema):
try:
resp = f(*args, **kwargs)
except Exception as e:
current_app.logger.exception(e)
return dict(message=e.message), 500
if isinstance(resp, tuple):

View File

@ -102,19 +102,19 @@ def dates(data):
raise ValidationError('Validity start must be before validity end.')
if data.get('authority'):
if data.get('validity_start').replace(tzinfo=None) < data['authority'].not_before:
raise ValidationError('Validity start must not be before {0}'.format(data['authority'].not_before))
if data.get('validity_start').replace(tzinfo=None) < data['authority'].authority_certificate.not_before:
raise ValidationError('Validity start must not be before {0}'.format(data['authority'].authority_certificate.not_before))
if data.get('validity_end').replace(tzinfo=None) > data['authority'].not_after:
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].not_after))
if data.get('validity_end').replace(tzinfo=None) > data['authority'].authority_certificate.not_after:
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].authority_certificate.not_after))
if data.get('validity_years'):
now = arrow.utcnow()
end = now.replace(years=+data['validity_years'])
if data.get('authority'):
if now.naive < data['authority'].not_before:
raise ValidationError('Validity start must not be before {0}'.format(data['authority'].not_before))
if now.naive < data['authority'].authority_certificate.not_before:
raise ValidationError('Validity start must not be before {0}'.format(data['authority'].authority_certificate.not_before))
if end.naive > data['authority'].not_after:
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].not_after))
if end.naive > data['authority'].authority_certificate.not_after:
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].authority_certificate.not_after))

View File

@ -9,7 +9,7 @@ from marshmallow import fields
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.schemas import AssociatedCertificateSchema
from lemur.certificates.schemas import CertificateNestedOutputSchema
# from lemur.certificates.schemas import CertificateNestedOutputSchema
class DomainInputSchema(LemurInputSchema):
@ -23,7 +23,7 @@ class DomainOutputSchema(LemurOutputSchema):
id = fields.Integer()
name = fields.String()
sensitive = fields.Boolean()
certificates = fields.Nested(CertificateNestedOutputSchema, many=True, missing=[])
# certificates = fields.Nested(CertificateNestedOutputSchema, many=True, missing=[])
class DomainNestedOutputSchema(DomainOutputSchema):

View File

@ -0,0 +1,58 @@
"""empty message
Revision ID: 3307381f3b88
Revises: 412b22cb656a
Create Date: 2016-05-20 17:33:04.360687
"""
# revision identifiers, used by Alembic.
revision = '3307381f3b88'
down_revision = '412b22cb656a'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.alter_column('authorities', 'owner',
existing_type=sa.VARCHAR(length=128),
nullable=False)
op.drop_column('authorities', 'not_after')
op.drop_column('authorities', 'bits')
op.drop_column('authorities', 'cn')
op.drop_column('authorities', 'not_before')
op.add_column('certificates', sa.Column('root_authority_id', sa.Integer(), nullable=True))
op.alter_column('certificates', 'body',
existing_type=sa.TEXT(),
nullable=False)
op.alter_column('certificates', 'owner',
existing_type=sa.VARCHAR(length=128),
nullable=False)
op.drop_constraint(u'certificates_authority_id_fkey', 'certificates', type_='foreignkey')
op.create_foreign_key(None, 'certificates', 'authorities', ['authority_id'], ['id'], ondelete='CASCADE')
op.create_foreign_key(None, 'certificates', 'authorities', ['root_authority_id'], ['id'], ondelete='CASCADE')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'certificates', type_='foreignkey')
op.drop_constraint(None, 'certificates', type_='foreignkey')
op.create_foreign_key(u'certificates_authority_id_fkey', 'certificates', 'authorities', ['authority_id'], ['id'])
op.alter_column('certificates', 'owner',
existing_type=sa.VARCHAR(length=128),
nullable=True)
op.alter_column('certificates', 'body',
existing_type=sa.TEXT(),
nullable=True)
op.drop_column('certificates', 'root_authority_id')
op.add_column('authorities', sa.Column('not_before', postgresql.TIMESTAMP(), autoincrement=False, nullable=True))
op.add_column('authorities', sa.Column('cn', sa.VARCHAR(length=128), autoincrement=False, nullable=True))
op.add_column('authorities', sa.Column('bits', sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column('authorities', sa.Column('not_after', postgresql.TIMESTAMP(), autoincrement=False, nullable=True))
op.alter_column('authorities', 'owner',
existing_type=sa.VARCHAR(length=128),
nullable=True)
### end Alembic commands ###

View File

@ -1,4 +1,4 @@
"""empty message
"""
Revision ID: 412b22cb656a
Revises: 4c50b903d1ae

View File

@ -9,17 +9,15 @@
"""
from marshmallow import fields, post_load, pre_load, post_dump, validates_schema
from lemur.roles.models import Role
from lemur.authorities.models import Authority
from lemur.destinations.models import Destination
from lemur.certificates.models import Certificate
from lemur.notifications.models import Notification
from lemur.users.models import User
from lemur.common import validators
from lemur.common.schema import LemurSchema, LemurInputSchema, LemurOutputSchema
from lemur.destinations.models import Destination
from lemur.notifications.models import Notification
from lemur.plugins import plugins
from lemur.roles.models import Role
from lemur.users.models import User
class AssociatedAuthoritySchema(LemurInputSchema):

View File

@ -93,8 +93,8 @@
is-open="popup1.opened"
datepicker-options="dateOptions"
close-text="Close"
max-date="authority.authority.notAfter"
min-date="authority.authority.notBefore"
max-date="authority.authority.authorityCertificate.notAfter"
min-date="authority.authority.authorityCertificate.notBefore"
alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>

View File

@ -16,7 +16,7 @@ angular.module('lemur')
});
})
.controller('AuthoritiesViewController', function ($scope, $q, $uibModal, $stateParams, AuthorityApi, AuthorityService, ngTableParams, toaster) {
.controller('AuthoritiesViewController', function ($scope, $q, $uibModal, $stateParams, AuthorityApi, AuthorityService, MomentService, ngTableParams, toaster) {
$scope.filter = $stateParams;
$scope.authoritiesTable = new ngTableParams({
page: 1, // show first page
@ -35,6 +35,8 @@ angular.module('lemur')
}
});
$scope.momentService = MomentService;
$scope.updateActive = function (authority) {
AuthorityService.updateActive(authority).then(
function () {

View File

@ -1,7 +1,8 @@
<div class="row">
<div class="col-md-12">
<h2 class="featurette-heading">Authorities
<span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest</small></span></h2>
<span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest</small></span>
</h2>
<div class="panel panel-default">
<div class="panel-heading">
<div class="btn-group pull-right">
@ -13,36 +14,104 @@
<div class="clearfix"></div>
</div>
<div class="table-responsive">
<table ng-table="authoritiesTable" class="table table-striped" template-pagination="angular/pager.html" show-filter="false">
<table ng-table="authoritiesTable" class="table table-striped" template-pagination="angular/pager.html"
show-filter="false">
<tbody>
<tr ng-repeat="authority in $data track by $index">
<tr ng-repeat-start="authority in $data track by $index">
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
<ul class="list-unstyled">
<li>{{ authority.name }}</li>
<li><span class="text-muted">{{ authority.description }}</span></li>
<li><span class="text-muted">{{ authority.owner }}</span></li>
</ul>
</td>
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getAuthorityStatus()">
<form>
<switch ng-change="updateActive(authority)" id="status" name="status" ng-model="authority.active" class="green small"></switch>
<switch ng-change="updateActive(authority)" id="status" name="status"
ng-model="authority.active" class="green small"></switch>
</form>
</td>
<td data-title="'Roles'"> <!--filter="{ 'select': 'role' }" filter-data="roleService.getRoleDropDown()">-->
<div class="btn-group">
<a ng-click="editRole(role.id)" ng-repeat="role in authority.roles" class="btn btn-sm btn-danger">
{{ role.name }}
</a>
</div>
<td data-title="'Common Name'" filter="{ 'cn': 'text'}">
{{ authority.authorityCertificate.cn }}
</td>
<td data-title="''">
<div class="btn-group pull-right">
<a class="btn btn-sm btn-default" ui-sref="authority({name: authority.name})">Permalink</a>
<button uib-tooltip="Edit Authority" ng-click="edit(authority.id)" class="btn btn-sm btn-info">
<a class="btn btn-sm btn-default"
ui-sref="authority({name: authority.name})">Permalink</a>
<button ng-model="authority.toggle" class="btn btn-sm btn-info" uib-btn-checkbox
btn-checkbox-true="1"
btn-checkbox-false="0">More
</button>
<button uib-tooltip="Edit Authority" ng-click="edit(authority.id)"
class="btn btn-sm btn-warning">
Edit
</button>
</div>
</td>
</tr>
<tr class="warning" ng-if="authority.toggle" ng-repeat-end>
<td colspan="12">
<uib-tabset justified="true" class="col-md-6">
<uib-tab>
<uib-tab-heading>Basic Info</uib-tab-heading>
<ul class="list-group">
<li class="list-group-item">
<strong>Creator</strong>
<span class="pull-right">
{{ authority.authorityCertificate.user.email }}
</span>
</li>
<li class="list-group-item">
<strong>Not Before</strong>
<span class="pull-right" uib-tooltip="{{ authority.authorityCertificate.notBefore }}">
{{ momentService.createMoment(authority.authorityCertificate.notBefore) }}
</span>
</li>
<li class="list-group-item">
<strong>Not After</strong>
<span class="pull-right" uib-tooltip="{{ authority.authorityCertificate.notAfter }}">
{{ momentService.createMoment(authority.authorityCertificate.notAfter) }}
</span>
</li>
<li class="list-group-item">
<strong>Description</strong>
<p>{{ authority.description }}</p>
</li>
</ul>
</uib-tab>
<uib-tab>
<uib-tab-heading>Roles</uib-tab-heading>
<ul class="list-group">
<li class="list-group-item" ng-repeat="role in authority.roles">
<strong>{{ role.name }}</strong>
<span class="pull-right">{{ role.description }}</span>
</li>
</ul>
</uib-tab>
</uib-tabset>
<uib-tabset justified="true" class="col-md-6">
<uib-tab>
<uib-tab-heading>
Chain
<button class="btn btn-xs btn-default clipboard-btn glyphicon glyphicon-copy"
uib-tooltip="Copy chain to clipboard" tooltip-trigger="mouseenter"
clipboard
text="authority.authorityCertificate.chain"></button>
</uib-tab-heading>
<pre style="width: 100%">{{ authority.authorityCertificate.chain }}</pre>
</uib-tab>
<uib-tab>
<uib-tab-heading>
Public Certificate
<button class="btn btn-xs btn-default clipboard-btn glyphicon glyphicon-copy"
uib-tooltip="Copy authority to clipboard" tooltip-trigger="mouseenter"
clipboard
text="authority.authorityCertificate.body"></button>
</uib-tab-heading>
<pre style="width: 100%">{{ authority.authorityCertificate.body }}</pre>
</uib-tab>
</uib-tabset>
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -102,8 +102,8 @@
is-open="popup1.opened"
datepicker-options="dateOptions"
close-text="Close"
max-date="certificate.authority.notAfter"
min-date="certificate.authority.notBefore"
max-date="certificate.authority.authorityCertificate.notAfter"
min-date="certificate.authority.authorityCertificate.notBefore"
alt-input-formats="altInputFormats"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i
@ -121,8 +121,8 @@
is-open="popup2.opened"
datepicker-options="dateOptions"
close-text="Close"
max-date="certificate.authority.notAfter"
min-date="certificate.authority.notBefore"
max-date="certificate.authority.authorityCertificate.notAfter"
min-date="certificate.authority.authorityCertificate.notBefore"
alt-input-formats="altInputFormats"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open2()"><i

View File

@ -137,6 +137,7 @@ def issuer_plugin():
from lemur.plugins.base import register
from .plugins.issuer_plugin import TestIssuerPlugin
register(TestIssuerPlugin)
return TestIssuerPlugin
@pytest.yield_fixture(scope="function")
@ -147,7 +148,7 @@ def logged_in_user(app):
@pytest.yield_fixture(scope="function")
def logged_in_admin(app):
def logged_in_admin(session, app):
with app.test_request_context():
identity_changed.send(current_app._get_current_object(), identity=Identity(2))
yield

View File

@ -1,7 +1,7 @@
from datetime import date
from factory import Sequence, post_generation
from factory import Sequence, post_generation, SubFactory
from factory.alchemy import SQLAlchemyModelFactory
from factory.fuzzy import FuzzyChoice, FuzzyText, FuzzyDate
@ -14,7 +14,7 @@ from lemur.notifications.models import Notification
from lemur.users.models import User
from lemur.roles.models import Role
from .vectors import INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
from .vectors import INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
class BaseFactory(SQLAlchemyModelFactory):
@ -26,27 +26,6 @@ class BaseFactory(SQLAlchemyModelFactory):
sqlalchemy_session = db.session
class AuthorityFactory(BaseFactory):
"""Authority factory."""
name = Sequence(lambda n: 'authority{0}'.format(n))
owner = 'joe@example.com'
plugin_name = 'test-issuer'
body = INTERNAL_VALID_LONG_STR
class Meta:
"""Factory configuration."""
model = Authority
@post_generation
def roles(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for role in extracted:
self.roles.append(role)
class CertificateFactory(BaseFactory):
"""Certificate factory."""
name = Sequence(lambda n: 'certificate{0}'.format(n))
@ -135,6 +114,27 @@ class CertificateFactory(BaseFactory):
self.roles.append(domain)
class AuthorityFactory(BaseFactory):
"""Authority factory."""
name = Sequence(lambda n: 'authority{0}'.format(n))
owner = 'joe@example.com'
plugin = {'slug': 'test-issuer'}
authority_certificate = SubFactory(CertificateFactory)
class Meta:
"""Factory configuration."""
model = Authority
@post_generation
def roles(self, create, extracted, **kwargs):
if not create:
return
if extracted:
for role in extracted:
self.roles.append(role)
class DestinationFactory(BaseFactory):
"""Destination factory."""
plugin_name = Sequence(lambda n: 'destination{0}'.format(n))

View File

@ -25,14 +25,6 @@ def test_authority_input_schema(client, role):
assert not errors
@pytest.mark.parametrize("token, count", [
(VALID_USER_HEADER_TOKEN, 0),
(VALID_ADMIN_HEADER_TOKEN, 1)
])
def test_admin_authority(client, authority, token, count):
assert client.get(api.url_for(AuthoritiesList), headers=token).json['total'] == count
def test_user_authority(session, client, authority, role, user):
assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0
u = user['user']
@ -45,9 +37,23 @@ def test_user_authority(session, client, authority, role, user):
assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0
def test_create_authority(issuer_plugin, logged_in_admin):
from lemur.authorities.service import create
authority = create(plugin={'plugin_object': issuer_plugin, 'slug': issuer_plugin.slug}, owner='jim@example.com', type='root')
assert authority.authority_certificate
@pytest.mark.parametrize("token, count", [
(VALID_USER_HEADER_TOKEN, 0),
(VALID_ADMIN_HEADER_TOKEN, 3)
])
def test_admin_authority(client, authority, token, count):
assert client.get(api.url_for(AuthoritiesList), headers=token).json['total'] == count
@pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 404),
(VALID_ADMIN_HEADER_TOKEN, 404),
(VALID_USER_HEADER_TOKEN, 200),
(VALID_ADMIN_HEADER_TOKEN, 200),
('', 401)
])
def test_authority_get(client, token, status):
@ -64,8 +70,8 @@ def test_authority_post(client, token, status):
@pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 404),
(VALID_ADMIN_HEADER_TOKEN, 404),
(VALID_USER_HEADER_TOKEN, 400),
(VALID_ADMIN_HEADER_TOKEN, 400),
('', 401)
])
def test_authority_put(client, token, status):

View File

@ -7,8 +7,8 @@ from .vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKEN
@pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 404),
(VALID_ADMIN_HEADER_TOKEN, 404),
(VALID_USER_HEADER_TOKEN, 200),
(VALID_ADMIN_HEADER_TOKEN, 200),
('', 401)
])
def test_domain_get(client, token, status):

View File

@ -6,6 +6,7 @@
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
"""
from marshmallow import fields
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
from lemur.schemas import AssociatedRoleSchema, AssociatedCertificateSchema, AssociatedAuthoritySchema
@ -30,14 +31,14 @@ class UserOutputSchema(LemurOutputSchema):
authorities = fields.Nested(AssociatedAuthoritySchema, many=True)
user_input_schema = UserInputSchema()
user_output_schema = UserOutputSchema()
users_output_schema = UserOutputSchema(many=True)
class UserNestedOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
username = fields.String()
email = fields.Email()
active = fields.Boolean()
user_input_schema = UserInputSchema()
user_output_schema = UserOutputSchema()
users_output_schema = UserOutputSchema(many=True)