Skip to content

Commit 43c9389

Browse files
authored
feat: 🎸 Add grant templates for roles (#3115)
* feat: 🎸 Add grant templates for roles * test: πŸ’ Add a11y ignore * fix: πŸ› Add selected text * test: πŸ’ Update tests * refactor: πŸ’‘ Update manage scope styles to be consistent as well * Add missing copyright headers --------- Co-authored-by: ZedLi <5783847+ZedLi@users.noreply.github.com>
1 parent 79b6360 commit 43c9389

File tree

19 files changed

+758
-32
lines changed

19 files changed

+758
-32
lines changed

β€Žaddons/core/addon/styles/addon.scssβ€Ž

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
}
3232
}
3333

34+
.selected-items-text {
35+
display: flex;
36+
justify-content: end;
37+
align-items: center;
38+
height: 1rem;
39+
margin-bottom: 0.5rem;
40+
}
41+
3442
> div,
3543
table {
3644
margin-bottom: 1rem;
@@ -40,6 +48,11 @@
4048
.hds-dropdown-toggle-button__text {
4149
white-space: nowrap;
4250
}
51+
52+
// Remove margin if the segmented group is followed by the selected items text
53+
&:has(+ .selected-items-text) {
54+
margin-bottom: 0;
55+
}
4356
}
4457

4558
.search-filtering-toolbar {

β€Žaddons/core/translations/resources/en-us.yamlβ€Ž

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,53 @@ role:
613613
description: Grants are permissions which allow roles to take actions and access resources.
614614
actions:
615615
create: New Grant
616+
grant-templates:
617+
title_plural: Grant Templates
618+
actions:
619+
add-grant-templates: Add Grant Templates
620+
messages:
621+
add-grant-templates:
622+
title: Add Grant Templates
623+
description: Select grant templates to add to this role.
624+
titles:
625+
grant-strings: Grant Strings
626+
templates:
627+
global-admin:
628+
name: Global Admin
629+
description: Full administrative access to all resources in Boundary
630+
org-admin:
631+
name: Org Admin
632+
description: Administrative access to all resources within an organization
633+
project-admin:
634+
name: Project Admin
635+
description: Administrative access to all resources within a project
636+
session-manager:
637+
name: Session Manager
638+
description: Can view and cancel sessions
639+
target-manager:
640+
name: Target Manager
641+
description: Can create and manage targets
642+
host-resource-manager:
643+
name: Host Resource Manager
644+
description: Can manage hosts and host catalogs
645+
credential-manager:
646+
name: Credential Manager
647+
description: Can manage credentials and credential stores
648+
user-manager:
649+
name: User Manager
650+
description: Can manage users, groups, and accounts
651+
auth-method-manager:
652+
name: Auth Method Manager
653+
description: Can manage auth methods and managed groups
654+
session-auditor:
655+
name: Session Auditor
656+
description: Can view session recordings
657+
target-user:
658+
name: Target User
659+
description: Can connect to targets but cannot manage anything
660+
read-only-user:
661+
name: Read-Only User
662+
description: Can view but not modify any resources
616663
scope:
617664
title: Scope
618665
title_plural: Scopes
@@ -624,7 +671,6 @@ role:
624671
parent_scope: Parent scope
625672
resource_type: Resource type
626673
projects_selected: Projects selected
627-
selected: '{items} selected out of {total}'
628674
messages:
629675
none:
630676
title: No scopes added

β€Žaddons/core/translations/states/en-us.yamlβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ incomplete-loading:
2929
refreshing:
3030
description: Updating cache...
3131
tooltip: Some items may not appear until the cache is finished updating
32+
selected-items: '{items} selected out of {total}'
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{{!
2+
Copyright (c) HashiCorp, Inc.
3+
SPDX-License-Identifier: BUSL-1.1
4+
}}
5+
6+
<Rose::Form
7+
class='full-width search-filtering'
8+
@onSubmit={{this.submit}}
9+
@cancel={{@cancel}}
10+
@disabled={{@model.isSaving}}
11+
as |form|
12+
>
13+
<Hds::SegmentedGroup as |S|>
14+
<S.TextInput
15+
@value={{@search}}
16+
@type='search'
17+
placeholder={{t 'actions.search'}}
18+
aria-label={{t 'actions.search'}}
19+
{{on 'input' @handleSearchInput}}
20+
/>
21+
</Hds::SegmentedGroup>
22+
23+
{{#if this.paginatedTemplates}}
24+
25+
<div class='selected-items-text'>
26+
{{#if this.selectedGrantTemplates}}
27+
<Hds::Text::Body @tag='p' @color='faint'>
28+
{{t
29+
'states.selected-items'
30+
items=this.selectedGrantTemplates.length
31+
total=this.totalCount
32+
}}
33+
</Hds::Text::Body>
34+
{{/if}}
35+
</div>
36+
37+
<Hds::Table
38+
@model={{this.paginatedTemplates}}
39+
@columns={{array
40+
(hash label=(t 'form.name.label'))
41+
(hash label=(t 'resources.role.grant-templates.titles.grant-strings'))
42+
}}
43+
@isSelectable={{true}}
44+
@onSelectionChange={{this.onSelectionChange}}
45+
@valign='middle'
46+
>
47+
<:body as |B|>
48+
<B.Tr
49+
@selectionKey={{B.data.id}}
50+
@isSelected={{includes B.data.id this.selectedGrantTemplates}}
51+
@selectionAriaLabelSuffix='row {{B.data.id}}'
52+
>
53+
<B.Td>
54+
<Hds::Text::Body @tag='p' @weight='semibold'>
55+
{{B.data.name}}
56+
</Hds::Text::Body>
57+
<Hds::Text::Body @tag='p'>
58+
{{B.data.description}}
59+
</Hds::Text::Body>
60+
</B.Td>
61+
<B.Td>
62+
<ul>
63+
{{#each B.data.grantStrings as |grantString|}}
64+
<li>
65+
<Hds::Text::Code @tag='code'>{{grantString}}</Hds::Text::Code>
66+
</li>
67+
{{/each}}
68+
</ul>
69+
</B.Td>
70+
</B.Tr>
71+
</:body>
72+
</Hds::Table>
73+
<Rose::Pagination
74+
@totalItems={{this.filteredTemplates.length}}
75+
@currentPage={{@page}}
76+
@currentPageSize={{@pageSize}}
77+
/>
78+
<form.actions
79+
@submitText={{t
80+
'resources.role.grant-templates.actions.add-grant-templates'
81+
}}
82+
@cancelText={{t 'actions.cancel'}}
83+
/>
84+
{{else}}
85+
<Hds::ApplicationState data-test-no-grant-template-results as |A|>
86+
<A.Header @title={{t 'titles.no-results-found'}} />
87+
<A.Body
88+
@text={{t
89+
'descriptions.no-search-results'
90+
query=@search
91+
resource=(t 'resources.role.grant-templates.title_plural')
92+
}}
93+
/>
94+
</Hds::ApplicationState>
95+
{{/if}}
96+
97+
</Rose::Form>
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* Copyright IBM Corp. 2021, 2026
3+
* SPDX-License-Identifier: BUSL-1.1
4+
*/
5+
6+
import Component from '@glimmer/component';
7+
import { action } from '@ember/object';
8+
import { tracked } from '@glimmer/tracking';
9+
import { service } from '@ember/service';
10+
11+
export default class FormRoleAddGrantTemplatesIndexComponent extends Component {
12+
// =services
13+
14+
@service intl;
15+
16+
// =properties
17+
18+
@tracked selectedGrantTemplates = [];
19+
20+
/**
21+
* Grant template definitions with their grant strings.
22+
* @type {Array<{id: string, grantStrings: Array<string>}>}
23+
*/
24+
grantTemplateDefinitions = [
25+
{
26+
id: 'global-admin',
27+
grantStrings: ['ids=*;type=*;actions=*'],
28+
},
29+
{
30+
id: 'org-admin',
31+
grantStrings: [
32+
'ids=<org-id>;type=scope;actions=*',
33+
'ids=*;type=auth-method;actions=*',
34+
'ids=*;type=user;actions=*',
35+
'ids=*;type=group;actions=*',
36+
'ids=*;type=role;actions=*',
37+
],
38+
},
39+
{
40+
id: 'project-admin',
41+
grantStrings: [
42+
'ids=<project-id>;type=scope;actions=*',
43+
'ids=*;type=host-catalog;actions=*',
44+
'ids=*;type=target;actions=*',
45+
'ids=*;type=credential-store;actions=*',
46+
'ids=*;type=session;actions=*',
47+
],
48+
},
49+
{
50+
id: 'session-manager',
51+
grantStrings: [
52+
'ids=*;type=session;actions=list,read,cancel',
53+
'ids=*;type=target;actions=read,list',
54+
],
55+
},
56+
{
57+
id: 'target-manager',
58+
grantStrings: [
59+
'ids=*;type=target;actions=create,list,read,update,delete,add-host-sources,set-host-sources,remove-host-sources,add-credential-sources,set-credential-sources,remove-credential-sources',
60+
],
61+
},
62+
{
63+
id: 'host-resource-manager',
64+
grantStrings: [
65+
'ids=*;type=host-catalog;actions=create,list,read,update,delete',
66+
'ids=*;type=host-set;actions=create,read,update,delete,add-hosts,set-hosts,remove-hosts',
67+
'ids=*;type=host;actions=create,read,update,delete',
68+
],
69+
},
70+
{
71+
id: 'credential-manager',
72+
grantStrings: [
73+
'ids=*;type=credential;actions=create,read,update,delete',
74+
'ids=*;type=credential-store;actions=create,list,read,update,delete',
75+
'ids=*;type=credential-library;actions=create,read,update,delete',
76+
],
77+
},
78+
{
79+
id: 'user-manager',
80+
grantStrings: [
81+
'ids=*;type=user;actions=create,list,read,update,delete,add-accounts,set-accounts,remove-accounts',
82+
'ids=*;type=group;actions=create,list,read,update,delete,add-members,set-members,remove-members',
83+
'ids=*;type=account;actions=create,read,update,delete',
84+
],
85+
},
86+
{
87+
id: 'auth-method-manager',
88+
grantStrings: [
89+
'ids=*;type=auth-method;actions=create,list,read,update,delete,change-state',
90+
'ids=*;type=managed-group;actions=create,read,update,delete',
91+
],
92+
},
93+
{
94+
id: 'session-auditor',
95+
grantStrings: [
96+
'ids=*;type=session;actions=list,read',
97+
'ids=*;type=session-recording;actions=list,read,download',
98+
],
99+
},
100+
{
101+
id: 'target-user',
102+
grantStrings: [
103+
'ids=*;type=target;actions=authorize-session,read,list',
104+
'ids=*;type=session;actions=read:self,cancel:self',
105+
],
106+
},
107+
{
108+
id: 'read-only-user',
109+
grantStrings: ['ids=*;type=session;actions=list,read,cancel'],
110+
},
111+
];
112+
113+
/**
114+
* Grant templates with translated names and descriptions.
115+
* @type {Array<{id: string, name: string, grantStrings: Array<string>, description: string}>}
116+
*/
117+
get grantTemplates() {
118+
return this.grantTemplateDefinitions.map((template) => ({
119+
id: template.id,
120+
name: this.intl.t(
121+
`resources.role.grant-templates.templates.${template.id}.name`,
122+
),
123+
grantStrings: template.grantStrings,
124+
description: this.intl.t(
125+
`resources.role.grant-templates.templates.${template.id}.description`,
126+
),
127+
}));
128+
}
129+
130+
/**
131+
* Filtered grant templates based on search query.
132+
* @return {Array<{id: string, name: string, grantStrings: Array<string>, description: string}>}
133+
*/
134+
get filteredTemplates() {
135+
let filteredTemplates = this.grantTemplates;
136+
137+
if (this.args.search) {
138+
filteredTemplates = this.grantTemplates.filter((template) => {
139+
const searchTerm = this.args.search?.toLowerCase() ?? '';
140+
return (
141+
template.name.toLowerCase().includes(searchTerm) ||
142+
template.description.toLowerCase().includes(searchTerm) ||
143+
template.grantStrings.some((grant) => grant.includes(searchTerm))
144+
);
145+
});
146+
}
147+
148+
return filteredTemplates;
149+
}
150+
151+
/**
152+
* Paginated templates for current page.
153+
* @type {Array}
154+
*/
155+
get paginatedTemplates() {
156+
return this.filteredTemplates.slice(
157+
(this.args.page - 1) * this.args.pageSize,
158+
this.args.page * this.args.pageSize,
159+
);
160+
}
161+
162+
get totalCount() {
163+
return this.grantTemplateDefinitions.length;
164+
}
165+
// =actions
166+
167+
@action
168+
onSelectionChange({ selectionKey, selectedRowsKeys }) {
169+
const oldKeys = new Set(this.selectedGrantTemplates);
170+
if (selectionKey === 'all') {
171+
const newKeys = new Set(selectedRowsKeys);
172+
if (newKeys.size === 0) {
173+
this.selectedGrantTemplates = [...newKeys.difference(oldKeys)];
174+
} else {
175+
this.selectedGrantTemplates = [...newKeys.union(oldKeys)];
176+
}
177+
} else {
178+
if (oldKeys.has(selectionKey)) {
179+
oldKeys.delete(selectionKey);
180+
} else {
181+
oldKeys.add(selectionKey);
182+
}
183+
this.selectedGrantTemplates = [...oldKeys];
184+
}
185+
}
186+
187+
@action
188+
submit() {
189+
// Convert selected template IDs to their grant strings
190+
const grantStrings = this.grantTemplates
191+
.filter((template) => this.selectedGrantTemplates.includes(template.id))
192+
.flatMap((template) => template.grantStrings);
193+
this.args.submit(grantStrings);
194+
}
195+
}

0 commit comments

Comments
Β (0)