diff --git a/lemur/auth/permissions.py b/lemur/auth/permissions.py index 8b64b558..5b6d7902 100644 --- a/lemur/auth/permissions.py +++ b/lemur/auth/permissions.py @@ -18,6 +18,9 @@ admin_permission = Permission(RoleNeed('admin')) CertificateCreator = namedtuple('certificate', ['method', 'value']) CertificateCreatorNeed = partial(CertificateCreator, 'key') +CertificateOwner = namedtuple('certificate', ['method', 'value']) +CertificateOwnerNeed = partial(CertificateOwner, 'role') + class SensitiveDomainPermission(Permission): def __init__(self): @@ -36,6 +39,15 @@ class UpdateCertificatePermission(Permission): super(UpdateCertificatePermission, self).__init__(c_need, RoleNeed(owner), RoleNeed('admin')) +class CertificatePermission(Permission): + def __init__(self, certificate_id, roles): + needs = [RoleNeed('admin'), CertificateCreatorNeed(certificate_id)] + for r in roles: + needs.append(CertificateOwnerNeed(str(r))) + + super(CertificatePermission, self).__init__(*needs) + + RoleUser = namedtuple('role', ['method', 'value']) ViewRoleCredentialsNeed = partial(RoleUser, 'roleView') diff --git a/lemur/auth/service.py b/lemur/auth/service.py index 352b905d..ad1cf4f6 100644 --- a/lemur/auth/service.py +++ b/lemur/auth/service.py @@ -165,7 +165,7 @@ 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(ViewRoleCredentialsNeed(role.id)) + identity.provides.add(ViewRoleCredentialsNeed(role.name)) identity.provides.add(RoleNeed(role.name)) # apply ownership for authorities diff --git a/lemur/authorities/views.py b/lemur/authorities/views.py index 500a1187..cd204d8d 100644 --- a/lemur/authorities/views.py +++ b/lemur/authorities/views.py @@ -5,7 +5,7 @@ :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson """ -from flask import Blueprint, g +from flask import Blueprint from flask.ext.restful import reqparse, Api from lemur.common.utils import paginated_parser @@ -13,7 +13,6 @@ from lemur.common.schema import validate_schema from lemur.auth.service import AuthenticatedResource from lemur.auth.permissions import AuthorityPermission -from lemur.roles import service as role_service from lemur.certificates import service as certificate_service from lemur.authorities import service @@ -270,24 +269,11 @@ class Authorities(AuthenticatedResource): if not authority: return dict(message='Not Found'), 404 - role = role_service.get_by_name(authority.owner) - # all the authority role members should be allowed roles = [x.name for x in authority.roles] - - # allow "owner" roles by team DL - roles.append(role) permission = AuthorityPermission(authority_id, roles) if permission.can(): - # we want to make sure that we cannot add roles that we are not members of - if not g.current_user.is_admin: - role_ids = set([r.id for r in data['roles']]) - user_role_ids = set([r.id for r in g.current_user.roles]) - - if not role_ids.issubset(user_role_ids): - return dict(message="You are not allowed to associate a role which you are not a member of."), 403 - return service.update( authority_id, owner=data['owner'], diff --git a/lemur/certificates/schemas.py b/lemur/certificates/schemas.py index d34c2fb1..a31a1c51 100644 --- a/lemur/certificates/schemas.py +++ b/lemur/certificates/schemas.py @@ -11,11 +11,12 @@ from marshmallow import fields, validates_schema, post_load from marshmallow.exceptions import ValidationError from lemur.schemas import AssociatedAuthoritySchema, AssociatedDestinationSchema, AssociatedCertificateSchema, \ - AssociatedNotificationSchema, PluginInputSchema, ExtensionSchema + AssociatedNotificationSchema, PluginInputSchema, ExtensionSchema, AssociatedRoleSchema 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.users.schemas import UserNestedOutputSchema @@ -51,6 +52,7 @@ class CertificateInputSchema(CertificateSchema): destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True) notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True) replacements = fields.Nested(AssociatedCertificateSchema, missing=[], many=True) + roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True) csr = fields.String(validate=validators.csr) @@ -73,6 +75,7 @@ class CertificateEditInputSchema(CertificateSchema): destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True) notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True) replacements = fields.Nested(AssociatedCertificateSchema, missing=[], many=True) + roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True) class CertificateNestedOutputSchema(LemurOutputSchema): @@ -117,6 +120,7 @@ class CertificateOutputSchema(LemurOutputSchema): notifications = fields.Nested(NotificationNestedOutputSchema, many=True) replaces = fields.Nested(CertificateNestedOutputSchema, many=True) authority = fields.Nested(AuthorityNestedOutputSchema) + roles = fields.Nested(RoleNestedOutputSchema, many=True) class CertificateUploadInputSchema(CertificateSchema): @@ -130,6 +134,7 @@ class CertificateUploadInputSchema(CertificateSchema): destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True) notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True) replacements = fields.Nested(AssociatedCertificateSchema, missing=[], many=True) + roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True) @validates_schema def keys(self, data): diff --git a/lemur/certificates/service.py b/lemur/certificates/service.py index 177cab84..c4f0822b 100644 --- a/lemur/certificates/service.py +++ b/lemur/certificates/service.py @@ -91,7 +91,7 @@ def export(cert, export_plugin): return plugin.export(cert.body, cert.chain, cert.private_key, export_plugin['pluginOptions']) -def update(cert_id, owner, description, active, destinations, notifications, replaces): +def update(cert_id, owner, description, active, destinations, notifications, replaces, roles): """ Updates a certificate :param cert_id: @@ -107,6 +107,8 @@ def update(cert_id, owner, description, active, destinations, notifications, rep cert.active = active cert.description = description cert.destinations = destinations + cert.notifications = notifications + cert.roles = roles cert.replaces = replaces cert.owner = owner diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index d3918b8b..cbb54237 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -15,7 +15,7 @@ from lemur.common.schema import validate_schema from lemur.common.utils import paginated_parser from lemur.auth.service import AuthenticatedResource -from lemur.auth.permissions import ViewKeyPermission, AuthorityPermission, UpdateCertificatePermission +from lemur.auth.permissions import ViewKeyPermission, AuthorityPermission, CertificatePermission from lemur.certificates import service from lemur.certificates.schemas import certificate_input_schema, certificate_output_schema, \ @@ -519,9 +519,8 @@ class Certificates(AuthenticatedResource): :statuscode 403: unauthenticated """ cert = service.get(certificate_id) - role = role_service.get_by_name(cert.owner) - permission = UpdateCertificatePermission(certificate_id, getattr(role, 'name', None)) + permission = CertificatePermission(cert.id, [x.name for x in cert.roles]) if permission.can(): return service.update( @@ -531,7 +530,8 @@ class Certificates(AuthenticatedResource): data['active'], data['destinations'], data['notifications'], - data['replacements'] + data['replacements'], + data['roles'] ) return dict(message='You are not authorized to update this certificate'), 403 @@ -742,8 +742,8 @@ class CertificateExport(AuthenticatedResource): :statuscode 403: unauthenticated """ cert = service.get(certificate_id) - role = role_service.get_by_name(cert.owner) - permission = UpdateCertificatePermission(certificate_id, getattr(role, 'name', None)) + + permission = CertificatePermission(cert.id, [x.name for x in cert.roles]) options = data['plugin']['plugin_options'] plugin = data['plugin']['plugin_object'] diff --git a/lemur/roles/schemas.py b/lemur/roles/schemas.py index 52dfac10..fa685d04 100644 --- a/lemur/roles/schemas.py +++ b/lemur/roles/schemas.py @@ -30,6 +30,13 @@ class RoleOutputSchema(LemurOutputSchema): users = fields.Nested(UserNestedOutputSchema, many=True) +class RoleNestedOutputSchema(LemurOutputSchema): + __envelope__ = False + id = fields.Integer() + name = fields.String() + description = fields.String() + + role_input_schema = RoleInputSchema() role_output_schema = RoleOutputSchema() roles_output_schema = RoleOutputSchema(many=True) diff --git a/lemur/roles/service.py b/lemur/roles/service.py index e4d4b394..b36a8665 100644 --- a/lemur/roles/service.py +++ b/lemur/roles/service.py @@ -9,8 +9,6 @@ .. moduleauthor:: Kevin Glisson """ -from flask import g - from lemur import database from lemur.roles.models import Role from lemur.users.models import User @@ -102,13 +100,6 @@ def render(args): if authority_id: query = query.filter(Role.authority_id == authority_id) - # we make sure that user can see the role - admins can see all - if not g.current_user.is_admin: - ids = [] - for role in g.current_user.roles: - ids.append(role.id) - query = query.filter(Role.id.in_(ids)) - if filt: terms = filt.split(';') query = database.filter(query, Role, terms) diff --git a/lemur/static/app/angular/authorities/authority/edit.tpl.html b/lemur/static/app/angular/authorities/authority/edit.tpl.html index 8859e2e4..f1386328 100644 --- a/lemur/static/app/angular/authorities/authority/edit.tpl.html +++ b/lemur/static/app/angular/authorities/authority/edit.tpl.html @@ -1,6 +1,8 @@ diff --git a/lemur/static/app/angular/authorities/authority/tracking.tpl.html b/lemur/static/app/angular/authorities/authority/tracking.tpl.html index f582d91d..1b766fe5 100644 --- a/lemur/static/app/angular/authorities/authority/tracking.tpl.html +++ b/lemur/static/app/angular/authorities/authority/tracking.tpl.html @@ -120,6 +120,12 @@ +
+ +
+
diff --git a/lemur/static/app/angular/certificates/certificate/edit.tpl.html b/lemur/static/app/angular/certificates/certificate/edit.tpl.html index 7aa4e53a..cf76bbd7 100644 --- a/lemur/static/app/angular/certificates/certificate/edit.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/edit.tpl.html @@ -27,6 +27,12 @@

You must give a short description about this authority will be used for, this description should only include alphanumeric characters

+
+ +
+
diff --git a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html index 3d2d37b6..31bb8921 100644 --- a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html @@ -146,6 +146,12 @@ class="help-block">Enter a valid certificate signing request.

+
+ +
+
diff --git a/lemur/static/app/angular/certificates/certificate/upload.tpl.html b/lemur/static/app/angular/certificates/certificate/upload.tpl.html index ecd33305..26514bf3 100644 --- a/lemur/static/app/angular/certificates/certificate/upload.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/upload.tpl.html @@ -81,6 +81,12 @@ class="help-block">Enter a valid certificate.

+
+ +
+
diff --git a/lemur/static/app/angular/certificates/services.js b/lemur/static/app/angular/certificates/services.js index d9f02846..4f69a889 100644 --- a/lemur/static/app/angular/certificates/services.js +++ b/lemur/static/app/angular/certificates/services.js @@ -4,6 +4,16 @@ angular.module('lemur') .service('CertificateApi', function (LemurRestangular, DomainService) { LemurRestangular.extendModel('certificates', function (obj) { return angular.extend(obj, { + attachRole: function (role) { + this.selectedRole = null; + if (this.roles === undefined) { + this.roles = []; + } + this.roles.push(role); + }, + removeRole: function (index) { + this.roles.splice(index, 1); + }, attachAuthority: function (authority) { this.authority = authority; this.authority.maxDate = moment(this.authority.notAfter).subtract(1, 'days').format('YYYY/MM/DD'); diff --git a/lemur/static/app/angular/certificates/view/view.tpl.html b/lemur/static/app/angular/certificates/view/view.tpl.html index 3426b023..d380059c 100644 --- a/lemur/static/app/angular/certificates/view/view.tpl.html +++ b/lemur/static/app/angular/certificates/view/view.tpl.html @@ -121,6 +121,15 @@ + + Roles +
    +
  • + {{ role.name }} + {{ role.description}} +
  • +
+
Destinations
    diff --git a/lemur/static/app/angular/roles/role/role.js b/lemur/static/app/angular/roles/role/role.js index 052e4163..e6619ac0 100644 --- a/lemur/static/app/angular/roles/role/role.js +++ b/lemur/static/app/angular/roles/role/role.js @@ -1,7 +1,29 @@ 'use strict'; angular.module('lemur') +.directive('roleSelect', function (RoleApi) { + return { + restrict: 'AE', + scope: { + ngModel: '=' + }, + replace: true, + require: 'ngModel', + templateUrl: '/angular/roles/role/roleSelect.tpl.html', + link: function postLink($scope) { + RoleApi.getList().then(function (roles) { + $scope.roles = roles; + }); + $scope.findRoleByName = function (search) { + return RoleApi.getList({'filter[name]': search}) + .then(function (roles) { + return roles; + }); + }; + } + }; + }) .controller('RolesEditController', function ($scope, $uibModalInstance, RoleApi, RoleService, UserService, toaster, editId) { RoleApi.get(editId).then(function (role) { $scope.role = role; diff --git a/lemur/static/app/angular/roles/role/roleSelect.tpl.html b/lemur/static/app/angular/roles/role/roleSelect.tpl.html new file mode 100644 index 00000000..bd9956ed --- /dev/null +++ b/lemur/static/app/angular/roles/role/roleSelect.tpl.html @@ -0,0 +1,23 @@ +
    +
    + + + + +
    + + + + + + +
    {{ role.name }}{{ role.description }} + +
    +
    \ No newline at end of file diff --git a/lemur/tests/test_certificates.py b/lemur/tests/test_certificates.py index 2953cdd4..db736d2d 100644 --- a/lemur/tests/test_certificates.py +++ b/lemur/tests/test_certificates.py @@ -108,7 +108,7 @@ def test_certificate_input_schema(client, authority): assert data['country'] == 'US' assert data['location'] == 'Los Gatos' - assert len(data.keys()) == 12 + assert len(data.keys()) == 13 def test_certificate_input_with_extensions(client, authority):