Add modals for User Cert create and revoke.
This commit is contained in:
parent
73017e83ef
commit
c4d208e457
@ -87,6 +87,22 @@ def _get_service_url(request, service):
|
|||||||
return service_url
|
return service_url
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class UserCert(generic.View):
|
||||||
|
"""Pass-through API for executing service requests.
|
||||||
|
|
||||||
|
Horizon only adds auth and CORS proxying.
|
||||||
|
"""
|
||||||
|
url_regex = r'ssh/usergen/(?P<path>.+)$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def post(self, request, path):
|
||||||
|
data = dict(request.DATA) if request.DATA else {}
|
||||||
|
data['user_id'] = request.user.id
|
||||||
|
data['auth_id'] = request.user.project_id
|
||||||
|
return passthrough_post(path, request, data).json()
|
||||||
|
|
||||||
|
|
||||||
@urls.register
|
@urls.register
|
||||||
class Passthrough(generic.View):
|
class Passthrough(generic.View):
|
||||||
"""Pass-through API for executing service requests.
|
"""Pass-through API for executing service requests.
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc overview
|
||||||
|
* @ngname designatedashboard.resources.os-tatu-user.actions
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Provides all of the actions for Tatu User Certs.
|
||||||
|
*/
|
||||||
|
angular.module('tatudashboard.resources.os-tatu-user.actions', [
|
||||||
|
'horizon.framework.conf',
|
||||||
|
'horizon.app.core'
|
||||||
|
])
|
||||||
|
.run(run);
|
||||||
|
|
||||||
|
run.$inject = [
|
||||||
|
'horizon.framework.conf.resource-type-registry.service',
|
||||||
|
'tatudashboard.resources.os-tatu-user.resourceType',
|
||||||
|
'tatudashboard.resources.os-tatu-user.actions.create',
|
||||||
|
'tatudashboard.resources.os-tatu-user.actions.revoke'
|
||||||
|
];
|
||||||
|
|
||||||
|
function run(registry,
|
||||||
|
resourceTypeString,
|
||||||
|
createAction,
|
||||||
|
revokeAction) {
|
||||||
|
var resourceType = registry.getResourceType(resourceTypeString);
|
||||||
|
resourceType
|
||||||
|
.globalActions
|
||||||
|
.append({
|
||||||
|
id: 'create',
|
||||||
|
service: createAction,
|
||||||
|
template: {
|
||||||
|
text: gettext('Create User SSH Certificate')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resourceType
|
||||||
|
.itemActions
|
||||||
|
.append({
|
||||||
|
id: 'revoke',
|
||||||
|
service: revokeAction,
|
||||||
|
template: {
|
||||||
|
text: gettext('Revoke'),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
@ -36,6 +36,8 @@
|
|||||||
*/
|
*/
|
||||||
function apiService(apiPassthroughUrl, httpService, toastService) {
|
function apiService(apiPassthroughUrl, httpService, toastService) {
|
||||||
var service = {
|
var service = {
|
||||||
|
create: create,
|
||||||
|
revoke: revoke,
|
||||||
get: get,
|
get: get,
|
||||||
list: list
|
list: list
|
||||||
};
|
};
|
||||||
@ -44,6 +46,41 @@
|
|||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name create
|
||||||
|
* @description
|
||||||
|
* Create a User Certificate
|
||||||
|
*
|
||||||
|
* @param {Object} data
|
||||||
|
* Specifies the User Certificate information to create
|
||||||
|
*
|
||||||
|
* @returns {Object} The created user object
|
||||||
|
*/
|
||||||
|
function create(data) {
|
||||||
|
return httpService.post(apiPassthroughUrl + 'usergen/noauth/usercerts/', data)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to create the certificate.'));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name revoke
|
||||||
|
* @description
|
||||||
|
* Revoke a single certificate
|
||||||
|
*
|
||||||
|
* @param {Object} user
|
||||||
|
* Specifies the user certificate to revoke
|
||||||
|
*
|
||||||
|
* @returns {Object} The revoked user certificate
|
||||||
|
*/
|
||||||
|
function revoke(user) {
|
||||||
|
var data = { 'serial': user.serial }
|
||||||
|
var url = apiPassthroughUrl + 'noauth/revokeduserkeys/' + user.auth_id + '/'
|
||||||
|
return httpService.post(url, data)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to revoke the certificate.'));
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name list
|
* @name list
|
||||||
* @description
|
* @description
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use self file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('tatudashboard.resources.os-tatu-user.actions')
|
||||||
|
.factory('tatudashboard.resources.os-tatu-user.actions.create', action);
|
||||||
|
|
||||||
|
action.$inject = [
|
||||||
|
'$q',
|
||||||
|
'tatudashboard.resources.os-tatu-user.actions.common-forms',
|
||||||
|
'tatudashboard.resources.os-tatu-user.api',
|
||||||
|
'tatudashboard.resources.os-tatu-user.resourceType',
|
||||||
|
'horizon.app.core.openstack-service-api.policy',
|
||||||
|
'horizon.app.core.openstack-service-api.serviceCatalog',
|
||||||
|
'horizon.framework.widgets.form.ModalFormService',
|
||||||
|
'horizon.framework.widgets.toast.service',
|
||||||
|
'horizon.framework.widgets.modal-wait-spinner.service'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngDoc factory
|
||||||
|
* @name tatudashboard.resources.os-tatu-user.actions.create
|
||||||
|
*
|
||||||
|
* @Description
|
||||||
|
* Brings up the Create User modal.
|
||||||
|
*/
|
||||||
|
function action($q,
|
||||||
|
forms,
|
||||||
|
api,
|
||||||
|
resourceTypeName,
|
||||||
|
policy,
|
||||||
|
serviceCatalog,
|
||||||
|
schemaFormModalService,
|
||||||
|
toast,
|
||||||
|
waitSpinner) {
|
||||||
|
var createUserPolicy, sshServiceEnabled;
|
||||||
|
var title = gettext("Generate User Certificate");
|
||||||
|
var message = {
|
||||||
|
success: gettext('Certificate %s was successfully generated.')
|
||||||
|
};
|
||||||
|
|
||||||
|
var service = {
|
||||||
|
initScope: initScope,
|
||||||
|
allowed: allowed,
|
||||||
|
perform: perform
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
|
||||||
|
function initScope() {
|
||||||
|
createUserPolicy = policy.ifAllowed({rules: [['ssh', 'create_user_cert']]});
|
||||||
|
sshServiceEnabled = serviceCatalog.ifTypeEnabled('ssh');
|
||||||
|
}
|
||||||
|
|
||||||
|
function allowed() {
|
||||||
|
return $q.all([
|
||||||
|
createUserPolicy,
|
||||||
|
sshServiceEnabled
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function perform() {
|
||||||
|
var formConfig = forms.getCreateFormConfig();
|
||||||
|
formConfig.title = title;
|
||||||
|
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmit(context) {
|
||||||
|
var userModel = angular.copy(context.model);
|
||||||
|
waitSpinner.showModalSpinner(gettext('Creating User SSH Certificate'));
|
||||||
|
return api.create(userModel).then(onSuccess, onFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
waitSpinner.hideModalSpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSuccess(response) {
|
||||||
|
waitSpinner.hideModalSpinner();
|
||||||
|
var user = response.data;
|
||||||
|
toast.add('success', interpolate(message.success, [user.serial]));
|
||||||
|
|
||||||
|
// To make the result of this action generically useful, reformat the return
|
||||||
|
// from the deleteModal into a standard form
|
||||||
|
return {
|
||||||
|
created: [{type: resourceTypeName, id: user.serial}],
|
||||||
|
updated: [],
|
||||||
|
deleted: [],
|
||||||
|
failed: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFailure() {
|
||||||
|
waitSpinner.hideModalSpinner();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return the create User Cert form.
|
||||||
|
* @returns {object} a schema form config, including default model
|
||||||
|
*/
|
||||||
|
function getCreateFormConfig() {
|
||||||
|
return {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"key.pub": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"key": "key.pub",
|
||||||
|
"type": "textarea",
|
||||||
|
"title": gettext("SSH Public Key"),
|
||||||
|
"description": gettext("The user's SSH public key."),
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use self file except in compliance with the License. You may obtain
|
||||||
|
* a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('tatudashboard.resources.os-tatu-user.actions')
|
||||||
|
.factory('tatudashboard.resources.os-tatu-user.actions.revoke', action);
|
||||||
|
|
||||||
|
action.$inject = [
|
||||||
|
'$q',
|
||||||
|
'tatudashboard.resources.os-tatu-user.api',
|
||||||
|
'tatudashboard.resources.util',
|
||||||
|
'horizon.app.core.openstack-service-api.policy',
|
||||||
|
'horizon.framework.util.actions.action-result.service',
|
||||||
|
'horizon.framework.util.i18n.gettext',
|
||||||
|
'horizon.framework.util.q.extensions',
|
||||||
|
'horizon.framework.widgets.modal.simple-modal.service',
|
||||||
|
'horizon.framework.widgets.toast.service',
|
||||||
|
'tatudashboard.resources.os-tatu-user.resourceType'
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ngdoc factory
|
||||||
|
* @name tatudashboard.resources.os-tatu-user.actions.revoke
|
||||||
|
*
|
||||||
|
* @Description
|
||||||
|
* Brings up the revoke user confirmation modal dialog.
|
||||||
|
|
||||||
|
* On submit, revoke given user SSH certificate.
|
||||||
|
* On cancel, do nothing.
|
||||||
|
*/
|
||||||
|
function action(
|
||||||
|
$q,
|
||||||
|
userApi,
|
||||||
|
util,
|
||||||
|
policy,
|
||||||
|
actionResultService,
|
||||||
|
gettext,
|
||||||
|
$qExtensions,
|
||||||
|
simpleModalService,
|
||||||
|
toast,
|
||||||
|
resourceType
|
||||||
|
) {
|
||||||
|
var scope, context, revokeUserPromise;
|
||||||
|
var notAllowedMessage = gettext("You are not allowed to revoke user certificates: %s");
|
||||||
|
|
||||||
|
var service = {
|
||||||
|
initScope: initScope,
|
||||||
|
allowed: allowed,
|
||||||
|
perform: perform
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
//////////////
|
||||||
|
|
||||||
|
function initScope(newScope) {
|
||||||
|
scope = newScope;
|
||||||
|
context = { };
|
||||||
|
revokeUserPromise = policy.ifAllowed({rules: [['ssh', 'revoke_user_cert']]});
|
||||||
|
}
|
||||||
|
|
||||||
|
function perform(items) {
|
||||||
|
var certs = angular.isArray(items) ? items : [items];
|
||||||
|
context.labels = labelize(certs.length);
|
||||||
|
return $qExtensions.allSettled(users.map(checkPermission)).then(afterCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
function allowed(user) {
|
||||||
|
// only row actions pass in user
|
||||||
|
// otherwise, assume it is a batch action
|
||||||
|
if (user) {
|
||||||
|
return $q.all([
|
||||||
|
revokeUserPromise
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return policy.ifAllowed({ rules: [['ssh', 'revoke_user_cert']] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPermission(user) {
|
||||||
|
return {promise: allowed(user), context: user};
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterCheck(result) {
|
||||||
|
var outcome = $q.reject(); // Reject the promise by default
|
||||||
|
if (result.fail.length > 0) {
|
||||||
|
toast.add('error', getMessage(notAllowedMessage, result.fail.map(getUser)));
|
||||||
|
outcome = $q.reject(result.fail);
|
||||||
|
}
|
||||||
|
if (result.pass.length > 0) {
|
||||||
|
outcome = open(result.pass.map(getUser)).then(createResult);
|
||||||
|
}
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createResult(deleteModalResult) {
|
||||||
|
// To make the result of this action generically useful, reformat the return
|
||||||
|
// from the deleteModal into a standard form
|
||||||
|
var actionResult = actionResultService.getActionResult();
|
||||||
|
deleteModalResult.pass.forEach(function markDeleted(item) {
|
||||||
|
actionResult.updated(resourceType, getUser(item).serial);
|
||||||
|
});
|
||||||
|
deleteModalResult.fail.forEach(function markFailed(item) {
|
||||||
|
actionResult.failed(resourceType, getUser(item).serial);
|
||||||
|
});
|
||||||
|
return actionResult.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function labelize(count) {
|
||||||
|
return {
|
||||||
|
|
||||||
|
title: ngettext(
|
||||||
|
'Confirm Revoke User Certificate',
|
||||||
|
'Confirm Revoke Users Certificates', count),
|
||||||
|
|
||||||
|
message: ngettext(
|
||||||
|
'You have selected "%s". Revoked user cert is not recoverable.',
|
||||||
|
'You have selected "%s". Revoked users certs are not recoverable.', count),
|
||||||
|
|
||||||
|
submit: ngettext(
|
||||||
|
'Revoke User Certificate',
|
||||||
|
'Revoke Users Certificates', count),
|
||||||
|
|
||||||
|
success: ngettext(
|
||||||
|
'Revoked User Cert: %s.',
|
||||||
|
'Revoked Users Certs: %s.', count),
|
||||||
|
|
||||||
|
error: ngettext(
|
||||||
|
'Unable to revoke User Certificate: %s.',
|
||||||
|
'Unable to revoke Users Certificates: %s.', count)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(users) {
|
||||||
|
var options = {
|
||||||
|
title: context.labels.title,
|
||||||
|
body: interpolate(context.labels.message, [users.map(getSerial).join("\", \"")]),
|
||||||
|
submit: context.labels.submit
|
||||||
|
};
|
||||||
|
|
||||||
|
return simpleModalService.modal(options).result.then(onModalSubmit);
|
||||||
|
|
||||||
|
function onModalSubmit() {
|
||||||
|
return $qExtensions.allSettled(users.map(revokePromise)).then(notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
function revokePromise(user) {
|
||||||
|
return {promise: userApi.revoke(user), context: user};
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(result) {
|
||||||
|
if (result.pass.length > 0) {
|
||||||
|
var passEntities = result.pass.map(getUser);
|
||||||
|
scope.$emit(context.successEvent, passEntities.map(getSerial));
|
||||||
|
toast.add('success', getMessage(context.labels.success, passEntities));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.fail.length > 0) {
|
||||||
|
var failEntities = result.fail.map(getUser);
|
||||||
|
scope.$emit(context.failedEvent, failEntities.map(getSerial));
|
||||||
|
toast.add('error', getMessage(context.labels.error, failEntities));
|
||||||
|
}
|
||||||
|
// Return the passed and failed entities as part of resolving the promise
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUser(result) {
|
||||||
|
return result.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get the displayed message
|
||||||
|
*/
|
||||||
|
function getMessage(message, users) {
|
||||||
|
return interpolate(message, [users.map(getSerial).join(", ")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get the serial number of the user
|
||||||
|
*/
|
||||||
|
function getSerial(user) {
|
||||||
|
return user.serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})();
|
@ -1,4 +1,4 @@
|
|||||||
<hz-resource-panel resource-type-name="OS::Tatu::User">
|
<hz-resource-panel resource-type-name="OS::Tatu::User">
|
||||||
<hz-resource-table resource-type-name="OS::Tatu::User"
|
<hz-resource-table resource-type-name="OS::Tatu::User"
|
||||||
track-by="fingerprint"></hz-resource-table>
|
track-by="serial"></hz-resource-table>
|
||||||
</hz-resource-panel>
|
</hz-resource-panel>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user