Add modals for User Cert create and revoke.

This commit is contained in:
Pino de Candia 2018-01-23 14:51:15 -06:00
parent 73017e83ef
commit c4d208e457
6 changed files with 464 additions and 1 deletions

View File

@ -87,6 +87,22 @@ def _get_service_url(request, service):
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
class Passthrough(generic.View):
"""Pass-through API for executing service requests.

View File

@ -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'),
}
});
}
})();

View File

@ -36,6 +36,8 @@
*/
function apiService(apiPassthroughUrl, httpService, toastService) {
var service = {
create: create,
revoke: revoke,
get: get,
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
* @description

View File

@ -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
}
]
};
}
}
})();

View File

@ -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;
}
}
})();

View File

@ -1,4 +1,4 @@
<hz-resource-panel 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>