Merge branch 'ICU-512-list-auth-methods' into ICU-512-view-auth-method-details

This commit is contained in:
Susmitha Girumala committed 2020-08-13 14:58:59 -04:00
commit e367d5da8b
17 files changed
+215 -4

No files matched your search

@@ -0,0 +1,20 @@
import Fragment from 'ember-data-model-fragments/fragment';
import { attr } from '@ember-data/model';
export default class FragmentPrincipal extends Fragment {
// =attributes
@attr('string', {
description: 'ID of the related scope'
}) scope_id;
@attr('string', {
description: 'ID of the related principal (user or group)'
}) principal_id;
@attr('string', {
description: 'Type of the related principal (user or group)'
}) type;
}
+9
View File
@@ -5,6 +5,15 @@ export default class RoleModel extends GeneratedRoleModel {
// =attributes
/**
* Principals are users and groups assigned to a role. They are represented
* as references to User and Group instances. Since Ember Data relationships
* are wanting, we do not model these as a polymorphic relationship as might
* see obvious. Instead, the application layer is expected to load referenced
* users and groups as needed.
*/
@fragmentArray('fragment-principal', {readOnly: true}) principals;
/**
* Grants is read-only _most_ under normal circumstances. But grants can
* be persisted via a dedicated call to `saveGrants()`.
@@ -0,0 +1,18 @@
import JSONSerializer from '@ember-data/serializer/json';
export default class FragmentPrincipalSerializer extends JSONSerializer {
// =methods
/**
* Renames `id` to `principal_id` because fragments may not have IDs.
* Otherwise normalizes as normal.
* @param {ModelClass} typeClass
* @param {object} resourceHash
* @return {object}
*/
normalize(typeClass, { id: principal_id, ...obj }) {
return super.normalize(typeClass, { principal_id, ...obj });
}
}
@@ -0,0 +1 @@
export { default } from 'api/models/fragment-principal';
@@ -0,0 +1 @@
export { default } from 'api/serializers/fragment-principal';
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | fragment principal', function(hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function(assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('fragment-principal', {});
assert.ok(model);
});
});
@@ -0,0 +1,30 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import FragmentPrincipal from 'api/models/fragment-principal';
module('Unit | Serializer | fragment principal', function(hooks) {
setupTest(hooks);
test('it normalizes `id` into `principal_id`', function(assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('fragment-principal');
const normalized = serializer.normalize(FragmentPrincipal, {
scope_id: '1',
principal_id: '2',
type: 'user'
});
assert.deepEqual(normalized, {
data: {
attributes: {
scope_id: '1',
principal_id: '2',
type: 'user'
},
id: null,
relationships: {},
type: 'fragment-principal'
}
});
});
});
+1
View File
@@ -23,6 +23,7 @@ Router.map(function () {
this.route('roles', function () {
this.route('role', { path: ':role_id' }, function () {
this.route('grants');
this.route('principals');
});
this.route('new');
});
@@ -0,0 +1,37 @@
import Route from '@ember/routing/route';
import { all } from 'rsvp';
export default class ScopesScopeRolesRolePrincipalsRoute extends Route {
// =methods
/**
* Returns users and groups associated with this role.
* @param {object} params
* @return {Promise{RoleModel}}
*/
model() {
const adapterOptions = { scopeID: this.modelFor('scopes.scope').id };
const role = this.modelFor('scopes.scope.roles.role');
const users = role.principals
.filterBy('type', 'user')
.map(({ principal_id }) =>
this.store.findRecord('user', principal_id, { adapterOptions })
.then(model => ({
type: 'user',
model
}))
);
const groups = role.principals
.filterBy('type', 'group')
.map(({ principal_id }) =>
this.store.findRecord('group', principal_id, { adapterOptions })
.then(model => ({
type: 'group',
model
}))
);
return all(users.concat(groups));
}
}
@@ -5,6 +5,7 @@
<page.body>
{{#if @model}}
<Rose::Table as |table|>
<table.header as |header|>
<header.row as |row|>
@@ -36,6 +37,17 @@
{{/each}}
</table.body>
</Rose::Table>
{{else}}
<Rose::Layout::Centered>
<Rose::Message @title={{t 'titles.auth-methods-welcome'}} as |message|>
<message.description>
{{t 'descriptions.auth-methods-welcome'}}
</message.description>
</Rose::Message>
</Rose::Layout::Centered>
{{/if}}
</page.body>
</Rose::Layout::Page>
@@ -15,6 +15,9 @@
<nav.link @route='scopes.scope.roles.role.index'>
{{t 'titles.settings'}}
</nav.link>
<nav.link @route='scopes.scope.roles.role.principals'>
{{t 'titles.principals'}}
</nav.link>
<nav.link @route='scopes.scope.roles.role.grants'>
{{t 'titles.grants'}}
</nav.link>
@@ -0,0 +1,22 @@
{{title (t 'titles.principals')}}
<Rose::Table as |table|>
<table.header as |header|>
<header.row as |row|>
<row.cell>{{t 'form.label.name'}}</row.cell>
<row.cell>{{t 'form.label.type'}}</row.cell>
</header.row>
</table.header>
<table.body as |body|>
{{#each @model as |principal|}}
<body.row as |row|>
<row.cell>
{{principal.model.displayName}}
</row.cell>
<row.cell>
{{t (concat 'resources.' principal.type)}}
</row.cell>
</body.row>
{{/each}}
</table.body>
</Rose::Table>
+26
View File
@@ -1,4 +1,5 @@
import factory from '../generated/factories/role';
import { trait } from 'ember-cli-mirage';
export default factory.extend({
@@ -8,4 +9,29 @@ export default factory.extend({
'id=*;type=host-catalog;actions=create,read'
],
/**
* Create roles with associated "principals": related users and groups.
*/
withPrincipals: trait({
afterCreate(role, server) {
const users = server.createList('user', 2);
const groups = server.createList('group', 2);
const userPrincipalFragments = users.map(user => ({
scope_id: role.scope.id,
id: user.id,
type: 'user'
}));
const groupPrincipalFragments = groups.map(group => ({
scope_id: role.scope.id,
id: group.id,
type: 'group'
}));
const principals = [
...userPrincipalFragments,
...groupPrincipalFragments
];
role.update({ principals });
}
})
});
+3 -4
View File
@@ -18,11 +18,10 @@ export default function(server) {
// User
server.createList('user', 5, { scope });
// Role
server.createList('role', 5, { scope });
// Groups
// Group
server.createList('group', 5, { scope });
// Role
server.createList('role', 5, { scope }, 'withPrincipals');
// Other resources
+4
View File
@@ -218,6 +218,10 @@ module('Acceptance | roles', function (hooks) {
);
});
test('principals may be viewed in principals tab', async function (assert) {
assert.expect(0);
});
test('grants can be updated and saved or canceled', async function (assert) {
assert.expect(0);
});
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | scopes/scope/roles/role/principals', function(hooks) {
setupTest(hooks);
test('it exists', function(assert) {
let route = this.owner.lookup('route:scopes/scope/roles/role/principals');
assert.ok(route);
});
});
+4
View File
@@ -47,8 +47,10 @@ titles:
users-welcome: Welcome to Users
roles-welcome: Welcome to Roles
groups-welcome: Welcome to Groups
auth-methods-welcome: Welcome to Auth Methods
grants: Permissions
new-grant: New Grant
principals: Principals
descriptions:
host-catalogs: Overview of collections of hosts and host sets.
empty-set: There are no items to display yet. You may be able to add items or try back later.
@@ -57,6 +59,7 @@ descriptions:
users-welcome: Users are authorized to access Boundary.
roles-welcome: Roles are collections of capabilities that can be granted to user and/or groups.
groups-welcome: Groups are collections of users.
auth-methods-welcome: Auth methods are configurations to authenticate users.
states:
info: Info
success: Success
@@ -71,6 +74,7 @@ form:
identification: Username
password: Password
grant: Grant
principal: Principal
errors:
titles:
unauthenticated-error: You are not signed in