Fixes various issues. (#317)
This commit is contained in:
parent
58e8fe0bd0
commit
c11034b9bc
|
@ -60,8 +60,15 @@ class AuthorityInputSchema(LemurInputSchema):
|
||||||
raise ValidationError("If generating a subca parent 'authority' must be specified.")
|
raise ValidationError("If generating a subca parent 'authority' must be specified.")
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorityUpdateSchema(LemurInputSchema):
|
||||||
|
owner = fields.Email()
|
||||||
|
description = fields.String()
|
||||||
|
roles = fields.Nested(AssociatedRoleSchema(many=True))
|
||||||
|
|
||||||
|
|
||||||
class AuthorityOutputSchema(LemurOutputSchema):
|
class AuthorityOutputSchema(LemurOutputSchema):
|
||||||
id = fields.Integer()
|
id = fields.Integer()
|
||||||
|
description = fields.String()
|
||||||
name = fields.String()
|
name = fields.String()
|
||||||
owner = fields.Email()
|
owner = fields.Email()
|
||||||
not_before = fields.DateTime()
|
not_before = fields.DateTime()
|
||||||
|
@ -73,7 +80,7 @@ class AuthorityOutputSchema(LemurOutputSchema):
|
||||||
options = fields.Dict()
|
options = fields.Dict()
|
||||||
roles = fields.List(fields.Nested(AssociatedRoleSchema))
|
roles = fields.List(fields.Nested(AssociatedRoleSchema))
|
||||||
|
|
||||||
|
authority_update_schema = AuthorityUpdateSchema()
|
||||||
authority_input_schema = AuthorityInputSchema()
|
authority_input_schema = AuthorityInputSchema()
|
||||||
authority_output_schema = AuthorityOutputSchema()
|
authority_output_schema = AuthorityOutputSchema()
|
||||||
authorities_output_schema = AuthorityOutputSchema(many=True)
|
authorities_output_schema = AuthorityOutputSchema(many=True)
|
||||||
|
|
|
@ -17,7 +17,7 @@ from lemur.roles import service as role_service
|
||||||
from lemur.certificates import service as certificate_service
|
from lemur.certificates import service as certificate_service
|
||||||
|
|
||||||
from lemur.authorities import service
|
from lemur.authorities import service
|
||||||
from lemur.authorities.schemas import authority_input_schema, authority_output_schema, authorities_output_schema
|
from lemur.authorities.schemas import authority_input_schema, authority_output_schema, authorities_output_schema, authority_update_schema
|
||||||
|
|
||||||
|
|
||||||
mod = Blueprint('authorities', __name__)
|
mod = Blueprint('authorities', __name__)
|
||||||
|
@ -218,7 +218,7 @@ class Authorities(AuthenticatedResource):
|
||||||
"""
|
"""
|
||||||
return service.get(authority_id)
|
return service.get(authority_id)
|
||||||
|
|
||||||
@validate_schema(authority_input_schema, authority_output_schema)
|
@validate_schema(authority_update_schema, authority_output_schema)
|
||||||
def put(self, authority_id, data=None):
|
def put(self, authority_id, data=None):
|
||||||
"""
|
"""
|
||||||
.. http:put:: /authorities/1
|
.. http:put:: /authorities/1
|
||||||
|
@ -266,6 +266,10 @@ class Authorities(AuthenticatedResource):
|
||||||
:statuscode 403: unauthenticated
|
:statuscode 403: unauthenticated
|
||||||
"""
|
"""
|
||||||
authority = service.get(authority_id)
|
authority = service.get(authority_id)
|
||||||
|
|
||||||
|
if not authority:
|
||||||
|
return dict(message='Not Found'), 404
|
||||||
|
|
||||||
role = role_service.get_by_name(authority.owner)
|
role = role_service.get_by_name(authority.owner)
|
||||||
|
|
||||||
# all the authority role members should be allowed
|
# all the authority role members should be allowed
|
||||||
|
@ -277,7 +281,7 @@ class Authorities(AuthenticatedResource):
|
||||||
|
|
||||||
# we want to make sure that we cannot add roles that we are not members of
|
# we want to make sure that we cannot add roles that we are not members of
|
||||||
if not g.current_user.is_admin:
|
if not g.current_user.is_admin:
|
||||||
role_ids = set([r['id'] for r in data['roles']])
|
role_ids = set([r.id for r in data['roles']])
|
||||||
user_role_ids = set([r.id for r in g.current_user.roles])
|
user_role_ids = set([r.id for r in g.current_user.roles])
|
||||||
|
|
||||||
if not role_ids.issubset(user_role_ids):
|
if not role_ids.issubset(user_role_ids):
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from marshmallow import fields
|
from marshmallow import fields
|
||||||
from lemur.users.schemas import UserOutputSchema
|
from lemur.users.schemas import UserNestedOutputSchema
|
||||||
from lemur.authorities.schemas import AuthorityOutputSchema
|
from lemur.authorities.schemas import AuthorityOutputSchema
|
||||||
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
|
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
|
||||||
from lemur.schemas import AssociatedUserSchema, AssociatedAuthoritySchema
|
from lemur.schemas import AssociatedUserSchema, AssociatedAuthoritySchema
|
||||||
|
@ -27,7 +27,7 @@ class RoleOutputSchema(LemurOutputSchema):
|
||||||
name = fields.String()
|
name = fields.String()
|
||||||
description = fields.String()
|
description = fields.String()
|
||||||
authorities = fields.Nested(AuthorityOutputSchema, many=True)
|
authorities = fields.Nested(AuthorityOutputSchema, many=True)
|
||||||
users = fields.Nested(UserOutputSchema, many=True)
|
users = fields.Nested(UserNestedOutputSchema, many=True)
|
||||||
|
|
||||||
|
|
||||||
role_input_schema = RoleInputSchema()
|
role_input_schema = RoleInputSchema()
|
||||||
|
|
|
@ -113,7 +113,7 @@ class PluginOutputSchema(LemurOutputSchema):
|
||||||
label = fields.String()
|
label = fields.String()
|
||||||
description = fields.String()
|
description = fields.String()
|
||||||
active = fields.Boolean()
|
active = fields.Boolean()
|
||||||
plugin_options = fields.List(fields.Dict())
|
options = fields.List(fields.Dict(), dump_to='pluginOptions')
|
||||||
slug = fields.String()
|
slug = fields.String()
|
||||||
title = fields.String()
|
title = fields.String()
|
||||||
|
|
||||||
|
|
|
@ -1,65 +1,62 @@
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<div class="modal-title">
|
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<h3>Edit <span class="text-muted"><small>{{ authority.name }}</small></span></h3>
|
||||||
<h3 class="modal-header">Edit <span class="text-muted"><small>{{ authority.name }}</small></span></h3>
|
<div class="modal-body">
|
||||||
</div>
|
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||||
<div class="modal-body">
|
<div class="form-group"
|
||||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
ng-class="{'has-error': editForm.owner.$invalid, 'has-success': !editForm.owner.$invalid&&editForm.owner.$dirty}">
|
||||||
<div class="form-group"
|
<label class="control-label col-sm-2">
|
||||||
ng-class="{'has-error': editForm.owner.$invalid, 'has-success': !editForm.owner.$invalid&&editForm.owner.$dirty}">
|
Owner
|
||||||
<label class="control-label col-sm-2">
|
</label>
|
||||||
Owner
|
<div class="col-sm-10">
|
||||||
</label>
|
<input type="email" name="owner" ng-model="authority.owner" placeholder="owner@example.com"
|
||||||
<div class="col-sm-10">
|
class="form-control" required/>
|
||||||
<input type="email" name="owner" ng-model="authority.owner" placeholder="owner@example.com"
|
|
||||||
class="form-control" required/>
|
|
||||||
|
|
||||||
<p ng-show="editForm.owner.$invalid && !editForm.owner.$pristine" class="help-block">Enter a valid
|
<p ng-show="editForm.owner.$invalid && !editForm.owner.$pristine" class="help-block">Enter a valid
|
||||||
email.</p>
|
email.</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
</div>
|
||||||
ng-class="{'has-error': editForm.description.$invalid, 'has-success': !editForm.$invalid&&editForm.description.$dirty}">
|
<div class="form-group"
|
||||||
<label class="control-label col-sm-2">
|
ng-class="{'has-error': editForm.description.$invalid, 'has-success': !editForm.$invalid&&editForm.description.$dirty}">
|
||||||
Description
|
<label class="control-label col-sm-2">
|
||||||
</label>
|
Description
|
||||||
<div class="col-sm-10">
|
</label>
|
||||||
<textarea name="description" ng-model="authority.description" placeholder="Something elegant" class="form-control" required></textarea>
|
<div class="col-sm-10">
|
||||||
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
<textarea name="description" ng-model="authority.description" placeholder="Something elegant" class="form-control" required></textarea>
|
||||||
</div>
|
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label class="control-label col-sm-2">
|
<div class="form-group">
|
||||||
Roles
|
<label class="control-label col-sm-2">
|
||||||
</label>
|
Roles
|
||||||
<div class="col-sm-10">
|
</label>
|
||||||
<div class="input-group">
|
<div class="col-sm-10">
|
||||||
<input type="text" ng-model="authority.selectedRole" placeholder="Role Name"
|
<div class="input-group">
|
||||||
uib-typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
<input type="text" ng-model="authority.selectedRole" placeholder="Role Name"
|
||||||
class="form-control input-md" typeahead-on-select="authority.attachRole($item)" typeahead-wait-ms="500"
|
uib-typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
||||||
uib-tooltip="Roles control which authorities a user can issue certificates from"
|
class="form-control input-md" typeahead-on-select="authority.attachRole($item)" typeahead-wait-ms="500"
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
uib-tooltip="Roles control which authorities a user can issue certificates from"
|
||||||
<span class="input-group-btn">
|
tooltip-trigger="focus" tooltip-placement="top">
|
||||||
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
<span class="input-group-btn">
|
||||||
<span class="badge">{{ authority.roles.length || 0 }}</span>
|
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||||
</button>
|
<span class="badge">{{ authority.roles.length || 0 }}</span>
|
||||||
</span>
|
</button>
|
||||||
</div>
|
</span>
|
||||||
<table ng-show="authority.roles" class="table">
|
|
||||||
<tr ng-repeat="role in authority.roles track by $index">
|
|
||||||
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
|
||||||
<td><span class="text-muted">{{ role.description }}</span></td>
|
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="authority.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<table ng-show="authority.roles" class="table">
|
||||||
|
<tr ng-repeat="role in authority.roles track by $index">
|
||||||
|
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
||||||
|
<td><span class="text-muted">{{ role.description }}</span></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" ng-click="authority.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<div class="modal-footer">
|
</div>
|
||||||
<button ng-click="save(authority)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
<div class="modal-footer">
|
||||||
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
<button ng-click="save(authority)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
||||||
</div>
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,40 +1,38 @@
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<div class="modal-title">
|
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<h3>Edit <span class="text-muted"><small>{{ certificate.name }}</small></span></h3>
|
||||||
<h3 class="modal-header">Edit <span class="text-muted"><small>{{ certificate.name }}</small></span></h3>
|
</div>
|
||||||
</div>
|
<div class="modal-body">
|
||||||
<div class="modal-body">
|
<form name="editForm" class="form-horizontal" role="form" novalidate>
|
||||||
<form name="editForm" class="form-horizontal" role="form" novalidate>
|
<div class="form-group"
|
||||||
<div class="form-group"
|
ng-class="{'has-error': editForm.owner.$invalid, 'has-success': !editForm.owner.$invalid&&editForm.owner.$dirty}">
|
||||||
ng-class="{'has-error': editForm.owner.$invalid, 'has-success': !editForm.owner.$invalid&&editForm.owner.$dirty}">
|
<label class="control-label col-sm-2">
|
||||||
<label class="control-label col-sm-2">
|
Owner
|
||||||
Owner
|
</label>
|
||||||
</label>
|
<div class="col-sm-10">
|
||||||
<div class="col-sm-10">
|
<input type="email" name="owner" ng-model="certificate.owner" placeholder="owner@example.com"
|
||||||
<input type="email" name="owner" ng-model="certificate.owner" placeholder="owner@example.com"
|
class="form-control" required/>
|
||||||
class="form-control" required/>
|
|
||||||
|
<p ng-show="editForm.owner.$invalid && !editForm.owner.$pristine" class="help-block">Enter a valid
|
||||||
<p ng-show="editForm.owner.$invalid && !editForm.owner.$pristine" class="help-block">Enter a valid
|
email.</p>
|
||||||
email.</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group"
|
||||||
<div class="form-group"
|
ng-class="{'has-error': editForm.description.$invalid, 'has-success': !editForm.$invalid&&editForm.description.$dirty}">
|
||||||
ng-class="{'has-error': editForm.description.$invalid, 'has-success': !editForm.$invalid&&editForm.description.$dirty}">
|
<label class="control-label col-sm-2">
|
||||||
<label class="control-label col-sm-2">
|
Description
|
||||||
Description
|
</label>
|
||||||
</label>
|
<div class="col-sm-10">
|
||||||
<div class="col-sm-10">
|
<textarea name="description" ng-model="certificate.description" placeholder="Something elegant" class="form-control" required></textarea>
|
||||||
<textarea name="description" ng-model="certificate.description" placeholder="Something elegant" class="form-control" required></textarea>
|
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
||||||
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div ng-include="'angular/certificates/certificate/replaces.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/replaces.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
<div class="modal-footer">
|
||||||
<div class="modal-footer">
|
<button type="submit" ng-click="save(certificate)" ng-disabled="editForm.$invalid" class="btn btn-success">Save</button>
|
||||||
<button type="submit" ng-click="save(certificate)" ng-disabled="editForm.$invalid" class="btn btn-success">Save</button>
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,7 +40,7 @@ angular.module('lemur')
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Opps',
|
title: 'Opps',
|
||||||
body: 'I see what you did there' + response.data.message
|
body: 'I see what you did there: ' + response.data.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -46,7 +46,7 @@ angular.module('lemur')
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Opps',
|
title: 'Opps',
|
||||||
body: 'I see what you did there' + response.data.message
|
body: 'I see what you did there: ' + response.data.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,6 @@ angular.module('lemur')
|
||||||
.controller('RolesEditController', function ($scope, $uibModalInstance, RoleApi, RoleService, UserService, toaster, editId) {
|
.controller('RolesEditController', function ($scope, $uibModalInstance, RoleApi, RoleService, UserService, toaster, editId) {
|
||||||
RoleApi.get(editId).then(function (role) {
|
RoleApi.get(editId).then(function (role) {
|
||||||
$scope.role = role;
|
$scope.role = role;
|
||||||
RoleService.getUsers(role);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.save = function (role) {
|
$scope.save = function (role) {
|
||||||
|
@ -40,7 +39,23 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.userService = UserService;
|
$scope.userService = UserService;
|
||||||
$scope.roleService = RoleService;
|
|
||||||
|
$scope.loadPassword = function (role) {
|
||||||
|
RoleService.loadPassword(role).then(
|
||||||
|
function (response) {
|
||||||
|
$scope.role.password = response.password;
|
||||||
|
$scope.role.username = response.username;
|
||||||
|
}, function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: role.name,
|
||||||
|
body: 'lemur-bad-request',
|
||||||
|
bodyOutputType: 'directive',
|
||||||
|
directiveData: response.data,
|
||||||
|
timeout: 100000
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('RolesCreateController', function ($scope,$uibModalInstance, RoleApi, RoleService, UserService, LemurRestangular, toaster) {
|
.controller('RolesCreateController', function ($scope,$uibModalInstance, RoleApi, RoleService, UserService, LemurRestangular, toaster) {
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button ng-click="roleService.loadPassword(role)" class="btn btn-warning pull-left">Show Credentials</button>
|
<button ng-click="loadPassword(role)" class="btn btn-warning pull-left">Show Credentials</button>
|
||||||
<button ng-click="save(role)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
<button ng-click="save(role)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
||||||
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,7 +40,7 @@ angular.module('lemur')
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'Opps',
|
title: 'Opps',
|
||||||
body: 'I see what you did there' + response.data.message
|
body: 'I see what you did there: ' + response.data.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -44,8 +44,8 @@ def test_authority_post(client, token, status):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("token,status", [
|
@pytest.mark.parametrize("token,status", [
|
||||||
(VALID_USER_HEADER_TOKEN, 400),
|
(VALID_USER_HEADER_TOKEN, 200),
|
||||||
(VALID_ADMIN_HEADER_TOKEN, 400),
|
(VALID_ADMIN_HEADER_TOKEN, 200),
|
||||||
('', 401)
|
('', 401)
|
||||||
])
|
])
|
||||||
def test_authority_put(client, token, status):
|
def test_authority_put(client, token, status):
|
||||||
|
|
|
@ -30,6 +30,14 @@ class UserOutputSchema(LemurOutputSchema):
|
||||||
authorities = fields.Nested(AssociatedAuthoritySchema, many=True)
|
authorities = fields.Nested(AssociatedAuthoritySchema, many=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserNestedOutputSchema(LemurOutputSchema):
|
||||||
|
__envelope__ = False
|
||||||
|
id = fields.Integer()
|
||||||
|
username = fields.String()
|
||||||
|
email = fields.Email()
|
||||||
|
active = fields.Boolean()
|
||||||
|
|
||||||
|
|
||||||
user_input_schema = UserInputSchema()
|
user_input_schema = UserInputSchema()
|
||||||
user_output_schema = UserOutputSchema()
|
user_output_schema = UserOutputSchema()
|
||||||
users_output_schema = UserOutputSchema(many=True)
|
users_output_schema = UserOutputSchema(many=True)
|
||||||
|
|
Loading…
Reference in New Issue