Support cluster role

This commit is contained in:
loganhz 2017-11-24 10:39:32 +08:00
parent dbc43c574e
commit 86965f55a0
20 changed files with 163 additions and 37 deletions

View File

@ -0,0 +1,43 @@
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { next } from '@ember/runloop';
import Resource from 'ember-api-store/models/resource';
import PolledResource from 'ui/mixins/cattle-polled-resource';
var ClusterRoleTemplate = Resource.extend(PolledResource, {
type: 'clusterRoleTemplate',
router: service(),
actions: {
edit: function () {
this.get('router').transitionTo('global-admin.roles.edit', this.get('id'), { queryParams: { type: 'cluster' } });
},
},
availableActions: function () {
return [
{ label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: true },
{ divider: true },
{ label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: true, altAction: 'delete', bulkable: true },
{ divider: true },
{ label: 'action.viewInApi', icon: 'icon icon-external-link', action: 'goToApi', enabled: true },
];
}.property(),
delete: function (/*arguments*/) {
var promise = this._super.apply(this, arguments);
return promise.then(() => {
this.set('state', 'removed');
}).catch((err) => {
this.get('growl').fromError('Error deleting', err);
});
},
});
ClusterRoleTemplate.reopenClass({
pollTransitioningDelay: 1000,
pollTransitioningInterval: 5000,
});
export default ClusterRoleTemplate;

View File

@ -11,7 +11,7 @@ var ProjectRoleTemplate = Resource.extend(PolledResource, {
actions: { actions: {
edit: function() { edit: function() {
this.get('router').transitionTo('global-admin.roles.edit', this.get('id')); this.get('router').transitionTo('global-admin.roles.edit', this.get('id'), { queryParams: { type: 'project' } });
}, },
}, },

View File

@ -15,10 +15,11 @@ export default Component.extend(NewOrEdit, {
ruleArray: alias('model.role.rules'), ruleArray: alias('model.role.rules'),
roleArray: null, roleArray: null,
ruleVerbs, ruleVerbs,
roleType: null,
actions: { actions: {
cancel() { cancel() {
this.get('router').transitionTo('global-admin.roles.index'); this.goBack();
}, },
addRule() { addRule() {
this.get('ruleArray').pushObject({ this.get('ruleArray').pushObject({
@ -41,18 +42,27 @@ export default Component.extend(NewOrEdit, {
}, },
}, },
goBack: function() {
const route = this.get('roleType') === 'project' ? '/admin/roles' : '/admin/roles?type=cluster'
this.get('router').transitionTo(route);
},
init: function () { init: function () {
this._super(); this._super();
this.set('roleArray', (this.get('primaryResource.projectRoleTemplateIds') || []).map(r => { this.set('roleArray', (this.get(`primaryResource.${this.get('roleType')}RoleTemplateIds`) || []).map(r => {
return { return {
value: r value: r
}; };
})); }));
}, },
otherRoles: function () {
return this.get('model.roles').filter(role => this.get('model.role.name') !== role.id);
}.property('model'),
roleDidChange: function () { roleDidChange: function () {
const role = this.get('model.role'); const role = this.get('model.role');
role.set('projectRoleTemplateIds', (this.get('roleArray') || []).filter(r => r.value).map(r => r.value)); role.set(`${this.get('roleType')}RoleTemplateIds`, (this.get('roleArray') || []).filter(r => r.value).map(r => r.value));
}.observes('roleArray.@each.value'), }.observes('roleArray.@each.value'),
doesNameExist() { doesNameExist() {
@ -100,16 +110,17 @@ export default Component.extend(NewOrEdit, {
willSave() { willSave() {
let ok = this._super(...arguments); let ok = this._super(...arguments);
if (ok) { if (ok) {
const templateIdsKey = `${this.get('roleType')}RoleTemplateIds`;
const role = this.get('primaryResource'); const role = this.get('primaryResource');
const name = (role.get('name') || '').trim().toLowerCase(); const name = (role.get('name') || '').trim().toLowerCase();
const otherRoles = role.get('projectRoleTemplateIds').filter(r => r).uniq(); const otherRoles = role.get(templateIdsKey).filter(r => r).uniq();
role.set('name', name); role.set('name', name);
role.set('projectRoleTemplateIds', otherRoles); role.set(templateIdsKey, otherRoles);
} }
return ok; return ok;
}, },
doneSaving() { doneSaving() {
this.get('router').transitionTo('global-admin.roles.index'); this.goBack();
}, },
}); });

View File

@ -1,9 +1,17 @@
<section class="header clearfix"> <section class="header clearfix">
<div class="pull-left"> <div class="pull-left">
{{#if editing}} {{#if editing}}
<h1>{{t 'rolesPage.editRole'}}</h1> {{#if (eq roleType "project")}}
<h1>{{t 'rolesPage.editProjectRole'}}</h1>
{{else if (eq roleType "cluster")}}
<h1>{{t 'rolesPage.editClusterRole'}}</h1>
{{/if}}
{{else}} {{else}}
<h1>{{t 'rolesPage.addRole'}}</h1> {{#if (eq roleType "project")}}
<h1>{{t 'rolesPage.addProjectRole'}}</h1>
{{else if (eq roleType "cluster")}}
<h1>{{t 'rolesPage.addClusterRole'}}</h1>
{{/if}}
{{/if}} {{/if}}
</div> </div>
</section> </section>
@ -38,7 +46,7 @@
</thead> </thead>
<tbody> <tbody>
{{#each ruleArray as |rule|}} {{#each ruleArray as |rule|}}
{{role-rule-row rule=rule remove="removeRule"}} {{role-rule-row roleType=roleType rule=rule remove="removeRule"}}
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>
@ -67,7 +75,7 @@
</thead> </thead>
<tbody> <tbody>
{{#each roleArray as |role|}} {{#each roleArray as |role|}}
{{other-role-row role=role otherRoles=model.roles remove="removeOtherRole"}} {{other-role-row role=role otherRoles=otherRoles remove="removeOtherRole"}}
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>

View File

@ -3,7 +3,6 @@ import { get, set, observer } from '@ember/object'
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
import layout from './template'; import layout from './template';
const rules = C.PROJECT_ROLE_RULES;
const verbs = C.RULE_VERBS; const verbs = C.RULE_VERBS;
export default Component.extend({ export default Component.extend({
@ -11,6 +10,7 @@ export default Component.extend({
rule: null, rule: null,
rules: null, rules: null,
resource: null, resource: null,
roleType: null,
tagName: 'TR', tagName: 'TR',
classNames: 'main-row', classNames: 'main-row',
@ -31,6 +31,7 @@ export default Component.extend({
value: currentVerbs.indexOf(verb) > -1, value: currentVerbs.indexOf(verb) > -1,
}; };
})); }));
const rules = C[`${this.get('roleType').toUpperCase()}_ROLE_RULES`];
this.set('rules', rules.map(rule => { this.set('rules', rules.map(rule => {
return { return {
label: rule, label: rule,

View File

@ -0,0 +1,6 @@
import Component from '@ember/component';
import layout from './template';
export default Component.extend({
layout,
});

View File

@ -0,0 +1,10 @@
<section class="header has-tabs clearfix p-0">
<ul class="tab-nav">
<li>{{#link-to "roles.index" (query-params type="project")}}{{t 'rolesPage.index.projectRoles'}}{{/link-to}}</li>
<li>{{#link-to "roles.index" (query-params type="cluster")}}{{t 'rolesPage.index.clusterRoles'}}{{/link-to}}</li>
</ul>
<div class="right-buttons">
{{yield}}
</div>
</section>

View File

@ -0,0 +1,7 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
queryParams: ['type'],
type: 'project',
});

View File

@ -1,4 +1,5 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import FilterState from 'ui/mixins/filter-state'; import FilterState from 'ui/mixins/filter-state';
const headers = [ const headers = [
@ -18,4 +19,22 @@ const headers = [
export default Controller.extend(FilterState, { export default Controller.extend(FilterState, {
sortBy: 'name', sortBy: 'name',
headers: headers, headers: headers,
queryParams: ['type'],
authzStore: service('authz-store'),
type: 'project',
searchText: '',
onRoleTypeChanged: function () {
const roleType = this.get('type');
return this.get('authzStore').find(`${roleType}RoleTemplate`, null, {
url: `${roleType}RoleTemplates`,
forceReload: true,
removeMissing: true,
sortBy: 'name',
})
.then((roles) => {
this.set('model', roles);
this.set('searchText', '');
});
}.observes('type'),
}); });

View File

@ -0,0 +1,7 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
queryParams: ['type'],
type: 'project',
});

View File

@ -3,11 +3,14 @@ import { inject as service } from '@ember/service';
export default Route.extend({ export default Route.extend({
authzStore: service('authz-store'), authzStore: service('authz-store'),
queryParams: {
type: 'project',
},
model: function (params) { model: function (params) {
return this.get('authzStore').find('projectRoleTemplate', null, { url: 'projectRoleTemplates', forceReload: true, removeMissing: true }).then((roles) => { return this.get('authzStore').find(`${params.type}RoleTemplate`, null, { url: `${params.type}RoleTemplates`, forceReload: true, removeMissing: true }).then((roles) => {
const role = roles.findBy('id', params.role_id); const role = roles.findBy('id', params.role_id);
if (!role) { if (!role) {
this.replaceWith('roles.index'); this.replaceWith('roles.index', { queryParams: { type: 'project' } });
} }
return { return {
role, role,

View File

@ -3,7 +3,10 @@ import { inject as service } from '@ember/service';
export default Route.extend({ export default Route.extend({
authzStore: service('authz-store'), authzStore: service('authz-store'),
model: function () { queryParams: {
return this.get('authzStore').find('projectRoleTemplate', null, { url: 'projectRoleTemplates', forceReload: true, removeMissing: true }).then((roles) => roles); type: 'project',
},
model: function (params) {
return this.get('authzStore').find(`${params.type}RoleTemplate`, null, { url: `${params.type}RoleTemplates`, forceReload: true, removeMissing: true }).then((roles) => roles);
}, },
}); });

View File

@ -3,9 +3,12 @@ import { inject as service } from '@ember/service';
export default Route.extend({ export default Route.extend({
authzStore: service('authz-store'), authzStore: service('authz-store'),
model: function () { queryParams: {
type: 'project',
},
model: function (params) {
var role = this.get('authzStore').createRecord({ var role = this.get('authzStore').createRecord({
type: 'projectRoleTemplate', type: `${params.type}RoleTemplate`,
name: '', name: '',
rules: [{ rules: [{
apiGroups: ['*'], apiGroups: ['*'],
@ -13,9 +16,9 @@ export default Route.extend({
resources: [], resources: [],
verbs: [], verbs: [],
}], }],
projectRoleTemplateIds: [''],
}); });
return this.get('authzStore').find('projectRoleTemplate', null, { url: 'projectRoleTemplates', forceReload: true, removeMissing: true }).then((roles) => { role.set(`${params.type}RoleTemplateIds`, ['']);
return this.get('authzStore').find(`${params.type}RoleTemplate`, null, { url: `${params.type}RoleTemplates`, forceReload: true, removeMissing: true }).then((roles) => {
return { return {
role: role, role: role,
roles: roles, roles: roles,

View File

@ -2,7 +2,7 @@
<section class="header has-tabs clearfix p-0"> <section class="header has-tabs clearfix p-0">
<ul class="tab-nav"> <ul class="tab-nav">
<li>{{#link-to "accounts"}}{{t 'nav.admin.accounts'}}{{/link-to}}</li> <li>{{#link-to "accounts"}}{{t 'nav.admin.accounts'}}{{/link-to}}</li>
<li>{{#link-to "roles"}}{{t 'nav.admin.roles'}}{{/link-to}}</li> <li>{{#link-to "roles.index"}}{{t 'nav.admin.roles'}}{{/link-to}}</li>
<li>{{#link-to "audit-logs"}}{{t 'nav.admin.audit'}}{{/link-to}}</li> <li>{{#link-to "audit-logs"}}{{t 'nav.admin.audit'}}{{/link-to}}</li>
<li>{{#link-to "catalog"}}{{t 'nav.admin.catalog'}}{{/link-to}}</li> <li>{{#link-to "catalog"}}{{t 'nav.admin.catalog'}}{{/link-to}}</li>
<li>{{#link-to "settings.auth"}}{{t 'nav.admin.settings.auth'}}{{/link-to}}</li> <li>{{#link-to "settings.auth"}}{{t 'nav.admin.settings.auth'}}{{/link-to}}</li>

View File

@ -1 +1 @@
{{new-edit-role model=model editing=true}} {{new-edit-role model=model editing=true roleType=type}}

View File

@ -1,18 +1,18 @@
<section class="header clearfix"> {{#roles-header showGroup=false}}
<div class="pull-left"> {{#if (eq type "project")}}
<h1>{{t 'rolesPage.index.header'}}</h1> {{#link-to "roles.new" (query-params type="project") classNames="btn btn-sm bg-primary right-divider-btn"}} {{t 'rolesPage.addProjectRole'}} {{/link-to}}
</div> {{else if (eq type "cluster")}}
<div class="right-buttons"> {{#link-to "roles.new" (query-params type="cluster") classNames="btn btn-sm bg-primary right-divider-btn"}} {{t 'rolesPage.addClusterRole'}} {{/link-to}}
{{#link-to "roles.new" classNames="btn btn-sm bg-primary right-divider-btn"}} {{t 'rolesPage.addRole'}} {{/link-to}} {{/if}}
</div> {{/roles-header}}
</section>
<section class="instances"> <section class="instances">
{{#sortable-table {{#sortable-table
classNames="grid sortable-table" classNames="grid sortable-table"
fullRows=true fullRows=true
sortBy=sortBy sortBy=sortBy
headers=headers headers=headers
searchText=searchText
body=filtered body=filtered
as |sortable kind row dt| as |sortable kind row dt|
}} }}
@ -22,7 +22,7 @@
&nbsp; &nbsp;
</td> </td>
<td data-title="{{t 'rolesPage.index.table.name'}}:" class="clip"> <td data-title="{{t 'rolesPage.index.table.name'}}:" class="clip">
{{#link-to "roles.edit" row.id}} {{#link-to "roles.edit" row.id (query-params type=type)}}
{{row.name}} {{row.name}}
{{/link-to}} {{/link-to}}
</td> </td>

View File

@ -1 +1 @@
{{new-edit-role model=model editing=false}} {{new-edit-role model=model editing=false roleType=type}}

View File

@ -49,6 +49,7 @@ export default Component.extend({
localizedLabel: false, localizedLabel: false,
// Whether to show the search input box. It maybe useful where the option list is short. // Whether to show the search input box. It maybe useful where the option list is short.
showSearch: true, showSearch: true,
searchLabel: 'generic.search',
showOptions: false, showOptions: false,
allowCustom: false, allowCustom: false,
@ -71,6 +72,7 @@ export default Component.extend({
this.set('content', []); this.set('content', []);
} }
if (this.get('allowCustom')) { if (this.get('allowCustom')) {
this.set('searchLabel', 'generic.searchOrCustomInput');
const value = this.get('value'); const value = this.get('value');
this.insertCustomValue(value, false); this.insertCustomValue(value, false);
} }

View File

@ -6,7 +6,7 @@
{{#if showOptions}} {{#if showOptions}}
<section class="searchable-options {{if showSearch '' 'pt-10'}}"> <section class="searchable-options {{if showSearch '' 'pt-10'}}">
{{#if showSearch}} {{#if showSearch}}
{{input class=(concat 'form-control ' class) placeholder=(t 'generic.searchOrCustomInput') type="text" value=filter}} {{input class=(concat 'form-control ' class) placeholder=(t searchLabel) type="text" value=filter}}
{{/if}} {{/if}}
{{#if prompt}} {{#if prompt}}
<div <div

View File

@ -124,15 +124,18 @@ accountsPage:
rolesPage: rolesPage:
index: index:
header: Roles clusterRoles: Cluster Roles
projectRoles: Project Roles
localLink: Add Role localLink: Add Role
table: table:
name: Name name: Name
created: Created Time created: Created Time
noData: There are no roles yet noData: There are no roles yet
noMatch: No roles match the current search noMatch: No roles match the current search
addRole: Add Role addProjectRole: Add Project Role
editRole: Edit Role addClusterRole: Add Cluster Role
editProjectRole: Edit Project Role
editClusterRole: Edit Cluster Role
saveEdit: Edit saveEdit: Edit
saveNew: Create saveNew: Create
new: new: