Log fixes (#534)
* tying up some loose ends with event logging * Ensuring creators can access
This commit is contained in:
parent
e2143d3ee8
commit
727bc87ede
|
@ -15,9 +15,6 @@ from flask_principal import Permission, RoleNeed
|
|||
operator_permission = Permission(RoleNeed('operator'))
|
||||
admin_permission = Permission(RoleNeed('admin'))
|
||||
|
||||
CertificateCreator = namedtuple('certificate', ['method', 'value'])
|
||||
CertificateCreatorNeed = partial(CertificateCreator, 'key')
|
||||
|
||||
CertificateOwner = namedtuple('certificate', ['method', 'value'])
|
||||
CertificateOwnerNeed = partial(CertificateOwner, 'role')
|
||||
|
||||
|
@ -28,8 +25,8 @@ class SensitiveDomainPermission(Permission):
|
|||
|
||||
|
||||
class CertificatePermission(Permission):
|
||||
def __init__(self, certificate_id, owner, roles):
|
||||
needs = [RoleNeed('admin'), CertificateCreatorNeed(certificate_id), RoleNeed(owner)]
|
||||
def __init__(self, owner, roles):
|
||||
needs = [RoleNeed('admin'), RoleNeed(owner), RoleNeed('creator')]
|
||||
for r in roles:
|
||||
needs.append(CertificateOwnerNeed(str(r)))
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ from cryptography.hazmat.primitives import serialization
|
|||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
|
||||
|
||||
from lemur.users import service as user_service
|
||||
from lemur.auth.permissions import CertificateCreatorNeed, \
|
||||
AuthorityCreatorNeed, RoleMemberNeed
|
||||
from lemur.auth.permissions import AuthorityCreatorNeed, RoleMemberNeed
|
||||
|
||||
|
||||
def get_rsa_public_key(n, e):
|
||||
|
@ -155,11 +154,6 @@ def on_identity_loaded(sender, identity):
|
|||
for authority in user.authorities:
|
||||
identity.provides.add(AuthorityCreatorNeed(authority.id))
|
||||
|
||||
# apply ownership of certificates
|
||||
if hasattr(user, 'certificates'):
|
||||
for certificate in user.certificates:
|
||||
identity.provides.add(CertificateCreatorNeed(certificate.id))
|
||||
|
||||
g.user = user
|
||||
|
||||
|
||||
|
|
|
@ -68,19 +68,19 @@ class Certificate(db.Model):
|
|||
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')
|
||||
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
||||
roles = relationship("Role", secondary=roles_certificates, backref="certificate")
|
||||
replaces = relationship("Certificate",
|
||||
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')
|
||||
domains = relationship('Domain', secondary=certificate_associations, backref='certificate')
|
||||
roles = relationship('Role', secondary=roles_certificates, backref='certificate')
|
||||
replaces = relationship('Certificate',
|
||||
secondary=certificate_replacement_associations,
|
||||
primaryjoin=id == certificate_replacement_associations.c.certificate_id, # noqa
|
||||
secondaryjoin=id == certificate_replacement_associations.c.replaced_certificate_id, # noqa
|
||||
backref='replaced')
|
||||
|
||||
logs = relationship("Log", backref="certificate")
|
||||
endpoints = relationship("Endpoint", backref='certificate')
|
||||
logs = relationship('Log', backref='certificate')
|
||||
endpoints = relationship('Endpoint', backref='certificate')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
cert = lemur.common.utils.parse_certificate(kwargs['body'])
|
||||
|
|
|
@ -438,9 +438,10 @@ class CertificatePrivateKey(AuthenticatedResource):
|
|||
if not cert:
|
||||
return dict(message="Cannot find specified certificate"), 404
|
||||
|
||||
if not g.current_user.is_admin:
|
||||
# allow creators
|
||||
if g.current_user != cert.user:
|
||||
owner_role = role_service.get_by_name(cert.owner)
|
||||
permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles])
|
||||
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
|
||||
|
||||
if not permission.can():
|
||||
return dict(message='You are not authorized to view this key'), 403
|
||||
|
@ -621,27 +622,32 @@ class Certificates(AuthenticatedResource):
|
|||
"""
|
||||
cert = service.get(certificate_id)
|
||||
|
||||
owner_role = role_service.get_by_name(cert.owner)
|
||||
permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles])
|
||||
if not cert:
|
||||
return dict(message="Cannot find specified certificate"), 404
|
||||
|
||||
if permission.can():
|
||||
for destination in data['destinations']:
|
||||
if destination.plugin.requires_key:
|
||||
if not cert.private_key:
|
||||
return dict('Unable to add destination: {0}. Certificate does not have required private key.'.format(destination.label))
|
||||
# allow creators
|
||||
if g.current_user != cert.user:
|
||||
owner_role = role_service.get_by_name(cert.owner)
|
||||
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
|
||||
|
||||
return service.update(
|
||||
certificate_id,
|
||||
data['owner'],
|
||||
data['description'],
|
||||
data['notify'],
|
||||
data['destinations'],
|
||||
data['notifications'],
|
||||
data['replacements'],
|
||||
data['roles']
|
||||
)
|
||||
if not permission.can():
|
||||
return dict(message='You are not authorized to update this certificate'), 403
|
||||
|
||||
return dict(message='You are not authorized to update this certificate'), 403
|
||||
for destination in data['destinations']:
|
||||
if destination.plugin.requires_key:
|
||||
if not cert.private_key:
|
||||
return dict('Unable to add destination: {0}. Certificate does not have required private key.'.format(destination.label))
|
||||
|
||||
return service.update(
|
||||
certificate_id,
|
||||
data['owner'],
|
||||
data['description'],
|
||||
data['notify'],
|
||||
data['destinations'],
|
||||
data['notifications'],
|
||||
data['replacements'],
|
||||
data['roles']
|
||||
)
|
||||
|
||||
|
||||
class NotificationCertificatesList(AuthenticatedResource):
|
||||
|
@ -923,9 +929,10 @@ class CertificateExport(AuthenticatedResource):
|
|||
plugin.slug))
|
||||
|
||||
else:
|
||||
if not g.current_user.is_admin:
|
||||
# allow creators
|
||||
if g.current_user != cert.user:
|
||||
owner_role = role_service.get_by_name(cert.owner)
|
||||
permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles])
|
||||
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
|
||||
|
||||
if not permission.can():
|
||||
return dict(message='You are not authorized to export this certificate.'), 403
|
||||
|
|
|
@ -15,7 +15,7 @@ from lemur.database import db
|
|||
|
||||
|
||||
class Log(db.Model):
|
||||
__tablename__ = 'log'
|
||||
__tablename__ = 'logs'
|
||||
id = Column(Integer, primary_key=True)
|
||||
certificate_id = Column(Integer, ForeignKey('certificates.id'))
|
||||
log_type = Column(Enum('key_view', name='log_type'), nullable=False)
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
"""Adding private key auditing.
|
||||
"""Adding logging database tables.
|
||||
|
||||
Revision ID: 6d6151f5f307
|
||||
Revision ID: e3691fc396e9
|
||||
Revises: 932525b82f1a
|
||||
Create Date: 2016-11-18 16:08:12.191959
|
||||
Create Date: 2016-11-28 13:15:46.995219
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6d6151f5f307'
|
||||
revision = 'e3691fc396e9'
|
||||
down_revision = '932525b82f1a'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from sqlalchemy_utils.types import ArrowType
|
||||
|
||||
import sqlalchemy_utils
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('views',
|
||||
op.create_table('logs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('certificate_id', sa.Integer(), nullable=True),
|
||||
sa.Column('viewed_at', ArrowType(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('log_type', sa.Enum('key_view', name='log_type'), nullable=False),
|
||||
sa.Column('logged_at', sqlalchemy_utils.types.arrow.ArrowType(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['certificate_id'], ['certificates.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
|
@ -32,5 +31,5 @@ def upgrade():
|
|||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('views')
|
||||
op.drop_table('logs')
|
||||
### end Alembic commands ###
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
angular.module('lemur')
|
||||
.service('LogApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('domains');
|
||||
return LemurRestangular.all('logs');
|
||||
})
|
||||
.service('LogService', function () {
|
||||
var LogService = this;
|
||||
|
|
|
@ -6,11 +6,11 @@ angular.module('lemur')
|
|||
$stateProvider.state('logs', {
|
||||
url: '/logs',
|
||||
templateUrl: '/angular/logs/view/view.tpl.html',
|
||||
controller: 'DomainsViewController'
|
||||
controller: 'LogsViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('DomainsViewController', function ($scope, $uibModal, DomainApi, DomainService, ngTableParams) {
|
||||
.controller('LogsViewController', function ($scope, $uibModal, LogApi, LogService, ngTableParams, MomentService) {
|
||||
$scope.filter = {};
|
||||
$scope.logsTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
|
@ -22,10 +22,12 @@ angular.module('lemur')
|
|||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
DomainApi.getList(params.url()).then(function (data) {
|
||||
LogApi.getList(params.url()).then(function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.momentService = MomentService;
|
||||
});
|
||||
|
|
|
@ -12,17 +12,22 @@
|
|||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="domainsTable" class="table table-striped" show-filter="showFilter" template-pagination="angular/pager.html">
|
||||
<table ng-table="logsTable" class="table table-striped" show-filter="showFilter" template-pagination="angular/pager.html">
|
||||
<tbody>
|
||||
<tr ng-repeat="log in $data track by $index">
|
||||
<td data-title="'Logged At'" sortable="'logged_at'" filter="{ 'logged_at': 'text' }">
|
||||
{{ log.logged_at }}
|
||||
<td data-title="'Logged'" sortable="'logged_at'" filter="{ 'logged_at': 'text' }">
|
||||
<span class="pull-right" uib-tooltip="{{ log.loggedAt }}">
|
||||
{{ momentService.createMoment(log.loggedAt) }}
|
||||
</span>
|
||||
</td>
|
||||
<td data-title="'Certificate'" sortable="'certificate'" filter="{ 'certificate.name': 'text' }">
|
||||
{{ log.certificate.name }}
|
||||
</td>
|
||||
<td data-title="'User'" sortable="'user'" filter="{ 'user.email': 'text' }">
|
||||
{{ log.user.email }}
|
||||
</td>
|
||||
<td data-title="'Type'" sortable="'type'" filter="{ 'type': 'text' }">
|
||||
{{ log.type }}
|
||||
{{ log.logType }}
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
<li><a ui-sref="roles">Roles</a></li>
|
||||
<li><a ui-sref="users">Users</a></li>
|
||||
<li><a ui-sref="domains">Domains</a></li>
|
||||
<li><a ui-sref="log">Log</a></li>
|
||||
<li><a ui-sref="logs">Logs</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -42,8 +42,9 @@ class User(db.Model):
|
|||
email = Column(String(128), unique=True)
|
||||
profile_picture = Column(String(255))
|
||||
roles = relationship('Role', secondary=roles_users, passive_deletes=True, backref=db.backref('user'), lazy='dynamic')
|
||||
certificates = relationship("Certificate", backref=db.backref('user'), lazy='dynamic')
|
||||
authorities = relationship("Authority", backref=db.backref('user'), lazy='dynamic')
|
||||
certificates = relationship('Certificate', backref=db.backref('user'), lazy='dynamic')
|
||||
authorities = relationship('Authority', backref=db.backref('user'), lazy='dynamic')
|
||||
logs = relationship('Log', backref=db.backref('user'), lazy='dynamic')
|
||||
|
||||
def check_password(self, password):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue