Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
798cfc6828 | ||
|
|
d93986c4e2 | ||
|
|
879c196887 | ||
|
|
98170ed01e | ||
|
|
47a218cf8f | ||
|
|
879a58dcb8 | ||
|
|
f100190ac1 | ||
|
|
0902e42cb0 | ||
|
|
6b0958e4d8 | ||
|
|
82ea70f44b | ||
|
|
0bb72040db | ||
|
|
9d9a38510b | ||
|
|
a54aecee07 | ||
|
|
7dab184bb9 | ||
|
|
f068ac0b0c | ||
|
|
d07c33ca41 | ||
|
|
f073de774d | ||
|
|
1040a05880 |
No files matched your search
@@ -19,7 +19,7 @@ jobs:
|
|||||||
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
@@ -37,17 +37,17 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn run build:ui:admin:oss
|
- run: yarn run build:ui:admin:oss
|
||||||
- name: Upload artifact admin-ui
|
- name: Upload artifact admin-ui
|
||||||
uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
with:
|
with:
|
||||||
name: admin-ui
|
name: admin-ui
|
||||||
path: ./ui/admin/dist/
|
path: ./ui/admin/dist/
|
||||||
- name: Upload artifact OSS admin UI
|
- name: Upload artifact OSS admin UI
|
||||||
uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
with:
|
with:
|
||||||
name: admin-ui-oss
|
name: admin-ui-oss
|
||||||
path: ./ui/admin/dist/
|
path: ./ui/admin/dist/
|
||||||
@@ -65,12 +65,12 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn run build:ui:admin:enterprise
|
- run: yarn run build:ui:admin:enterprise
|
||||||
- name: Upload artifact enterprise admin UI
|
- name: Upload artifact enterprise admin UI
|
||||||
uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
with:
|
with:
|
||||||
name: admin-ui-ent
|
name: admin-ui-ent
|
||||||
path: ./ui/admin/dist/
|
path: ./ui/admin/dist/
|
||||||
@@ -88,12 +88,12 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn run build:ui:admin:hcp
|
- run: yarn run build:ui:admin:hcp
|
||||||
- name: Upload artifact HCP admin UI
|
- name: Upload artifact HCP admin UI
|
||||||
uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
with:
|
with:
|
||||||
name: admin-ui-hcp
|
name: admin-ui-hcp
|
||||||
path: ./ui/admin/dist/
|
path: ./ui/admin/dist/
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@e6a47e25652c5dbefe5b65d3b8e8dab0e9364b21 # codeql-bundle-v2.16.0
|
uses: github/codeql-action/init@592977e6ae857384aa79bb31e7a1d62d63449ec5 # codeql-bundle-v2.16.3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -54,4 +54,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@e6a47e25652c5dbefe5b65d3b8e8dab0e9364b21 # codeql-bundle-v2.16.0
|
uses: github/codeql-action/analyze@592977e6ae857384aa79bb31e7a1d62d63449ec5 # codeql-bundle-v2.16.3
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn run compliance:licenses
|
- run: yarn run compliance:licenses
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
node-modules-
|
node-modules-
|
||||||
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: '**/node_modules'
|
path: '**/node_modules'
|
||||||
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
key: node-modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
|
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import BaseModel from '../../models/base';
|
||||||
|
import { attr } from '@ember-data/model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias contains all fields related to an Alias resource
|
||||||
|
*/
|
||||||
|
export default class GeneratedAliasModel extends BaseModel {
|
||||||
|
// =attributes
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
description: 'The type of the resource, to help differentiate schemas',
|
||||||
|
})
|
||||||
|
type;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
description: 'Optional name for identification purposes.',
|
||||||
|
})
|
||||||
|
name;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
description: 'Optional user-set description for identification purposes.',
|
||||||
|
})
|
||||||
|
description;
|
||||||
|
|
||||||
|
@attr('date', {
|
||||||
|
description: 'The time this resource was created\nOutput only.',
|
||||||
|
readOnly: true,
|
||||||
|
})
|
||||||
|
created_time;
|
||||||
|
|
||||||
|
@attr('date', {
|
||||||
|
description: 'The time this resource was last updated\nOutput only.',
|
||||||
|
readOnly: true,
|
||||||
|
})
|
||||||
|
updated_time;
|
||||||
|
|
||||||
|
@attr('number', {
|
||||||
|
description: 'Current version number of this resource.',
|
||||||
|
})
|
||||||
|
version;
|
||||||
|
|
||||||
|
@attr('string', {
|
||||||
|
description: 'This is the id of the resource that this Alias points to.',
|
||||||
|
})
|
||||||
|
destination_id;
|
||||||
|
|
||||||
|
@attr('object', {
|
||||||
|
isNestedAttribute: true,
|
||||||
|
description:
|
||||||
|
'This contains a host_id which is the id of the host that the session will be authorized for.',
|
||||||
|
})
|
||||||
|
authorize_session_arguments;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import GeneratedAliasModel from '../generated/models/alias';
|
||||||
|
|
||||||
|
export const TYPE_ALIAS_TARGET = 'target';
|
||||||
|
export const TYPES_ALIAS = Object.freeze([TYPE_ALIAS_TARGET]);
|
||||||
|
export default class AliasModel extends GeneratedAliasModel {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default } from 'api/models/alias';
|
||||||
@@ -753,6 +753,22 @@ function routes() {
|
|||||||
return policies.create(attrs);
|
return policies.create(attrs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Alias
|
||||||
|
this.get(
|
||||||
|
'/aliases',
|
||||||
|
({ aliases }, { queryParams: { scope_id: scopeId } }) => {
|
||||||
|
return aliases.where({ scopeId });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.get('/aliases/:id');
|
||||||
|
this.del('/aliases/:id');
|
||||||
|
this.patch('/aliases/:id');
|
||||||
|
this.post('/aliases', function ({ aliases }) {
|
||||||
|
const attrs = this.normalizedRequestAttrs();
|
||||||
|
|
||||||
|
return aliases.create(attrs);
|
||||||
|
});
|
||||||
|
|
||||||
// session recordings
|
// session recordings
|
||||||
this.get(
|
this.get(
|
||||||
'/session-recordings',
|
'/session-recordings',
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import factory from '../generated/factories/alias';
|
||||||
|
import permissions from '../helpers/permissions';
|
||||||
|
import generateId from '../helpers/id';
|
||||||
|
|
||||||
|
const destinationIDs = ['', generateId('t_')];
|
||||||
|
|
||||||
|
export default factory.extend({
|
||||||
|
id: () => generateId('alt_'),
|
||||||
|
destination_id: (i) => destinationIDs[i % destinationIDs.length],
|
||||||
|
authorized_actions: () =>
|
||||||
|
permissions.authorizedActionsFor('alias') || [
|
||||||
|
'no-op',
|
||||||
|
'read',
|
||||||
|
'update',
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -33,12 +33,13 @@ export default factory.extend({
|
|||||||
roles: ['create', 'list'],
|
roles: ['create', 'list'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Worker permissions only available on the global scope
|
// Resources that are available only on the global scope
|
||||||
if (this.type === 'global') {
|
if (this.type === 'global') {
|
||||||
collectionActions.workers = ['create:worker-led', 'list'];
|
collectionActions.workers = ['create:worker-led', 'list'];
|
||||||
|
collectionActions.aliases = ['create', 'list'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session recording resources are only available on global or org scope
|
// Resources that are only available on the global or the org scope
|
||||||
if (this.type === 'global' || this.type === 'org') {
|
if (this.type === 'global' || this.type === 'org') {
|
||||||
collectionActions['storage-buckets'] = ['create', 'list'];
|
collectionActions['storage-buckets'] = ['create', 'list'];
|
||||||
collectionActions['session-recordings'] = ['list'];
|
collectionActions['session-recordings'] = ['list'];
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Factory } from 'miragejs';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { TYPE_ALIAS_TARGET } from 'api/models/alias';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeneratedAliasModelFactory
|
||||||
|
*/
|
||||||
|
export default Factory.extend({
|
||||||
|
name: () => faker.word.words(),
|
||||||
|
type: TYPE_ALIAS_TARGET,
|
||||||
|
value: () => faker.word.words(),
|
||||||
|
description: () => faker.word.words(),
|
||||||
|
created_time: () => faker.date.recent(),
|
||||||
|
updated_time: () => faker.date.recent(),
|
||||||
|
version: () => faker.number.int(),
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { belongsTo } from 'miragejs';
|
||||||
|
import Model from './base';
|
||||||
|
|
||||||
|
export default Model.extend({
|
||||||
|
scope: belongsTo(),
|
||||||
|
});
|
||||||
@@ -108,6 +108,9 @@ export default function (server) {
|
|||||||
server.createList('policy', 3, { scope: globalScope });
|
server.createList('policy', 3, { scope: globalScope });
|
||||||
server.createList('policy', 3, { scope: orgScope });
|
server.createList('policy', 3, { scope: orgScope });
|
||||||
|
|
||||||
|
// Aliases
|
||||||
|
server.createList('alias', 3, { scope: globalScope });
|
||||||
|
|
||||||
// Other resources
|
// Other resources
|
||||||
server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => {
|
server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => {
|
||||||
server.createList('host-catalog', 8, { scope }, 'withChildren');
|
server.createList('host-catalog', 8, { scope }, 'withChildren');
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module('Unit | Model | alias', function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
let store = this.owner.lookup('service:store');
|
||||||
|
let model = store.createRecord('alias', {});
|
||||||
|
assert.ok(model);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -999,3 +999,8 @@ policy:
|
|||||||
label: Do not delete
|
label: Do not delete
|
||||||
custom:
|
custom:
|
||||||
label: Custom
|
label: Custom
|
||||||
|
alias:
|
||||||
|
title: Alias
|
||||||
|
title_plural: Aliases
|
||||||
|
titles:
|
||||||
|
new: New Alias
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'deleteAccount' @model)}}
|
{{on 'click' (fn @delete @model)}}
|
||||||
>
|
>
|
||||||
{{t 'resources.account.actions.delete'}}
|
{{t 'resources.account.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'deleteManagedGroup' @model)}}
|
{{on 'click' (fn @delete @model)}}
|
||||||
>
|
>
|
||||||
{{t 'resources.managed-group.actions.delete'}}
|
{{t 'resources.managed-group.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'delete' @model)}}
|
{{on 'click' @delete}}
|
||||||
>
|
>
|
||||||
{{t 'resources.credential-store.actions.delete'}}
|
{{t 'resources.credential-store.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
|
|||||||
@@ -10,58 +10,48 @@
|
|||||||
@showEditToggle={{if @model.isNew false true}}
|
@showEditToggle={{if @model.isNew false true}}
|
||||||
as |form|
|
as |form|
|
||||||
>
|
>
|
||||||
<form.input
|
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
|
||||||
@value={{@model.type}}
|
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||||
@label={{t 'form.type.label'}}
|
</InfoField>
|
||||||
readonly={{true}}
|
|
||||||
@disabled={{true}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#unless @model.isNew}}
|
{{#unless @model.isNew}}
|
||||||
<form.input
|
|
||||||
@value={{@model.issuer}}
|
<InfoField @value={{@model.issuer}} readonly={{true}} as |F|>
|
||||||
@label={{t 'form.issuer.label'}}
|
<F.Label>{{t 'form.issuer.label'}}</F.Label>
|
||||||
readonly={{true}}
|
</InfoField>
|
||||||
@disabled={{true}}
|
|
||||||
/>
|
<InfoField @value={{@model.subject}} readonly={{true}} as |F|>
|
||||||
<form.input
|
<F.Label>{{t 'form.subject.label'}}</F.Label>
|
||||||
@value={{@model.subject}}
|
</InfoField>
|
||||||
@label={{t 'form.subject.label'}}
|
|
||||||
readonly={{true}}
|
<InfoField @value={{@model.email}} readonly={{true}} as |F|>
|
||||||
@disabled={{true}}
|
<F.Label>{{t 'form.email.label'}}</F.Label>
|
||||||
/>
|
</InfoField>
|
||||||
<form.input
|
|
||||||
@value={{@model.email}}
|
<InfoField @value={{@model.full_name}} readonly={{true}} as |F|>
|
||||||
@label={{t 'form.email.label'}}
|
<F.Label>{{t 'form.full_name.label'}}</F.Label>
|
||||||
readonly={{true}}
|
</InfoField>
|
||||||
@disabled={{true}}
|
|
||||||
/>
|
|
||||||
<form.input
|
|
||||||
@value={{@model.full_name}}
|
|
||||||
@label={{t 'form.full_name.label'}}
|
|
||||||
readonly={{true}}
|
|
||||||
@disabled={{true}}
|
|
||||||
/>
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='name'
|
name='name'
|
||||||
@type='name'
|
|
||||||
@value={{@model.name}}
|
@value={{@model.name}}
|
||||||
@label={{t 'form.name.label'}}
|
@isInvalid={{@model.errors.name}}
|
||||||
@helperText={{t 'form.name.help'}}
|
@isOptional={{true}}
|
||||||
@error={{@model.errors.name}}
|
disabled={{form.disabled}}
|
||||||
readonly={{false}}
|
{{on 'input' (set-from-event @model 'name')}}
|
||||||
as |field|
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||||
|
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||||
{{#if @model.errors.name}}
|
{{#if @model.errors.name}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.name as |error|}}
|
{{#each @model.errors.name as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
|
|
||||||
<Hds::Form::Textarea::Field
|
<Hds::Form::Textarea::Field
|
||||||
@isOptional={{true}}
|
@isOptional={{true}}
|
||||||
@@ -83,41 +73,41 @@
|
|||||||
</Hds::Form::Textarea::Field>
|
</Hds::Form::Textarea::Field>
|
||||||
|
|
||||||
{{#if @model.isNew}}
|
{{#if @model.isNew}}
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='issuer'
|
name='issuer'
|
||||||
@type='text'
|
|
||||||
@value={{@model.issuer}}
|
@value={{@model.issuer}}
|
||||||
@label={{t 'form.issuer.label'}}
|
@isInvalid={{@model.errors.issuer}}
|
||||||
@error={{@model.errors.issuer}}
|
disabled={{form.disabled}}
|
||||||
readonly={{false}}
|
{{on 'input' (set-from-event @model 'issuer')}}
|
||||||
as |field|
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.issuer.label'}}</F.Label>
|
||||||
{{#if @model.errors.issuer}}
|
{{#if @model.errors.issuer}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.issuer as |error|}}
|
{{#each @model.errors.issuer as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
|
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='subject'
|
name='subject'
|
||||||
@type='text'
|
|
||||||
@value={{@model.subject}}
|
@value={{@model.subject}}
|
||||||
@label={{t 'form.subject.label'}}
|
@isInvalid={{@model.errors.subject}}
|
||||||
@error={{@model.errors.subject}}
|
disabled={{form.disabled}}
|
||||||
readonly={{false}}
|
{{on 'input' (set-from-event @model 'subject')}}
|
||||||
as |field|
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.subject.label'}}</F.Label>
|
||||||
{{#if @model.errors.subject}}
|
{{#if @model.errors.subject}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.subject as |error|}}
|
{{#each @model.errors.subject as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (can 'save model' @model)}}
|
{{#if (can 'save model' @model)}}
|
||||||
|
|||||||
@@ -10,37 +10,35 @@
|
|||||||
@showEditToggle={{if @model.isNew false true}}
|
@showEditToggle={{if @model.isNew false true}}
|
||||||
as |form|
|
as |form|
|
||||||
>
|
>
|
||||||
<form.input
|
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
|
||||||
@value={{@model.type}}
|
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||||
@label={{t 'form.type.label'}}
|
</InfoField>
|
||||||
readonly={{true}}
|
|
||||||
@disabled={{true}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='name'
|
name='name'
|
||||||
@type='name'
|
|
||||||
@value={{@model.name}}
|
@value={{@model.name}}
|
||||||
@label={{t 'form.name.label'}}
|
@isInvalid={{@model.errors.name}}
|
||||||
@helperText={{t 'form.name.help'}}
|
@isOptional={{true}}
|
||||||
@error={{@model.errors.name}}
|
disabled={{form.disabled}}
|
||||||
readonly={{false}}
|
{{on 'input' (set-from-event @model 'name')}}
|
||||||
as |field|
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||||
|
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||||
{{#if @model.errors.name}}
|
{{#if @model.errors.name}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.name as |error|}}
|
{{#each @model.errors.name as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
|
|
||||||
<Hds::Form::Textarea::Field
|
<Hds::Form::Textarea::Field
|
||||||
@isOptional={{true}}
|
name='description'
|
||||||
@value={{@model.description}}
|
@value={{@model.description}}
|
||||||
@isInvalid={{@model.errors.description}}
|
@isInvalid={{@model.errors.description}}
|
||||||
name='description'
|
@isOptional={{true}}
|
||||||
disabled={{form.disabled}}
|
disabled={{form.disabled}}
|
||||||
as |F|
|
as |F|
|
||||||
>
|
>
|
||||||
@@ -55,43 +53,45 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</Hds::Form::Textarea::Field>
|
</Hds::Form::Textarea::Field>
|
||||||
|
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='login_name'
|
name='login_name'
|
||||||
@type='text'
|
|
||||||
@value={{@model.login_name}}
|
@value={{@model.login_name}}
|
||||||
@label={{t 'form.login_name.label'}}
|
@isInvalid={{@model.errors.login_name}}
|
||||||
@error={{@model.errors.login_name}}
|
@isRequired={{true}}
|
||||||
readonly={{false}}
|
disabled={{form.disabled}}
|
||||||
as |field|
|
{{on 'input' (set-from-event @model 'login_name')}}
|
||||||
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.login_name.label'}}</F.Label>
|
||||||
{{#if @model.errors.login_name}}
|
{{#if @model.errors.login_name}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.login_name as |error|}}
|
{{#each @model.errors.login_name as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
|
|
||||||
{{#if @model.isNew}}
|
{{#if @model.isNew}}
|
||||||
<form.input
|
<Hds::Form::TextInput::Field
|
||||||
@name='password'
|
name='password'
|
||||||
@type='password'
|
@type='password'
|
||||||
@value={{this.password}}
|
@value={{@model.password}}
|
||||||
@label={{t 'form.password.label'}}
|
@isInvalid={{@model.errors.password}}
|
||||||
@error={{@model.errors.password}}
|
disabled={{form.disabled}}
|
||||||
readonly={{false}}
|
|
||||||
autocomplete='new-password'
|
autocomplete='new-password'
|
||||||
as |field|
|
{{on 'input' (set-from-event @model 'password')}}
|
||||||
|
as |F|
|
||||||
>
|
>
|
||||||
|
<F.Label>{{t 'form.password.label'}}</F.Label>
|
||||||
{{#if @model.errors.password}}
|
{{#if @model.errors.password}}
|
||||||
<field.errors as |errors|>
|
<F.Error as |E|>
|
||||||
{{#each @model.errors.password as |error|}}
|
{{#each @model.errors.password as |error|}}
|
||||||
<errors.message>{{error.message}}</errors.message>
|
<E.Message>{{error.message}}</E.Message>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</field.errors>
|
</F.Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form.input>
|
</Hds::Form::TextInput::Field>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (can 'save model' @model)}}
|
{{#if (can 'save model' @model)}}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'delete' @model)}}
|
{{on 'click' @delete}}
|
||||||
>
|
>
|
||||||
{{t 'resources.group.actions.delete'}}
|
{{t 'resources.group.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
|
|||||||
@@ -5,9 +5,45 @@
|
|||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
export default class AccountChangePasswordController extends Controller {
|
export default class AccountChangePasswordController extends Controller {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service session;
|
@service session;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the password on the account and then redirects to the index route.
|
||||||
|
* @param {AccountModel} account
|
||||||
|
* @param {string} currentPassword
|
||||||
|
* @param {string} newPassword
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.save-success')
|
||||||
|
async changePassword(account, currentPassword, newPassword) {
|
||||||
|
await account.changePassword(currentPassword, newPassword);
|
||||||
|
// Transition to index after success. We ignore errors in the transition,
|
||||||
|
// since the index route is responsible for additional
|
||||||
|
// redirects depending on the authentication context. These additional
|
||||||
|
// redirects may abort this initial transition, resulting in a
|
||||||
|
// TransitionAborted error, which we do not want to show to the user.
|
||||||
|
try {
|
||||||
|
await this.router.replaceWith('index');
|
||||||
|
} catch (e) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On cancel, redirects to index route for further processing.
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
cancel() {
|
||||||
|
this.router.replaceWith('index');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,73 @@
|
|||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
import { getOwner } from '@ember/application';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
|
||||||
export default class ApplicationController extends Controller {
|
export default class ApplicationController extends Controller {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service session;
|
@service session;
|
||||||
@service flashMessages;
|
@service features;
|
||||||
@service featureEdition;
|
@service featureEdition;
|
||||||
|
@service flashMessages;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates invalidation to the session service.
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
invalidateSession() {
|
||||||
|
this.session.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the specified color theme to the root ember element.
|
||||||
|
* @param {string} theme - "light", "dark", or nullish (system default)
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
toggleTheme(theme) {
|
||||||
|
const rootElementSelector = getOwner(this).rootElement;
|
||||||
|
const rootEl = getOwner(this)
|
||||||
|
.lookup('service:-document')
|
||||||
|
.querySelector(rootElementSelector);
|
||||||
|
this.session.set('data.theme', theme);
|
||||||
|
switch (theme) {
|
||||||
|
case 'light':
|
||||||
|
rootEl.classList.add('rose-theme-light');
|
||||||
|
rootEl.classList.remove('rose-theme-dark');
|
||||||
|
break;
|
||||||
|
case 'dark':
|
||||||
|
rootEl.classList.add('rose-theme-dark');
|
||||||
|
rootEl.classList.remove('rose-theme-light');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rootEl.classList.remove('rose-theme-dark');
|
||||||
|
rootEl.classList.remove('rose-theme-light');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to specified edition.
|
||||||
|
* Options are oss, enterprise, and hcp.
|
||||||
|
* @param {string} edition
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
toggleEdition(edition) {
|
||||||
|
this.featureEdition.setEdition(edition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} feature
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
toggleFeature(feature) {
|
||||||
|
if (this.features.isEnabled(feature)) {
|
||||||
|
this.features.disable(feature);
|
||||||
|
} else {
|
||||||
|
this.features.enable(feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesAliasIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/aliases/index') aliases;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesIndexController extends Controller {
|
||||||
|
// =services
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesNewController extends Controller {
|
||||||
|
@controller('scopes/scope/aliases/index') aliases;
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/accounts/index') accounts;
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSetPasswordController extends Controller {
|
||||||
|
// =controllers
|
||||||
|
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/accounts/index') accounts;
|
||||||
|
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set password for the specified account.
|
||||||
|
* @param {AccountModel} account
|
||||||
|
* @param {string} password
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.save-success')
|
||||||
|
async setPassword(account, password) {
|
||||||
|
await account.setPassword(password);
|
||||||
|
await this.router.replaceWith(
|
||||||
|
'scopes.scope.auth-methods.auth-method.accounts.account.set-password',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsIndexController extends Controller {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on an account.
|
||||||
|
* @param {AccountModel} account
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(account) {
|
||||||
|
const { isNew } = account;
|
||||||
|
account.rollbackAttributes();
|
||||||
|
if (isNew) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.accounts',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save an account in current scope.
|
||||||
|
* @param {AccountModel} account
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess(({ isNew }) =>
|
||||||
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||||
|
)
|
||||||
|
async save(account, password) {
|
||||||
|
const { isNew } = account;
|
||||||
|
const adapterOptions = {};
|
||||||
|
if (isNew) {
|
||||||
|
adapterOptions.password = password;
|
||||||
|
}
|
||||||
|
await account.save({ adapterOptions });
|
||||||
|
if (this.can.can('read model', account)) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.accounts.account',
|
||||||
|
account,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.accounts',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an account in current scope and redirect to index
|
||||||
|
* @param {AccountModel} account
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(account) {
|
||||||
|
await account.destroyRecord();
|
||||||
|
await this.router.replaceWith(
|
||||||
|
'scopes.scope.auth-methods.auth-method.accounts',
|
||||||
|
);
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsNewController extends Controller {
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/accounts/index') accounts;
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsIndexController extends Controller {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on a managed group.
|
||||||
|
* @param {ManagedGroupModel} managedGroup
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(managedGroup) {
|
||||||
|
const { isNew } = managedGroup;
|
||||||
|
managedGroup.rollbackAttributes();
|
||||||
|
if (isNew) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.managed-groups',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a managed group in current scope.
|
||||||
|
* @param {ManagedGroupModel} managedGroup
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess(({ isNew }) =>
|
||||||
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||||
|
)
|
||||||
|
async save(managedGroup) {
|
||||||
|
await managedGroup.save();
|
||||||
|
if (this.can.can('read model', managedGroup)) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.managed-groups.managed-group',
|
||||||
|
managedGroup,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.auth-methods.auth-method.managed-groups',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a managed group in current scope and redirect to index
|
||||||
|
* @param {ManagedGroupModel} managed group
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(managedGroup) {
|
||||||
|
await managedGroup.destroyRecord();
|
||||||
|
await this.router.replaceWith(
|
||||||
|
'scopes.scope.auth-methods.auth-method.managed-groups',
|
||||||
|
);
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the contents of string array fields in order to force the instance
|
||||||
|
* into a dirty state. This ensures that `model.rollbackAttributes()` reverts
|
||||||
|
* to the original expected array.
|
||||||
|
*
|
||||||
|
* The deep copy implemented here is required to ensure that both the
|
||||||
|
* array itself and its members are all new.
|
||||||
|
*
|
||||||
|
* @param {managedGroupModel} managedGroup
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
edit(managedGroup) {
|
||||||
|
if (managedGroup.group_names) {
|
||||||
|
managedGroup.group_names = structuredClone(managedGroup.group_names);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/auth-methods/index') authMethods;
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/managed-groups/index')
|
||||||
|
managedGroups;
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupMembersController extends Controller {
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/managed-groups/index')
|
||||||
|
managedGroups;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsNewController extends Controller {
|
||||||
|
@controller('scopes/scope/auth-methods/index') authMethods;
|
||||||
|
@controller('scopes/scope/auth-methods/auth-method/managed-groups/index')
|
||||||
|
managedGroups;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
|
||||||
export default class ScopesScopeAuthMethodsIndexController extends Controller {
|
export default class ScopesScopeAuthMethodsIndexController extends Controller {
|
||||||
// =attributes
|
// =attributes
|
||||||
@@ -15,4 +16,35 @@ export default class ScopesScopeAuthMethodsIndexController extends Controller {
|
|||||||
get hasPrimaryAuthMethod() {
|
get hasPrimaryAuthMethod() {
|
||||||
return Boolean(this.scopeModel.primary_auth_method_id);
|
return Boolean(this.scopeModel.primary_auth_method_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from array `property` at `index` on the
|
||||||
|
* passed `authMethod`. This is used to manage entries in fragment array
|
||||||
|
* fields such as `signing_algorithms`.
|
||||||
|
* @param {AuthMethodModel} authMethod
|
||||||
|
* @param {string} property
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async removeItemByIndex(authMethod, property, index) {
|
||||||
|
const array = authMethod.get(property).filter((item, i) => i !== index);
|
||||||
|
authMethod.set(property, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a string item to array `property` on the passed `authMethod`.
|
||||||
|
* This is used to manage entries in fragment OIDC string array fields such
|
||||||
|
* as `signing_algorithms`.
|
||||||
|
* @param {AuthMethodModel} authMethod
|
||||||
|
* @param {string} property
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async addStringItem(authMethod, property, value) {
|
||||||
|
const existingArray = authMethod[property] ?? [];
|
||||||
|
const array = [...existingArray, { value }];
|
||||||
|
authMethod.set(property, array);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryIndexController extends Controller {
|
||||||
|
@controller(
|
||||||
|
'scopes/scope/credential-stores/credential-store/credential-libraries/index',
|
||||||
|
)
|
||||||
|
credentialLibraries;
|
||||||
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/credential-stores/index') credentialStores;
|
||||||
|
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on a credential library.
|
||||||
|
* @param {CredentialLibraryModel} credentialLibrary
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(credentialLibrary) {
|
||||||
|
const { isNew } = credentialLibrary;
|
||||||
|
credentialLibrary.rollbackAttributes();
|
||||||
|
if (isNew)
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle save of a credential library
|
||||||
|
* @param {CredentialLibraryModel} credentialLibrary
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess(({ isNew }) =>
|
||||||
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||||
|
)
|
||||||
|
async save(credentialLibrary) {
|
||||||
|
await credentialLibrary.save();
|
||||||
|
if (this.can.can('read model', credentialLibrary)) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credential-libraries.credential-library',
|
||||||
|
credentialLibrary,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle delete of a credential library
|
||||||
|
* @param {CredentialLibraryModel} credentialLibrary
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(credentialLibrary) {
|
||||||
|
await credentialLibrary.destroyRecord();
|
||||||
|
await this.router.replaceWith(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
||||||
|
);
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the contents of array fields in order to force the instance
|
||||||
|
* into a dirty state. This ensures that `model.rollbackAttributes()` reverts
|
||||||
|
* to the original expected array.
|
||||||
|
*
|
||||||
|
* The deep copy implemented here is required to ensure that both the
|
||||||
|
* array itself and its members are all new.
|
||||||
|
*
|
||||||
|
* @param {credentialLibraryModel} credentialLibrary
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
edit(credentialLibrary) {
|
||||||
|
const { critical_options, extensions } = credentialLibrary;
|
||||||
|
credentialLibrary.critical_options = structuredClone(critical_options);
|
||||||
|
credentialLibrary.extensions = structuredClone(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update type of credential library
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async changeType(type) {
|
||||||
|
await this.router.replaceWith({ queryParams: { type } });
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-2
@@ -3,10 +3,13 @@
|
|||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewController extends Controller {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewController extends Controller {
|
||||||
// =services
|
@controller(
|
||||||
|
'scopes/scope/credential-stores/credential-store/credential-libraries/index',
|
||||||
|
)
|
||||||
|
credentialLibraries;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
queryParams = ['type'];
|
queryParams = ['type'];
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsCredentialIndexController extends Controller {
|
||||||
|
@controller(
|
||||||
|
'scopes/scope/credential-stores/credential-store/credentials/index',
|
||||||
|
)
|
||||||
|
credentials;
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/credential-stores/index') credentialStores;
|
||||||
|
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on a credential.
|
||||||
|
* @param {CredentialModel} credential
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(credential) {
|
||||||
|
const { isNew } = credential;
|
||||||
|
credential.rollbackAttributes();
|
||||||
|
if (isNew)
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credentials',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle save of a credential
|
||||||
|
* @param {CredentialModel} credential
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess(({ isNew }) =>
|
||||||
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||||
|
)
|
||||||
|
async save(credential) {
|
||||||
|
await credential.save();
|
||||||
|
if (this.can.can('read credential', credential)) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credentials.credential',
|
||||||
|
credential,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credentials',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle delete of a credential
|
||||||
|
* @param {Credential} credential
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(credential) {
|
||||||
|
await credential.destroyRecord();
|
||||||
|
await this.router.replaceWith(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credentials',
|
||||||
|
);
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update type of credential
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async changeType(type) {
|
||||||
|
await this.router.replaceWith({ queryParams: { type } });
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-2
@@ -3,10 +3,13 @@
|
|||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewController extends Controller {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewController extends Controller {
|
||||||
// =services
|
@controller(
|
||||||
|
'scopes/scope/credential-stores/credential-store/credentials/index',
|
||||||
|
)
|
||||||
|
credentials;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresCredentialStoreIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/credential-stores/index') credentialStores;
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
|
export default class ScopesScopeCredentialStoresIndexController extends Controller {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle save
|
||||||
|
* @param {CredentialStoreModel} credentialStore
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess('notifications.save-success')
|
||||||
|
async save(credentialStore) {
|
||||||
|
await credentialStore.save();
|
||||||
|
if (this.can.can('read model', credentialStore)) {
|
||||||
|
await this.router.transitionTo(
|
||||||
|
'scopes.scope.credential-stores.credential-store',
|
||||||
|
credentialStore,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo('scopes.scope.credential-stores');
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on credential stores.
|
||||||
|
* @param {CredentialStoreModel} credentialStore
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(credentialStore) {
|
||||||
|
const { isNew } = credentialStore;
|
||||||
|
credentialStore.rollbackAttributes();
|
||||||
|
if (isNew) {
|
||||||
|
await this.router.transitionTo('scopes.scope.credential-stores');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the credential store and redirects to index
|
||||||
|
* @param {CredentialStoreModel} credentialStore
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(credentialStore) {
|
||||||
|
await credentialStore.destroyRecord();
|
||||||
|
await this.router.replaceWith('scopes.scope.credential-stores');
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update type of credential store
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async changeType(type) {
|
||||||
|
await this.router.replaceWith({ queryParams: { type } });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,10 @@
|
|||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresNewController extends Controller {
|
export default class ScopesScopeCredentialStoresNewController extends Controller {
|
||||||
// =services
|
@controller('scopes/scope/credential-stores/index') credentialStores;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
queryParams = ['type'];
|
queryParams = ['type'];
|
||||||
|
|||||||
@@ -4,10 +4,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
export default class ScopesScopeGroupsGroupAddMembersController extends Controller {
|
export default class ScopesScopeGroupsGroupAddMembersController extends Controller {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
|
@service router;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,4 +32,29 @@ export default class ScopesScopeGroupsGroupAddMembersController extends Controll
|
|||||||
const sorted = [global, ...orgsAndProjects].flat();
|
const sorted = [global, ...orgsAndProjects].flat();
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds members to the group and saves, replaces with the members index
|
||||||
|
* route, and notifies the user of success or error.
|
||||||
|
* @param {GroupModel} group
|
||||||
|
* @param {[string]} userIDs
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.add-success')
|
||||||
|
async addMembers(group, userIDs) {
|
||||||
|
await group.addMembers(userIDs);
|
||||||
|
await this.router.replaceWith('scopes.scope.groups.group.members');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to group members as if nothing ever happened.
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel() {
|
||||||
|
await this.router.replaceWith('scopes.scope.groups.group.members');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeGroupsGroupIndexController extends Controller {
|
||||||
|
@controller('scopes/scope/groups/index') groups;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeGroupsGroupMembersController extends Controller {
|
||||||
|
@controller('scopes/scope/groups/index') groups;
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { loading } from 'ember-loading';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
|
export default class ScopesScopeGroupsIndexController extends Controller {
|
||||||
|
// =services
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback changes on a group.
|
||||||
|
* @param {GroupModel} group
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
async cancel(group) {
|
||||||
|
const { isNew } = group;
|
||||||
|
group.rollbackAttributes();
|
||||||
|
if (isNew) await this.router.transitionTo('scopes.scope.groups');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a group in current scope.
|
||||||
|
* @param {GroupModel} group
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@notifyError(({ message }) => message)
|
||||||
|
@notifySuccess(({ isNew }) =>
|
||||||
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
||||||
|
)
|
||||||
|
async save(group) {
|
||||||
|
await group.save();
|
||||||
|
if (this.can.can('read model', group)) {
|
||||||
|
await this.router.transitionTo('scopes.scope.groups.group', group);
|
||||||
|
} else {
|
||||||
|
await this.router.transitionTo('scopes.scope.groups');
|
||||||
|
}
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a group in current scope and redirect to index
|
||||||
|
* @param {GroupModel} group
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(group) {
|
||||||
|
await group.destroyRecord();
|
||||||
|
await this.router.replaceWith('scopes.scope.groups');
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a member from the current role and redirect to members index.
|
||||||
|
* @param {GroupModel} group
|
||||||
|
* @param {UserModel} member
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.remove-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.remove-success')
|
||||||
|
async removeMember(group, member) {
|
||||||
|
await group.removeMember(member.id);
|
||||||
|
await this.router.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Controller, { inject as controller } from '@ember/controller';
|
||||||
|
|
||||||
|
export default class ScopesScopeGroupsNewController extends Controller {
|
||||||
|
@controller('scopes/scope/groups/index') groups;
|
||||||
|
}
|
||||||
@@ -95,6 +95,11 @@ Router.map(function () {
|
|||||||
this.route('new');
|
this.route('new');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.route('aliases', function () {
|
||||||
|
this.route('new');
|
||||||
|
this.route('alias', { path: ':alias_id' }, function () {});
|
||||||
|
});
|
||||||
|
|
||||||
this.route('host-catalogs', function () {
|
this.route('host-catalogs', function () {
|
||||||
this.route('new');
|
this.route('new');
|
||||||
this.route('host-catalog', { path: ':host_catalog_id' }, function () {
|
this.route('host-catalog', { path: ':host_catalog_id' }, function () {
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The change password flow is associated only with password-type auth methods.
|
* The change password flow is associated only with password-type auth methods.
|
||||||
@@ -30,33 +28,4 @@ export default class AccountChangePasswordRoute extends Route {
|
|||||||
const account_id = this.session?.data?.authenticated?.account_id;
|
const account_id = this.session?.data?.authenticated?.account_id;
|
||||||
return this.store.findRecord('account', account_id);
|
return this.store.findRecord('account', account_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the password on the account and then redirects to the index route.
|
|
||||||
* @param {AccountModel} account
|
|
||||||
* @param {string} currentPassword
|
|
||||||
* @param {string} newPassword
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.save-success')
|
|
||||||
async changePassword(account, currentPassword, newPassword) {
|
|
||||||
await account.changePassword(currentPassword, newPassword);
|
|
||||||
// Transition to index after success. We ignore errors in the transition,
|
|
||||||
// since the index route is responsible for additional
|
|
||||||
// redirects depending on the authentication context. These additional
|
|
||||||
// redirects may abort this initial transition, resulting in a
|
|
||||||
// TransitionAborted error, which we do not want to show to the user.
|
|
||||||
await this.router.replaceWith('index').catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On cancel, redirects to index route for further processing.
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel() {
|
|
||||||
this.router.replaceWith('index');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,6 @@ export default class ApplicationRoute extends Route {
|
|||||||
@service confirm;
|
@service confirm;
|
||||||
@service router;
|
@service router;
|
||||||
@service intl;
|
@service intl;
|
||||||
@service features;
|
|
||||||
@service featureEdition;
|
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
|
|
||||||
@@ -64,33 +62,6 @@ export default class ApplicationRoute extends Route {
|
|||||||
this.toggleTheme(theme);
|
this.toggleTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegates invalidation to the session service.
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
invalidateSession() {
|
|
||||||
this.session.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates the session if a 401 error occurs and returns false to
|
|
||||||
* prevent further error handling.
|
|
||||||
* Returns true in all other cases, allowing error handling to occur (such
|
|
||||||
* as displaying the `error.hbs` template, if one exists).
|
|
||||||
* @param {Error} e
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
error(e) {
|
|
||||||
const isUnauthenticated = A(e?.errors)?.[0]?.isUnauthenticated;
|
|
||||||
if (isUnauthenticated) {
|
|
||||||
this.session.invalidate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the specified color theme to the root ember element.
|
* Applies the specified color theme to the root ember element.
|
||||||
* @param {string} theme - "light", "dark", or nullish (system default)
|
* @param {string} theme - "light", "dark", or nullish (system default)
|
||||||
@@ -117,17 +88,20 @@ export default class ApplicationRoute extends Route {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates the session if a 401 error occurs and returns false to
|
||||||
|
* prevent further error handling.
|
||||||
|
* Returns true in all other cases, allowing error handling to occur (such
|
||||||
|
* as displaying the `error.hbs` template, if one exists).
|
||||||
|
* @param {Error} e
|
||||||
|
*/
|
||||||
@action
|
@action
|
||||||
toggleEdition(edition) {
|
error(e) {
|
||||||
this.featureEdition.setEdition(edition);
|
const isUnauthenticated = A(e?.errors)?.[0]?.isUnauthenticated;
|
||||||
}
|
if (isUnauthenticated) {
|
||||||
|
this.session.invalidate();
|
||||||
@action
|
return false;
|
||||||
toggleFeature(feature) {
|
|
||||||
if (this.features.isEnabled(feature)) {
|
|
||||||
this.features.disable(feature);
|
|
||||||
} else {
|
|
||||||
this.features.enable(feature);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesRoute extends Route {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service store;
|
||||||
|
@service session;
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If arriving here unauthenticated, redirect to index for further processing.
|
||||||
|
*/
|
||||||
|
beforeModel() {
|
||||||
|
if (!this.session.isAuthenticated) this.router.transitionTo('index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all aliases in current scope
|
||||||
|
* @return {Promise<[AliasModel]>}
|
||||||
|
*/
|
||||||
|
async model() {
|
||||||
|
const scope = this.modelFor('scopes.scope');
|
||||||
|
const { id: scope_id } = scope;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.can.can('list model', scope, {
|
||||||
|
collection: 'aliases',
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return this.store.query('alias', { scope_id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesAliasRoute extends Route {
|
||||||
|
@service store;
|
||||||
|
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an alias in current scope.
|
||||||
|
* @param {object} params
|
||||||
|
* @param {string} params.alias_id
|
||||||
|
* @return {Promise{AliasModel}}
|
||||||
|
*/
|
||||||
|
async model({ alias_id }) {
|
||||||
|
return this.store.findRecord('alias', alias_id, {
|
||||||
|
reload: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesIndexRoute extends Route {
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
setupController(controller) {
|
||||||
|
const scope = this.modelFor('scopes.scope');
|
||||||
|
super.setupController(...arguments);
|
||||||
|
controller.setProperties({ scope });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { TYPE_ALIAS_TARGET } from 'api/models/alias';
|
||||||
|
|
||||||
|
export default class ScopesScopeAliasesNewRoute extends Route {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service store;
|
||||||
|
@service can;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to parent route when scope does not have create authorized action.
|
||||||
|
*/
|
||||||
|
beforeModel() {
|
||||||
|
const scopeModel = this.modelFor('scopes.scope');
|
||||||
|
if (
|
||||||
|
this.can.cannot('create model', scopeModel, { collection: 'aliases' })
|
||||||
|
) {
|
||||||
|
this.router.replaceWith('scopes.scope.aliases');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unsaved alias.
|
||||||
|
* @return {AliasModel}
|
||||||
|
*/
|
||||||
|
model() {
|
||||||
|
const scopeModel = this.modelFor('scopes.scope');
|
||||||
|
const record = this.store.createRecord('alias', {
|
||||||
|
type: TYPE_ALIAS_TARGET,
|
||||||
|
});
|
||||||
|
record.scopeModel = scopeModel;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,19 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route {
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service intl;
|
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
|
|
||||||
@@ -42,67 +36,4 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route
|
|||||||
accounts,
|
accounts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rollback changes on an account.
|
|
||||||
* @param {AccountModel} account
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(account) {
|
|
||||||
const { isNew } = account;
|
|
||||||
account.rollbackAttributes();
|
|
||||||
if (isNew)
|
|
||||||
this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.accounts',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save an account in current scope.
|
|
||||||
* @param {AccountModel} account
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess(({ isNew }) =>
|
|
||||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
||||||
)
|
|
||||||
async save(account, password) {
|
|
||||||
const { isNew } = account;
|
|
||||||
const adapterOptions = {};
|
|
||||||
if (isNew) {
|
|
||||||
adapterOptions.password = password;
|
|
||||||
}
|
|
||||||
await account.save({ adapterOptions });
|
|
||||||
if (this.can.can('read model', account)) {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.accounts.account',
|
|
||||||
account,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.accounts',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an account in current scope and redirect to index
|
|
||||||
* @param {AccountModel} account
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async deleteAccount(account) {
|
|
||||||
await account.destroyRecord();
|
|
||||||
await this.router.replaceWith(
|
|
||||||
'scopes.scope.auth-methods.auth-method.accounts',
|
|
||||||
);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+1
-29
@@ -4,33 +4,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSettingsRoute extends Route {
|
export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSetPasswordRoute extends Route {}
|
||||||
// =services
|
|
||||||
|
|
||||||
@service intl;
|
|
||||||
|
|
||||||
@service router;
|
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set password for the specified account.
|
|
||||||
* @param {AccountModel} account
|
|
||||||
* @param {string} password
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.save-success')
|
|
||||||
async setPassword(account, password) {
|
|
||||||
await account.setPassword(password);
|
|
||||||
await this.router.replaceWith(
|
|
||||||
'scopes.scope.auth-methods.auth-method.accounts.account.set-password',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,19 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends Route {
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service intl;
|
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
|
||||||
|
|
||||||
//=methods
|
//=methods
|
||||||
|
|
||||||
@@ -43,62 +37,4 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends
|
|||||||
managedGroups,
|
managedGroups,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
/**
|
|
||||||
* Rollback changes on a managed group.
|
|
||||||
* @param {ManagedGroupModel} managedGroup
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(managedGroup) {
|
|
||||||
const { isNew } = managedGroup;
|
|
||||||
managedGroup.rollbackAttributes();
|
|
||||||
if (isNew) {
|
|
||||||
this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.managed-groups',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save a managed group in current scope.
|
|
||||||
* @param {ManagedGroupModel} managedGroup
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess(({ isNew }) =>
|
|
||||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
||||||
)
|
|
||||||
async save(managedGroup) {
|
|
||||||
await managedGroup.save();
|
|
||||||
if (this.can.can('read model', managedGroup)) {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.managed-groups.managed-group',
|
|
||||||
managedGroup,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.auth-methods.auth-method.managed-groups',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a managed group in current scope and redirect to index
|
|
||||||
* @param {ManagedGroupModel} managed group
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async deleteManagedGroup(managedGroup) {
|
|
||||||
await managedGroup.destroyRecord();
|
|
||||||
await this.router.replaceWith(
|
|
||||||
'scopes.scope.auth-methods.auth-method.managed-groups',
|
|
||||||
);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-19
@@ -4,14 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupRoute extends Route {
|
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service can;
|
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
@@ -44,21 +42,4 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupRo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the contents of string array fields in order to force the instance
|
|
||||||
* into a dirty state. This ensures that `model.rollbackAttributes()` reverts
|
|
||||||
* to the original expected array.
|
|
||||||
*
|
|
||||||
* The deep copy implemented here is required to ensure that both the
|
|
||||||
* array itself and its members are all new.
|
|
||||||
*
|
|
||||||
* @param {managedGroupModel} managedGroup
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
edit(managedGroup) {
|
|
||||||
if (managedGroup.group_names) {
|
|
||||||
managedGroup.group_names = structuredClone(managedGroup.group_names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,17 +5,12 @@
|
|||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresRoute extends Route {
|
export default class ScopesScopeCredentialStoresRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
|
|
||||||
@@ -34,49 +29,4 @@ export default class ScopesScopeCredentialStoresRoute extends Route {
|
|||||||
return this.store.query('credential-store', { scope_id });
|
return this.store.query('credential-store', { scope_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle save
|
|
||||||
* @param {CredentialStoreModel} credentialStore
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess('notifications.save-success')
|
|
||||||
async save(credentialStore) {
|
|
||||||
await credentialStore.save();
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store',
|
|
||||||
credentialStore,
|
|
||||||
);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rollback changes on credential stores.
|
|
||||||
* @param {CredentialStoreModel} credentialStore
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(credentialStore) {
|
|
||||||
const { isNew } = credentialStore;
|
|
||||||
credentialStore.rollbackAttributes();
|
|
||||||
if (isNew) this.router.transitionTo('scopes.scope.credential-stores');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the credential store and redirects to index
|
|
||||||
* @param {CredentialStoreModel} credentialStore
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async delete(credentialStore) {
|
|
||||||
await credentialStore.destroyRecord();
|
|
||||||
await this.router.replaceWith('scopes.scope.credential-stores');
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-63
@@ -4,10 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
|
||||||
@@ -15,7 +11,6 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
|
|
||||||
@@ -36,62 +31,4 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
return this.store.query('credential-library', { credential_store_id });
|
return this.store.query('credential-library', { credential_store_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rollback changes on a credential library.
|
|
||||||
* @param {CredentialLibraryModel} credentialLibrary
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(credentialLibrary) {
|
|
||||||
const { isNew } = credentialLibrary;
|
|
||||||
credentialLibrary.rollbackAttributes();
|
|
||||||
if (isNew)
|
|
||||||
this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle save of a credential library
|
|
||||||
* @param {CredentialLibraryModel} credentialLibrary
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess(({ isNew }) =>
|
|
||||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
||||||
)
|
|
||||||
async save(credentialLibrary) {
|
|
||||||
await credentialLibrary.save();
|
|
||||||
if (this.can.can('read model', credentialLibrary)) {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credential-libraries.credential-library',
|
|
||||||
credentialLibrary,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle delete of a credential library
|
|
||||||
* @param {CredentialLibraryModel} credentialLibrary
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async deleteCredentialLibrary(credentialLibrary) {
|
|
||||||
await credentialLibrary.destroyRecord();
|
|
||||||
await this.router.replaceWith(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credential-libraries',
|
|
||||||
);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-19
@@ -5,13 +5,11 @@
|
|||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryRoute extends Route {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service can;
|
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
@@ -45,21 +43,4 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the contents of array fields in order to force the instance
|
|
||||||
* into a dirty state. This ensures that `model.rollbackAttributes()` reverts
|
|
||||||
* to the original expected array.
|
|
||||||
*
|
|
||||||
* The deep copy implemented here is required to ensure that both the
|
|
||||||
* array itself and its members are all new.
|
|
||||||
*
|
|
||||||
* @param {credentialLibraryModel} credentialLibrary
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
edit(credentialLibrary) {
|
|
||||||
const { critical_options, extensions } = credentialLibrary;
|
|
||||||
credentialLibrary.critical_options = structuredClone(critical_options);
|
|
||||||
credentialLibrary.extensions = structuredClone(extensions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-10
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC } from 'api/models/credential-library';
|
import { TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC } from 'api/models/credential-library';
|
||||||
|
|
||||||
@@ -64,13 +63,4 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
credential_store_id,
|
credential_store_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update type of credential library
|
|
||||||
* @param {string} type
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
changeType(type) {
|
|
||||||
this.router.replaceWith({ queryParams: { type } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsRoute extends Route {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsRoute extends Route {
|
||||||
@@ -15,7 +11,6 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialsRoute
|
|||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
|
||||||
|
|
||||||
// =methods
|
// =methods
|
||||||
|
|
||||||
@@ -36,61 +31,4 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialsRoute
|
|||||||
return this.store.query('credential', { credential_store_id });
|
return this.store.query('credential', { credential_store_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rollback changes on a credential.
|
|
||||||
* @param {CredentialModel} credential
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(credential) {
|
|
||||||
const { isNew } = credential;
|
|
||||||
credential.rollbackAttributes();
|
|
||||||
if (isNew)
|
|
||||||
this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credentials',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle save of a credential
|
|
||||||
* @param {CredentialModel} credential
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess(({ isNew }) =>
|
|
||||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
||||||
)
|
|
||||||
async save(credential) {
|
|
||||||
await credential.save();
|
|
||||||
if (this.can.can('read model', credential)) {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credentials.credential',
|
|
||||||
credential,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await this.router.transitionTo(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credentials',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Handle delete of a credential
|
|
||||||
* @param {Credential} credential
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async deleteCredential(credential) {
|
|
||||||
await credential.destroyRecord();
|
|
||||||
await this.router.replaceWith(
|
|
||||||
'scopes.scope.credential-stores.credential-store.credentials',
|
|
||||||
);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-10
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewRoute extends Route {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewRoute extends Route {
|
||||||
@@ -77,13 +76,4 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewRou
|
|||||||
...typeSpecificAttrs,
|
...typeSpecificAttrs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update type of credential
|
|
||||||
* @param {string} type
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
changeType(type) {
|
|
||||||
this.router.replaceWith({ queryParams: { type } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresNewRoute extends Route {
|
export default class ScopesScopeCredentialStoresNewRoute extends Route {
|
||||||
@@ -63,13 +62,4 @@ export default class ScopesScopeCredentialStoresNewRoute extends Route {
|
|||||||
record.scopeModel = scopeModel;
|
record.scopeModel = scopeModel;
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update type of credential store
|
|
||||||
* @param {string} type
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
changeType(type) {
|
|
||||||
this.router.replaceWith({ queryParams: { type } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,16 +5,11 @@
|
|||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeGroupsRoute extends Route {
|
export default class ScopesScopeGroupsRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service store;
|
||||||
@service intl;
|
|
||||||
@service session;
|
@service session;
|
||||||
@service can;
|
@service can;
|
||||||
@service router;
|
@service router;
|
||||||
@@ -39,51 +34,4 @@ export default class ScopesScopeGroupsRoute extends Route {
|
|||||||
return this.store.query('group', { scope_id });
|
return this.store.query('group', { scope_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
/**
|
|
||||||
* Rollback changes on a group.
|
|
||||||
* @param {GroupModel} group
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel(group) {
|
|
||||||
const { isNew } = group;
|
|
||||||
group.rollbackAttributes();
|
|
||||||
if (isNew) this.router.transitionTo('scopes.scope.groups');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save a group in current scope.
|
|
||||||
* @param {GroupModel} group
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message)
|
|
||||||
@notifySuccess(({ isNew }) =>
|
|
||||||
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
||||||
)
|
|
||||||
async save(group) {
|
|
||||||
await group.save();
|
|
||||||
if (this.can.can('read model', group)) {
|
|
||||||
await this.router.transitionTo('scopes.scope.groups.group', group);
|
|
||||||
} else {
|
|
||||||
await this.router.transitionTo('scopes.scope.groups');
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a group in current scope and redirect to index
|
|
||||||
* @param {GroupModel} group
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.delete-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.delete-success')
|
|
||||||
async delete(group) {
|
|
||||||
await group.destroyRecord();
|
|
||||||
await this.router.replaceWith('scopes.scope.groups');
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,6 @@ import Route from '@ember/routing/route';
|
|||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
import { resourceFilter } from 'core/decorators/resource-filter';
|
import { resourceFilter } from 'core/decorators/resource-filter';
|
||||||
|
|
||||||
export default class ScopesScopeGroupsGroupAddMembersRoute extends Route {
|
export default class ScopesScopeGroupsGroupAddMembersRoute extends Route {
|
||||||
@@ -67,29 +65,6 @@ export default class ScopesScopeGroupsGroupAddMembersRoute extends Route {
|
|||||||
|
|
||||||
// =actions
|
// =actions
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds members to the group and saves, replaces with the members index
|
|
||||||
* route, and notifies the user of success or error.
|
|
||||||
* @param {GroupModel} group
|
|
||||||
* @param {[string]} userIDs
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.add-success')
|
|
||||||
async addMembers(group, userIDs) {
|
|
||||||
await group.addMembers(userIDs);
|
|
||||||
await this.router.replaceWith('scopes.scope.groups.group.members');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect to group members as if nothing ever happened.
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
cancel() {
|
|
||||||
this.router.replaceWith('scopes.scope.groups.group.members');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the specified resource filter field to the specified value.
|
* Sets the specified resource filter field to the specified value.
|
||||||
* @param {string} field
|
* @param {string} field
|
||||||
|
|||||||
@@ -4,12 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
import { loading } from 'ember-loading';
|
|
||||||
import { confirm } from 'core/decorators/confirm';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
||||||
|
|
||||||
export default class ScopesScopeGroupsGroupMembersRoute extends Route {
|
export default class ScopesScopeGroupsGroupMembersRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
@@ -37,21 +33,4 @@ export default class ScopesScopeGroupsGroupMembersRoute extends Route {
|
|||||||
: [],
|
: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a member from the current role and redirect to members index.
|
|
||||||
* @param {GroupModel} group
|
|
||||||
* @param {UserModel} member
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
@loading
|
|
||||||
@confirm('questions.remove-confirm')
|
|
||||||
@notifyError(({ message }) => message, { catch: true })
|
|
||||||
@notifySuccess('notifications.remove-success')
|
|
||||||
async removeMember(group, member) {
|
|
||||||
await group.removeMember(member.id);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::Account::Password::ChangePassword
|
<Form::Account::Password::ChangePassword
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'changePassword' @model}}
|
@submit={{fn this.changePassword @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</dropdown.link>
|
</dropdown.link>
|
||||||
<dropdown.separator />
|
<dropdown.separator />
|
||||||
|
|
||||||
<dropdown.button {{on 'click' (route-action 'invalidateSession')}}>
|
<dropdown.button {{on 'click' this.invalidateSession}}>
|
||||||
{{t 'actions.deauthenticate'}}
|
{{t 'actions.deauthenticate'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
this.session.data.theme
|
this.session.data.theme
|
||||||
'system-default-theme'
|
'system-default-theme'
|
||||||
}}
|
}}
|
||||||
@changed={{route-action 'toggleTheme'}}
|
@changed={{this.toggleTheme}}
|
||||||
as |radioGroup|
|
as |radioGroup|
|
||||||
>
|
>
|
||||||
<dropdown.item>
|
<dropdown.item>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
<form.radioGroup
|
<form.radioGroup
|
||||||
@name='edition'
|
@name='edition'
|
||||||
@selectedValue={{this.featureEdition.edition}}
|
@selectedValue={{this.featureEdition.edition}}
|
||||||
@changed={{route-action 'toggleEdition'}}
|
@changed={{this.toggleEdition}}
|
||||||
as |radioGroup|
|
as |radioGroup|
|
||||||
>
|
>
|
||||||
{{#each this.featureEdition.editions as |edition|}}
|
{{#each this.featureEdition.editions as |edition|}}
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
<form.checkbox
|
<form.checkbox
|
||||||
@label={{feature}}
|
@label={{feature}}
|
||||||
@checked={{feature-flag feature}}
|
@checked={{feature-flag feature}}
|
||||||
@onChange={{route-action 'toggleFeature' feature}}
|
@onChange={{fn this.toggleFeature feature}}
|
||||||
/>
|
/>
|
||||||
</dropdown.item>
|
</dropdown.item>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -69,6 +69,12 @@
|
|||||||
{{t 'resources.policy.title_plural'}}
|
{{t 'resources.policy.title_plural'}}
|
||||||
</nav.link>
|
</nav.link>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if (can 'navigate scope' @model collection='aliases')}}
|
||||||
|
<nav.link @route='scopes.scope.aliases'>
|
||||||
|
<Rose::Icon @name='flight-icons/svg/at-sign-16' @size='medium' />
|
||||||
|
{{t 'resources.alias.title_plural'}}
|
||||||
|
</nav.link>
|
||||||
|
{{/if}}
|
||||||
{{#if
|
{{#if
|
||||||
(and
|
(and
|
||||||
(can 'navigate worker' @model collection='workers')
|
(can 'navigate worker' @model collection='workers')
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{page-title (t 'resources.alias.title_plural')}}
|
||||||
|
<Breadcrumbs::Item
|
||||||
|
@text={{t 'resources.alias.title_plural'}}
|
||||||
|
@route='scopes.scope.aliases'
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{page-title @model.displayName}}
|
||||||
|
<Breadcrumbs::Item
|
||||||
|
@text={{@model.displayName}}
|
||||||
|
@route='scopes.scope.aliases.alias'
|
||||||
|
@model={{@model}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
}}
|
||||||
|
|
||||||
|
<Rose::Layout::Page as |page|>
|
||||||
|
<page.breadcrumbs>
|
||||||
|
<Breadcrumbs::Container />
|
||||||
|
</page.breadcrumbs>
|
||||||
|
|
||||||
|
</Rose::Layout::Page>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
}}
|
||||||
|
<Rose::Layout::Page as |page|>
|
||||||
|
<page.breadcrumbs>
|
||||||
|
<Breadcrumbs::Container />
|
||||||
|
</page.breadcrumbs>
|
||||||
|
|
||||||
|
<page.header>
|
||||||
|
<h2>
|
||||||
|
{{t 'resources.alias.title'}}
|
||||||
|
{{! TODO: Add doc link for alias }}
|
||||||
|
</h2>
|
||||||
|
{{! TODO: Add desc for alias }}
|
||||||
|
</page.header>
|
||||||
|
<page.actions>
|
||||||
|
{{#if (can 'create model' this.scope collection='aliases')}}
|
||||||
|
{{#if @model}}
|
||||||
|
<Hds::Button
|
||||||
|
@route='scopes.scope.aliases.new'
|
||||||
|
@text={{t 'resources.alias.titles.new'}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</page.actions>
|
||||||
|
</Rose::Layout::Page>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{page-title (t 'titles.new')}}
|
||||||
|
<Breadcrumbs::Item
|
||||||
|
@text={{t 'resources.alias.titles.new'}}
|
||||||
|
@route='scopes.scope.aliases.new'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Rose::Layout::Page as |page|>
|
||||||
|
<page.breadcrumbs>
|
||||||
|
<Breadcrumbs::Container />
|
||||||
|
</page.breadcrumbs>
|
||||||
|
|
||||||
|
<page.header>
|
||||||
|
<h2>
|
||||||
|
{{t 'resources.alias.titles.new'}}
|
||||||
|
{{! TODO: Add doc link for alias }}
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
{{! TODO: Add desc for alias }}
|
||||||
|
|
||||||
|
</page.header>
|
||||||
|
|
||||||
|
</Rose::Layout::Page>
|
||||||
+6
-3
@@ -18,14 +18,17 @@
|
|||||||
</page.navigation>
|
</page.navigation>
|
||||||
|
|
||||||
<page.actions>
|
<page.actions>
|
||||||
<AuthMethods::AuthMethod::Accounts::Account::Actions @model={{@model}} />
|
<AuthMethods::AuthMethod::Accounts::Account::Actions
|
||||||
|
@model={{@model}}
|
||||||
|
@delete={{this.accounts.delete}}
|
||||||
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
<Form::Account
|
<Form::Account
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.accounts.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.accounts.cancel @model}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|||||||
+6
-3
@@ -26,14 +26,17 @@
|
|||||||
</page.navigation>
|
</page.navigation>
|
||||||
|
|
||||||
<page.actions>
|
<page.actions>
|
||||||
<AuthMethods::AuthMethod::Accounts::Account::Actions @model={{@model}} />
|
<AuthMethods::AuthMethod::Accounts::Account::Actions
|
||||||
|
@model={{@model}}
|
||||||
|
@delete={{this.accounts.delete}}
|
||||||
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
<Form::Account::Password::SetPassword
|
<Form::Account::Password::SetPassword
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'setPassword' @model}}
|
@submit={{fn this.setPassword @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.accounts.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::Account
|
<Form::Account
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.accounts.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.accounts.cancel @model}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|||||||
+6
-5
@@ -24,17 +24,18 @@
|
|||||||
<page.actions>
|
<page.actions>
|
||||||
<AuthMethods::AuthMethod::ManagedGroups::ManagedGroup::Actions
|
<AuthMethods::AuthMethod::ManagedGroups::ManagedGroup::Actions
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
|
@delete={{this.managedGroups.delete}}
|
||||||
/>
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
<Form::ManagedGroup
|
<Form::ManagedGroup
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@edit={{route-action 'edit' @model}}
|
@edit={{fn this.managedGroups.edit @model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.managedGroups.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.managedGroups.cancel @model}}
|
||||||
@removeItemByIndex={{route-action 'removeItemByIndex' @model}}
|
@removeItemByIndex={{fn this.authMethods.removeItemByIndex @model}}
|
||||||
@addStringItem={{route-action 'addStringItem' @model}}
|
@addStringItem={{fn this.authMethods.addStringItem @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
+1
@@ -30,6 +30,7 @@
|
|||||||
<page.actions>
|
<page.actions>
|
||||||
<AuthMethods::AuthMethod::ManagedGroups::ManagedGroup::Actions
|
<AuthMethods::AuthMethod::ManagedGroups::ManagedGroup::Actions
|
||||||
@model={{@model.managedGroup}}
|
@model={{@model.managedGroup}}
|
||||||
|
@delete={{this.managedGroups.delete}}
|
||||||
/>
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::ManagedGroup
|
<Form::ManagedGroup
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.managedGroups.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.managedGroups.cancel @model}}
|
||||||
@removeItemByIndex={{route-action 'removeItemByIndex' @model}}
|
@removeItemByIndex={{fn this.authMethods.removeItemByIndex @model}}
|
||||||
@addStringItem={{route-action 'addStringItem' @model}}
|
@addStringItem={{fn this.authMethods.addStringItem @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -38,7 +38,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'deleteCredentialLibrary' @model)}}
|
{{on 'click' (fn this.credentialLibraries.delete @model)}}
|
||||||
>
|
>
|
||||||
{{t 'resources.credential-library.actions.delete'}}
|
{{t 'resources.credential-library.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::CredentialLibrary
|
<Form::CredentialLibrary
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@edit={{route-action 'edit' @model}}
|
@edit={{fn this.credentialLibraries.edit @model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentialLibraries.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentialLibraries.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
</Rose::Layout::Page>
|
</Rose::Layout::Page>
|
||||||
+1
@@ -22,6 +22,7 @@
|
|||||||
<page.actions>
|
<page.actions>
|
||||||
<CredentialStores::CredentialStore::Actions
|
<CredentialStores::CredentialStore::Actions
|
||||||
@model={{this.credentialStore}}
|
@model={{this.credentialStore}}
|
||||||
|
@delete={{fn this.credentialStores.delete this.credentialStore}}
|
||||||
/>
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -26,9 +26,9 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::CredentialLibrary
|
<Form::CredentialLibrary
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentialLibraries.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentialLibraries.cancel @model}}
|
||||||
@changeType={{route-action 'changeType'}}
|
@changeType={{this.credentialLibraries.changeType}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
</Rose::Layout::Page>
|
</Rose::Layout::Page>
|
||||||
+3
-3
@@ -38,7 +38,7 @@
|
|||||||
<dropdown.button
|
<dropdown.button
|
||||||
@style='danger'
|
@style='danger'
|
||||||
@disabled={{@model.canSave}}
|
@disabled={{@model.canSave}}
|
||||||
{{on 'click' (route-action 'deleteCredential' @model)}}
|
{{on 'click' (fn this.credentials.delete @model)}}
|
||||||
>
|
>
|
||||||
{{t 'resources.credential.actions.delete'}}
|
{{t 'resources.credential.actions.delete'}}
|
||||||
</dropdown.button>
|
</dropdown.button>
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::Credential
|
<Form::Credential
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentials.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentials.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
</Rose::Layout::Page>
|
</Rose::Layout::Page>
|
||||||
+1
@@ -22,6 +22,7 @@
|
|||||||
<page.actions>
|
<page.actions>
|
||||||
<CredentialStores::CredentialStore::Actions
|
<CredentialStores::CredentialStore::Actions
|
||||||
@model={{this.credentialStore}}
|
@model={{this.credentialStore}}
|
||||||
|
@delete={{fn this.credentialStores.delete this.credentialStore}}
|
||||||
/>
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -26,9 +26,9 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::Credential
|
<Form::Credential
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentials.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentials.cancel @model}}
|
||||||
@changeType={{route-action 'changeType'}}
|
@changeType={{this.credentials.changeType}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
</Rose::Layout::Page>
|
</Rose::Layout::Page>
|
||||||
@@ -18,14 +18,17 @@
|
|||||||
</page.navigation>
|
</page.navigation>
|
||||||
|
|
||||||
<page.actions>
|
<page.actions>
|
||||||
<CredentialStores::CredentialStore::Actions @model={{@model}} />
|
<CredentialStores::CredentialStore::Actions
|
||||||
|
@model={{@model}}
|
||||||
|
@delete={{fn this.credentialStores.delete @model}}
|
||||||
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
<Form::CredentialStore
|
<Form::CredentialStore
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentialStores.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentialStores.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
</Rose::Layout::Page>
|
</Rose::Layout::Page>
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::CredentialStore
|
<Form::CredentialStore
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.credentialStores.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.credentialStores.cancel @model}}
|
||||||
@changeType={{route-action 'changeType'}}
|
@changeType={{this.credentialStores.changeType}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@
|
|||||||
<Form::Group::AddMembers
|
<Form::Group::AddMembers
|
||||||
@model={{@model.group}}
|
@model={{@model.group}}
|
||||||
@users={{@model.users}}
|
@users={{@model.users}}
|
||||||
@submit={{route-action 'addMembers' @model.group}}
|
@submit={{fn this.addMembers @model.group}}
|
||||||
@cancel={{route-action 'cancel'}}
|
@cancel={{this.cancel}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,17 @@
|
|||||||
</page.navigation>
|
</page.navigation>
|
||||||
|
|
||||||
<page.actions>
|
<page.actions>
|
||||||
<Groups::Group::Actions @model={{@model}} />
|
<Groups::Group::Actions
|
||||||
|
@model={{@model}}
|
||||||
|
@delete={{fn this.groups.delete @model}}
|
||||||
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
<Form::Group
|
<Form::Group
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.groups.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.groups.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,10 @@
|
|||||||
</page.navigation>
|
</page.navigation>
|
||||||
|
|
||||||
<page.actions>
|
<page.actions>
|
||||||
<Groups::Group::Actions @model={{@model.group}} />
|
<Groups::Group::Actions
|
||||||
|
@model={{@model.group}}
|
||||||
|
@delete={{fn this.groups.delete @model.group}}
|
||||||
|
/>
|
||||||
</page.actions>
|
</page.actions>
|
||||||
|
|
||||||
<page.body>
|
<page.body>
|
||||||
@@ -75,7 +78,7 @@
|
|||||||
@text={{t 'actions.remove'}}
|
@text={{t 'actions.remove'}}
|
||||||
{{on
|
{{on
|
||||||
'click'
|
'click'
|
||||||
(route-action 'removeMember' @model.group B.data)
|
(fn this.groups.removeMember @model.group B.data)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Hds::Dropdown>
|
</Hds::Dropdown>
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
<page.body>
|
<page.body>
|
||||||
<Form::Group
|
<Form::Group
|
||||||
@model={{@model}}
|
@model={{@model}}
|
||||||
@submit={{route-action 'save' @model}}
|
@submit={{fn this.groups.save @model}}
|
||||||
@cancel={{route-action 'cancel' @model}}
|
@cancel={{fn this.groups.cancel @model}}
|
||||||
/>
|
/>
|
||||||
</page.body>
|
</page.body>
|
||||||
|
|
||||||
|
|||||||
@@ -134,18 +134,18 @@ module('Acceptance | accounts | create', function (hooks) {
|
|||||||
const accountsCount = this.server.db.accounts.length;
|
const accountsCount = this.server.db.accounts.length;
|
||||||
await visit(urls.newAccount);
|
await visit(urls.newAccount);
|
||||||
await fillIn('[name="name"]', 'Account name');
|
await fillIn('[name="name"]', 'Account name');
|
||||||
await click('form button:not([type="submit"])');
|
await click('.rose-form-actions [type="button"]');
|
||||||
assert.strictEqual(this.server.db.accounts.length, accountsCount);
|
|
||||||
assert.strictEqual(currentURL(), urls.accounts);
|
assert.strictEqual(currentURL(), urls.accounts);
|
||||||
|
assert.strictEqual(this.server.db.accounts.length, accountsCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can cancel a new LDAP account creation', async function (assert) {
|
test('can cancel a new LDAP account creation', async function (assert) {
|
||||||
const accountsCount = this.server.db.accounts.length;
|
const accountsCount = this.server.db.accounts.length;
|
||||||
await visit(urls.newAccount);
|
await visit(urls.newAccount);
|
||||||
await fillIn('[name="name"]', 'Account name');
|
await fillIn('[name="name"]', 'Account name');
|
||||||
await click('form button:not([type="submit"])');
|
await click('.rose-form-actions [type="button"]');
|
||||||
assert.strictEqual(this.server.db.accounts.length, accountsCount);
|
|
||||||
assert.strictEqual(currentURL(), urls.accounts);
|
assert.strictEqual(currentURL(), urls.accounts);
|
||||||
|
assert.strictEqual(this.server.db.accounts.length, accountsCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('saving a new account with invalid fields displays error messages', async function (assert) {
|
test('saving a new account with invalid fields displays error messages', async function (assert) {
|
||||||
@@ -178,7 +178,7 @@ module('Acceptance | accounts | create', function (hooks) {
|
|||||||
'Displays primary error message.',
|
'Displays primary error message.',
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
find('.rose-form-error-message').textContent.trim(),
|
find('[data-test-error-message-name]').textContent.trim(),
|
||||||
'Name is required.',
|
'Name is required.',
|
||||||
'Displays field-level errors.',
|
'Displays field-level errors.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ module('Acceptance | accounts | update', function (hooks) {
|
|||||||
'Displays primary error message.',
|
'Displays primary error message.',
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
find('.rose-form-error-message').textContent.trim(),
|
find('[data-test-error-message-name]').textContent.trim(),
|
||||||
'Name is required.',
|
'Name is required.',
|
||||||
'Displays field-level errors.',
|
'Displays field-level errors.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ module(
|
|||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
|
let featuresService;
|
||||||
|
|
||||||
const instances = {
|
const instances = {
|
||||||
scopes: {
|
scopes: {
|
||||||
global: null,
|
global: null,
|
||||||
@@ -83,6 +85,8 @@ module(
|
|||||||
urls.usernamePasswordCredential = `${urls.credentials}/${instances.usernamePasswordCredential.id}`;
|
urls.usernamePasswordCredential = `${urls.credentials}/${instances.usernamePasswordCredential.id}`;
|
||||||
urls.usernameKeyPairCredential = `${urls.credentials}/${instances.usernameKeyPairCredential.id}`;
|
urls.usernameKeyPairCredential = `${urls.credentials}/${instances.usernameKeyPairCredential.id}`;
|
||||||
urls.jsonCredential = `${urls.credentials}/${instances.jsonCredential.id}`;
|
urls.jsonCredential = `${urls.credentials}/${instances.jsonCredential.id}`;
|
||||||
|
|
||||||
|
featuresService = this.owner.lookup('service:features');
|
||||||
authenticateSession({});
|
authenticateSession({});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,6 +121,7 @@ module(
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('can save changes to existing JSON credential', async function (assert) {
|
test('can save changes to existing JSON credential', async function (assert) {
|
||||||
|
featuresService.enable('json-credentials');
|
||||||
const mockInput = 'random string';
|
const mockInput = 'random string';
|
||||||
assert.notEqual(instances.jsonCredential.name, mockInput);
|
assert.notEqual(instances.jsonCredential.name, mockInput);
|
||||||
await visit(urls.jsonCredential);
|
await visit(urls.jsonCredential);
|
||||||
|
|||||||
@@ -20,18 +20,25 @@ exports.checkBoundaryCli = async () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the boundary CLI to authenticate to the specified Boundary instance
|
* Uses the boundary CLI to authenticate to the specified Boundary instance
|
||||||
|
* @param {string} addr Address of the Boundary instance to be authenticated to
|
||||||
|
* @param {string} authMethodId ID of the auth method to be used for authentication
|
||||||
|
* @param {string} loginName Login name to be used for authentication
|
||||||
|
* @param {string} password Password to be used for authentication
|
||||||
*/
|
*/
|
||||||
exports.authenticateBoundaryCli = async () => {
|
exports.authenticateBoundaryCli = async (
|
||||||
|
addr,
|
||||||
|
authMethodId,
|
||||||
|
loginName,
|
||||||
|
password,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
execSync(
|
execSync(
|
||||||
'boundary authenticate password' +
|
'boundary authenticate password' +
|
||||||
' -addr=' +
|
` -addr=${addr}` +
|
||||||
process.env.BOUNDARY_ADDR +
|
` -auth-method-id=${authMethodId}` +
|
||||||
' -auth-method-id=' +
|
` -login-name=${loginName}` +
|
||||||
process.env.E2E_PASSWORD_AUTH_METHOD_ID +
|
' -password=env://BPASS',
|
||||||
' -login-name=' +
|
{ env: { ...process.env, BPASS: password } },
|
||||||
process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME +
|
|
||||||
' -password=env://E2E_PASSWORD_ADMIN_PASSWORD',
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`${e.stderr}`);
|
console.log(`${e.stderr}`);
|
||||||
@@ -41,25 +48,24 @@ exports.authenticateBoundaryCli = async () => {
|
|||||||
/**
|
/**
|
||||||
* Uses the boundary CLI to connect to the specified target
|
* Uses the boundary CLI to connect to the specified target
|
||||||
* @param {string} targetId ID of the target to be connected to
|
* @param {string} targetId ID of the target to be connected to
|
||||||
|
* @param {string} sshUser User to be used for the ssh connection
|
||||||
|
* @param {string} sshKeyPath Path to the ssh key to be used for the ssh connection
|
||||||
* @returns ChildProcess representing the result of the command execution
|
* @returns ChildProcess representing the result of the command execution
|
||||||
*/
|
*/
|
||||||
exports.connectToTarget = async (targetId) => {
|
exports.connectToTarget = async (targetId, sshUser, sshKeyPath) => {
|
||||||
let connect;
|
let connect;
|
||||||
try {
|
try {
|
||||||
connect = exec(
|
connect = exec(
|
||||||
'boundary connect' +
|
'boundary connect' +
|
||||||
' -target-id=' +
|
` -target-id=${targetId}` +
|
||||||
targetId +
|
' -exec /usr/bin/ssh --' +
|
||||||
' -exec /usr/bin/ssh --' +
|
` -l ${sshUser}` +
|
||||||
' -l ' +
|
` -i ${sshKeyPath}` +
|
||||||
process.env.E2E_SSH_USER +
|
' -o UserKnownHostsFile=/dev/null' +
|
||||||
' -i ' +
|
' -o StrictHostKeyChecking=no' +
|
||||||
process.env.E2E_SSH_KEY_PATH +
|
' -o IdentitiesOnly=yes' + // forces the use of the provided key
|
||||||
' -o UserKnownHostsFile=/dev/null' +
|
' -p {{boundary.port}}' +
|
||||||
' -o StrictHostKeyChecking=no' +
|
' {{boundary.ip}}',
|
||||||
' -o IdentitiesOnly=yes' + // forces the use of the provided key
|
|
||||||
' -p {{boundary.port}}' +
|
|
||||||
' {{boundary.ip}}',
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`${e.stderr}`);
|
console.log(`${e.stderr}`);
|
||||||
@@ -77,12 +83,12 @@ exports.connectSshToTarget = async (targetId) => {
|
|||||||
try {
|
try {
|
||||||
connect = exec(
|
connect = exec(
|
||||||
'boundary connect ssh' +
|
'boundary connect ssh' +
|
||||||
' -target-id=' +
|
' -target-id=' +
|
||||||
targetId +
|
targetId +
|
||||||
' --' +
|
' --' +
|
||||||
' -o UserKnownHostsFile=/dev/null' +
|
' -o UserKnownHostsFile=/dev/null' +
|
||||||
' -o StrictHostKeyChecking=no' +
|
' -o StrictHostKeyChecking=no' +
|
||||||
' -o IdentitiesOnly=yes', // forces the use of the provided key
|
' -o IdentitiesOnly=yes', // forces the use of the provided key
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`${e.stderr}`);
|
console.log(`${e.stderr}`);
|
||||||
@@ -124,6 +130,30 @@ exports.deleteOrgCli = async (orgId) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the boundary CLI to delete the specified storage bucket
|
||||||
|
* @param {string} storageBucketId ID of the storage bucket to be deleted
|
||||||
|
*/
|
||||||
|
exports.deleteStorageBucketCli = async (storageBucketId) => {
|
||||||
|
try {
|
||||||
|
exec('boundary storage-buckets delete -id=' + storageBucketId);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`${e.stderr}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the boundary CLI to delete the specified policy
|
||||||
|
* @param {string} policyId ID of the policy to be deleted
|
||||||
|
*/
|
||||||
|
exports.deletePolicyCli = async (policyId) => {
|
||||||
|
try {
|
||||||
|
exec('boundary policies delete -id=' + policyId);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`${e.stderr}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the boundary CLI to create a new project.
|
* Uses the boundary CLI to create a new project.
|
||||||
* @param {string} orgId ID of the organization under which the project will be created.
|
* @param {string} orgId ID of the organization under which the project will be created.
|
||||||
@@ -217,7 +247,6 @@ exports.makeAuthMethodPrimaryCli = async (scopeId, authMethodId) => {
|
|||||||
exports.createNewPasswordAccountCli = async (authMethodId) => {
|
exports.createNewPasswordAccountCli = async (authMethodId) => {
|
||||||
let passwordAccount;
|
let passwordAccount;
|
||||||
const login = 'test-login';
|
const login = 'test-login';
|
||||||
process.env.ACCOUNT_PASSWORD = 'test-password';
|
|
||||||
try {
|
try {
|
||||||
passwordAccount = JSON.parse(
|
passwordAccount = JSON.parse(
|
||||||
execSync(
|
execSync(
|
||||||
@@ -226,6 +255,7 @@ exports.createNewPasswordAccountCli = async (authMethodId) => {
|
|||||||
-login-name ${login} \
|
-login-name ${login} \
|
||||||
-password env://ACCOUNT_PASSWORD \
|
-password env://ACCOUNT_PASSWORD \
|
||||||
-format json`,
|
-format json`,
|
||||||
|
{ env: { ...process.env, ACCOUNT_PASSWORD: 'test-password' } },
|
||||||
),
|
),
|
||||||
).item;
|
).item;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -427,12 +457,14 @@ exports.createNewStaticCredentialStoreCli = async (projectId) => {
|
|||||||
/**
|
/**
|
||||||
* Uses the boundary CLI to create a new vault credential store.
|
* Uses the boundary CLI to create a new vault credential store.
|
||||||
* @param {string} projectId ID of the project under which the credential store will be created.
|
* @param {string} projectId ID of the project under which the credential store will be created.
|
||||||
|
* @param {string} vaultAddr Address of the vault that the credential store will be created for.
|
||||||
* @param {string} secretPolicyName Name of the secret policy that's used to create vault token.
|
* @param {string} secretPolicyName Name of the secret policy that's used to create vault token.
|
||||||
* @param {string} boundaryPolicyName Name of the boundary policy that's used to create vault token.
|
* @param {string} boundaryPolicyName Name of the boundary policy that's used to create vault token.
|
||||||
* @returns {Promise<string>} new credential store's ID
|
* @returns {Promise<string>} new credential store's ID
|
||||||
*/
|
*/
|
||||||
exports.createNewVaultCredentialStoreCli = async (
|
exports.createNewVaultCredentialStoreCli = async (
|
||||||
projectId,
|
projectId,
|
||||||
|
vaultAddr,
|
||||||
secretPolicyName,
|
secretPolicyName,
|
||||||
boundaryPolicyName,
|
boundaryPolicyName,
|
||||||
) => {
|
) => {
|
||||||
@@ -463,7 +495,7 @@ exports.createNewVaultCredentialStoreCli = async (
|
|||||||
`boundary credential-stores create vault \
|
`boundary credential-stores create vault \
|
||||||
-scope-id ${projectId} \
|
-scope-id ${projectId} \
|
||||||
-name ${credentialStoreName} \
|
-name ${credentialStoreName} \
|
||||||
-vault-address ${process.env.E2E_VAULT_ADDR} \
|
-vault-address ${vaultAddr} \
|
||||||
-vault-token ${clientToken} \
|
-vault-token ${clientToken} \
|
||||||
-format json`,
|
-format json`,
|
||||||
),
|
),
|
||||||
@@ -482,7 +514,6 @@ exports.createNewVaultCredentialStoreCli = async (
|
|||||||
exports.createNewUsernamePasswordCredentialCli = async (credentialStoreId) => {
|
exports.createNewUsernamePasswordCredentialCli = async (credentialStoreId) => {
|
||||||
let usernamePasswordCredential;
|
let usernamePasswordCredential;
|
||||||
const login = 'test-login';
|
const login = 'test-login';
|
||||||
process.env.CREDENTIALS_PASSWORD = 'credentials-password';
|
|
||||||
try {
|
try {
|
||||||
usernamePasswordCredential = JSON.parse(
|
usernamePasswordCredential = JSON.parse(
|
||||||
execSync(
|
execSync(
|
||||||
@@ -492,6 +523,9 @@ exports.createNewUsernamePasswordCredentialCli = async (credentialStoreId) => {
|
|||||||
-username ${login} \
|
-username ${login} \
|
||||||
-password env://CREDENTIALS_PASSWORD \
|
-password env://CREDENTIALS_PASSWORD \
|
||||||
-format json`,
|
-format json`,
|
||||||
|
{
|
||||||
|
env: { ...process.env, CREDENTIALS_PASSWORD: 'credentials-password' },
|
||||||
|
},
|
||||||
),
|
),
|
||||||
).item;
|
).item;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -580,3 +614,29 @@ exports.getSessionCli = async (orgName, projectName, targetName) => {
|
|||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the boundary CLI to wait for a session recording to be available.
|
||||||
|
* @param {string} storageBucketId ID of storage bucket that the session recording is associated with
|
||||||
|
* @returns An object representing a session recording
|
||||||
|
*/
|
||||||
|
exports.waitForSessionRecordingCli = async (storageBucketId) => {
|
||||||
|
let i = 0;
|
||||||
|
let filteredSessionRecording = [];
|
||||||
|
do {
|
||||||
|
i = i + 1;
|
||||||
|
const sessionRecordings = JSON.parse(
|
||||||
|
execSync('boundary session-recordings list -format json'),
|
||||||
|
);
|
||||||
|
filteredSessionRecording = sessionRecordings.items.filter(
|
||||||
|
(obj) =>
|
||||||
|
obj.storage_bucket_id == storageBucketId && obj.state == 'available',
|
||||||
|
);
|
||||||
|
if (filteredSessionRecording.length > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
} while (i < 5);
|
||||||
|
|
||||||
|
return filteredSessionRecording[0];
|
||||||
|
};
|
||||||
@@ -118,15 +118,16 @@ exports.createNewHostSet = async (page) => {
|
|||||||
/**
|
/**
|
||||||
* Uses the UI to create a new host in a host set. Assumes you have just created a new host set.
|
* Uses the UI to create a new host in a host set. Assumes you have just created a new host set.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} address Address of the host
|
||||||
* @returns Name of the host
|
* @returns Name of the host
|
||||||
*/
|
*/
|
||||||
exports.createNewHostInHostSet = async (page) => {
|
exports.createNewHostInHostSet = async (page, address) => {
|
||||||
const hostName = 'Host ' + nanoid();
|
const hostName = 'Host ' + nanoid();
|
||||||
await page.getByText('Manage').click();
|
await page.getByText('Manage').click();
|
||||||
await page.getByRole('link', { name: 'Create and Add Host' }).click();
|
await page.getByRole('link', { name: 'Create and Add Host' }).click();
|
||||||
await page.getByLabel('Name').fill(hostName);
|
await page.getByLabel('Name').fill(hostName);
|
||||||
await page.getByLabel('Description').fill('This is an automated test');
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
await page.getByLabel('Address').fill(process.env.E2E_TARGET_ADDRESS);
|
await page.getByLabel('Address').fill(address);
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
@@ -138,7 +139,7 @@ exports.createNewHostInHostSet = async (page) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the UI to create a credential store. Assumes you have selected the desired project.
|
* Uses the UI to create a static credential store. Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
* @returns Name of the credential store
|
* @returns Name of the credential store
|
||||||
*/
|
*/
|
||||||
@@ -166,12 +167,47 @@ exports.createStaticCredentialStore = async (page) => {
|
|||||||
return credentialStoreName;
|
return credentialStoreName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a vault credential store. Assumes you have selected the desired project.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} vaultAddr address of the vault server
|
||||||
|
* @param {string} clientToken vault token to connect to boundary
|
||||||
|
* @returns Name of the credential store
|
||||||
|
*/
|
||||||
|
exports.createVaultCredentialStore = async (page, vaultAddr, clientToken) => {
|
||||||
|
const credentialStoreName = 'Credential Store ' + nanoid();
|
||||||
|
await page
|
||||||
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
|
.getByRole('link', { name: 'Credential Stores' })
|
||||||
|
.click();
|
||||||
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
|
await page.getByLabel('Name', { exact: true }).fill(credentialStoreName);
|
||||||
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
|
await page.getByRole('group', { name: 'Type' }).getByLabel('Vault').click();
|
||||||
|
await page.getByLabel('Address', { exact: true }).fill(vaultAddr);
|
||||||
|
await page.getByLabel('Token', { exact: true }).fill(clientToken);
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByRole('navigation', { name: 'breadcrumbs' })
|
||||||
|
.getByText(credentialStoreName),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
return credentialStoreName;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the UI to create a static key pair credential . Assumes you have selected the desired project.
|
* Uses the UI to create a static key pair credential . Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} username username for the credential
|
||||||
|
* @param {string} keyPath path to the private key
|
||||||
* @returns Name of the credential
|
* @returns Name of the credential
|
||||||
*/
|
*/
|
||||||
exports.createStaticCredentialKeyPair = async (page) => {
|
exports.createStaticCredentialKeyPair = async (page, username, keyPath) => {
|
||||||
const credentialName = 'Credential ' + nanoid();
|
const credentialName = 'Credential ' + nanoid();
|
||||||
await page.getByRole('link', { name: 'Credentials', exact: true }).click();
|
await page.getByRole('link', { name: 'Credentials', exact: true }).click();
|
||||||
await page.getByRole('link', { name: 'New', exact: true }).click();
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
@@ -181,10 +217,8 @@ exports.createStaticCredentialKeyPair = async (page) => {
|
|||||||
.getByRole('group', { name: 'Type' })
|
.getByRole('group', { name: 'Type' })
|
||||||
.getByLabel('Username & Key Pair')
|
.getByLabel('Username & Key Pair')
|
||||||
.click();
|
.click();
|
||||||
await page
|
await page.getByLabel('Username', { exact: true }).fill(username);
|
||||||
.getByLabel('Username', { exact: true })
|
const keyData = await readFile(keyPath, {
|
||||||
.fill(process.env.E2E_SSH_USER);
|
|
||||||
const keyData = await readFile(process.env.E2E_SSH_KEY_PATH, {
|
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
await page.getByLabel('SSH Private Key', { exact: true }).fill(keyData);
|
await page.getByLabel('SSH Private Key', { exact: true }).fill(keyData);
|
||||||
@@ -202,12 +236,107 @@ exports.createStaticCredentialKeyPair = async (page) => {
|
|||||||
return credentialName;
|
return credentialName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a vault-generic credential library. Assumes you have selected
|
||||||
|
* the desired credential store.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} vaultPath path to secret in vault
|
||||||
|
* @param {string} credentialType type of credential for credential injection
|
||||||
|
* @returns Name of the credential library
|
||||||
|
*/
|
||||||
|
exports.createVaultGenericCredentialLibrary = async (
|
||||||
|
page,
|
||||||
|
vaultPath,
|
||||||
|
credentialType,
|
||||||
|
) => {
|
||||||
|
const credentialLibraryName = 'Credential Library ' + nanoid();
|
||||||
|
await page.getByRole('link', { name: 'Credential Libraries' }).click();
|
||||||
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
|
await page
|
||||||
|
.getByLabel('Name (Optional)', { exact: true })
|
||||||
|
.fill(credentialLibraryName);
|
||||||
|
await page
|
||||||
|
.getByLabel('Description (Optional)')
|
||||||
|
.fill('This is an automated test');
|
||||||
|
await page
|
||||||
|
.getByRole('group', { name: 'Type' })
|
||||||
|
.getByLabel('Generic Secrets')
|
||||||
|
.click();
|
||||||
|
await page.getByLabel('Vault Path').fill(vaultPath);
|
||||||
|
await page
|
||||||
|
.getByRole('combobox', { name: 'Credential Type' })
|
||||||
|
.selectOption(credentialType);
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
|
||||||
|
return credentialLibraryName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a vault-ssh-certificate credential library. Assumes you have selected
|
||||||
|
* the desired credential store.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} vaultPath path to secret in vault
|
||||||
|
* @param {string} username username for the credential
|
||||||
|
* @returns Name of the credential library
|
||||||
|
*/
|
||||||
|
exports.createVaultSshCertificateCredentialLibrary = async (
|
||||||
|
page,
|
||||||
|
vaultPath,
|
||||||
|
username,
|
||||||
|
) => {
|
||||||
|
const credentialLibraryName = 'Credential Library ' + nanoid();
|
||||||
|
|
||||||
|
await page.getByRole('link', { name: 'Credential Libraries' }).click();
|
||||||
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
|
|
||||||
|
// Temporarily putting the Group selection first due to a bug where
|
||||||
|
// Name and Description fields get cleared when the Group is selected
|
||||||
|
await page
|
||||||
|
.getByRole('group', { name: 'Type' })
|
||||||
|
.getByLabel('SSH Certificates')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByLabel('Name (Optional)', { exact: true })
|
||||||
|
.fill(credentialLibraryName);
|
||||||
|
await page
|
||||||
|
.getByLabel('Description (Optional)')
|
||||||
|
.fill('This is an automated test');
|
||||||
|
await page.getByLabel('Vault Path').fill(vaultPath);
|
||||||
|
await page.getByLabel('Username').fill(username);
|
||||||
|
await page.getByLabel('Key Type').selectOption('ecdsa');
|
||||||
|
await page.getByLabel('Key Bits').fill('521');
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('group', { name: 'Extensions' })
|
||||||
|
.getByLabel('Key')
|
||||||
|
.fill('permity-pty');
|
||||||
|
await page
|
||||||
|
.getByRole('group', { name: 'Extensions' })
|
||||||
|
.getByRole('button', { name: 'Add' })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
|
||||||
|
return credentialLibraryName;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the UI to create a new target. Assumes you have selected the desired project.
|
* Uses the UI to create a new target. Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} port Port of the target
|
||||||
* @returns Name of the target
|
* @returns Name of the target
|
||||||
*/
|
*/
|
||||||
exports.createNewTarget = async (page) => {
|
exports.createNewTarget = async (page, port) => {
|
||||||
const targetName = 'Target ' + nanoid();
|
const targetName = 'Target ' + nanoid();
|
||||||
await page
|
await page
|
||||||
.getByRole('navigation', { name: 'Resources' })
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
@@ -216,7 +345,7 @@ exports.createNewTarget = async (page) => {
|
|||||||
await page.getByRole('link', { name: 'New', exact: true }).click();
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
await page.getByLabel('Name').fill(targetName);
|
await page.getByLabel('Name').fill(targetName);
|
||||||
await page.getByLabel('Description').fill('This is an automated test');
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
await page.getByLabel('Default Port').fill(process.env.E2E_TARGET_PORT);
|
await page.getByLabel('Default Port').fill(port);
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
@@ -232,9 +361,11 @@ exports.createNewTarget = async (page) => {
|
|||||||
/**
|
/**
|
||||||
* Uses the UI to create a new target with address. Assumes you have selected the desired project.
|
* Uses the UI to create a new target with address. Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} address Address of the target
|
||||||
|
* @param {string} port Port of the target
|
||||||
* @returns Name of the target
|
* @returns Name of the target
|
||||||
*/
|
*/
|
||||||
exports.createNewTargetWithAddress = async (page) => {
|
exports.createNewTargetWithAddress = async (page, address, port) => {
|
||||||
const targetName = 'Target ' + nanoid();
|
const targetName = 'Target ' + nanoid();
|
||||||
await page
|
await page
|
||||||
.getByRole('navigation', { name: 'Resources' })
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
@@ -243,8 +374,8 @@ exports.createNewTargetWithAddress = async (page) => {
|
|||||||
await page.getByRole('link', { name: 'New', exact: true }).click();
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
await page.getByLabel('Name').fill(targetName);
|
await page.getByLabel('Name').fill(targetName);
|
||||||
await page.getByLabel('Description').fill('This is an automated test');
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
await page.getByLabel('Target Address').fill(process.env.E2E_TARGET_ADDRESS);
|
await page.getByLabel('Target Address').fill(address);
|
||||||
await page.getByLabel('Default Port').fill(process.env.E2E_TARGET_PORT);
|
await page.getByLabel('Default Port').fill(port);
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
@@ -261,9 +392,11 @@ exports.createNewTargetWithAddress = async (page) => {
|
|||||||
* Uses the UI to create a new TCP target with address in boundary-enterprise
|
* Uses the UI to create a new TCP target with address in boundary-enterprise
|
||||||
* Assumes you have selected the desired project.
|
* Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} address Address of the target
|
||||||
|
* @param {string} port Port of the target
|
||||||
* @returns Name of the target
|
* @returns Name of the target
|
||||||
*/
|
*/
|
||||||
exports.createTcpTargetWithAddressEnt = async (page) => {
|
exports.createTcpTargetWithAddressEnt = async (page, address, port) => {
|
||||||
const targetName = 'Target ' + nanoid();
|
const targetName = 'Target ' + nanoid();
|
||||||
await page
|
await page
|
||||||
.getByRole('navigation', { name: 'Resources' })
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
@@ -273,8 +406,8 @@ exports.createTcpTargetWithAddressEnt = async (page) => {
|
|||||||
await page.getByLabel('Name').fill(targetName);
|
await page.getByLabel('Name').fill(targetName);
|
||||||
await page.getByLabel('Description').fill('This is an automated test');
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
await page.getByRole('group', { name: 'Type' }).getByLabel('TCP').click();
|
await page.getByRole('group', { name: 'Type' }).getByLabel('TCP').click();
|
||||||
await page.getByLabel('Target Address').fill(process.env.E2E_TARGET_ADDRESS);
|
await page.getByLabel('Target Address').fill(address);
|
||||||
await page.getByLabel('Default Port').fill(process.env.E2E_TARGET_PORT);
|
await page.getByLabel('Default Port').fill(port);
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
@@ -291,9 +424,11 @@ exports.createTcpTargetWithAddressEnt = async (page) => {
|
|||||||
* Uses the UI to create a new SSH target with address in boundary-enterprise
|
* Uses the UI to create a new SSH target with address in boundary-enterprise
|
||||||
* Assumes you have selected the desired project.
|
* Assumes you have selected the desired project.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} address Address of the target
|
||||||
|
* @param {string} port Port of the target
|
||||||
* @returns Name of the target
|
* @returns Name of the target
|
||||||
*/
|
*/
|
||||||
exports.createSshTargetWithAddressEnt = async (page) => {
|
exports.createSshTargetWithAddressEnt = async (page, address, port) => {
|
||||||
const targetName = 'Target ' + nanoid();
|
const targetName = 'Target ' + nanoid();
|
||||||
await page
|
await page
|
||||||
.getByRole('navigation', { name: 'Resources' })
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
@@ -303,8 +438,48 @@ exports.createSshTargetWithAddressEnt = async (page) => {
|
|||||||
await page.getByLabel('Name').fill(targetName);
|
await page.getByLabel('Name').fill(targetName);
|
||||||
await page.getByLabel('Description').fill('This is an automated test');
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click();
|
await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click();
|
||||||
await page.getByLabel('Target Address').fill(process.env.E2E_TARGET_ADDRESS);
|
await page.getByLabel('Target Address').fill(address);
|
||||||
await page.getByLabel('Default Port').fill(process.env.E2E_TARGET_PORT);
|
await page.getByLabel('Default Port').fill(port);
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
return targetName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a new SSH target with address in boundary-enterprise
|
||||||
|
* Assumes you have selected the desired project.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} address Address of the target
|
||||||
|
* @param {string} port Port of the target
|
||||||
|
* @param {string} workerFilterEgress Egress worker filter
|
||||||
|
* @returns Name of the target
|
||||||
|
*/
|
||||||
|
exports.createSshTargetWithAddressAndWorkerFilterEnt = async (
|
||||||
|
page,
|
||||||
|
address,
|
||||||
|
port,
|
||||||
|
workerFilterEgress,
|
||||||
|
) => {
|
||||||
|
const targetName = 'Target ' + nanoid();
|
||||||
|
await page
|
||||||
|
.getByRole('navigation', { name: 'Resources' })
|
||||||
|
.getByRole('link', { name: 'Targets' })
|
||||||
|
.click();
|
||||||
|
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||||
|
await page.getByLabel('Name').fill(targetName);
|
||||||
|
await page.getByLabel('Description').fill('This is an automated test');
|
||||||
|
await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click();
|
||||||
|
await page.getByLabel('Target Address').fill(address);
|
||||||
|
await page.getByLabel('Default Port').fill(port);
|
||||||
|
await page.getByLabel('Egress worker filter').click();
|
||||||
|
await page.getByRole('textbox', { name: 'Filter' }).fill(workerFilterEgress);
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
@@ -372,7 +547,8 @@ exports.addHostSourceToTarget = async (page, hostSourceName) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the UI to navigate to Sessions and waits for the session to appear.
|
* Uses the UI to navigate to Sessions and waits for the session to appear
|
||||||
|
* and be in Active state.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
* @param {string} targetName Name of the target associated with the session
|
* @param {string} targetName Name of the target associated with the session
|
||||||
*/
|
*/
|
||||||
@@ -383,12 +559,16 @@ exports.waitForSessionToBeVisible = async (page, targetName) => {
|
|||||||
.click();
|
.click();
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let sessionIsVisible = false;
|
let sessionIsVisible = false;
|
||||||
|
let sessionIsActive = false;
|
||||||
do {
|
do {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
sessionIsVisible = await page
|
sessionIsVisible = await page
|
||||||
.getByRole('cell', { name: targetName })
|
.getByRole('cell', { name: targetName })
|
||||||
.isVisible();
|
.isVisible();
|
||||||
if (sessionIsVisible) {
|
sessionIsActive = await page
|
||||||
|
.getByRole('cell', { name: 'Active' })
|
||||||
|
.isVisible();
|
||||||
|
if (sessionIsVisible && sessionIsActive) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
await page.getByRole('button', { name: 'Refresh' }).click();
|
await page.getByRole('button', { name: 'Refresh' }).click();
|
||||||
@@ -668,9 +848,10 @@ exports.addMemberToGroup = async (page, userName) => {
|
|||||||
/**
|
/**
|
||||||
* Uses the UI to create a new role. Assumes you have selected the desired scope.
|
* Uses the UI to create a new role. Assumes you have selected the desired scope.
|
||||||
* @param {Page} page Playwright page object
|
* @param {Page} page Playwright page object
|
||||||
* @param {string} roleName Name of the new role
|
|
||||||
*/
|
*/
|
||||||
exports.createNewRole = async (page, roleName) => {
|
exports.createRole = async (page) => {
|
||||||
|
const roleName = 'Role ' + nanoid();
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('navigation', { name: 'IAM' })
|
.getByRole('navigation', { name: 'IAM' })
|
||||||
.getByRole('link', { name: 'Roles' })
|
.getByRole('link', { name: 'Roles' })
|
||||||
@@ -685,6 +866,8 @@ exports.createNewRole = async (page, roleName) => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(roleName),
|
page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(roleName),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
|
return roleName;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -729,3 +912,116 @@ exports.addGrantsToGroup = async (page, grants) => {
|
|||||||
page.getByRole('textbox', { name: 'Grant', exact: true }),
|
page.getByRole('textbox', { name: 'Grant', exact: true }),
|
||||||
).toHaveValue(grants);
|
).toHaveValue(grants);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a new Storage Bucket. Assumes you have selected the desired scope.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} bucketName Name of the Storage Bucket
|
||||||
|
* @param {string} region Region of the Storage Bucket
|
||||||
|
* @param {string} accessKeyId Access Key ID for the Storage Bucket
|
||||||
|
* @param {string} secretAccessKey Secret Access Key for the Storage Bucket
|
||||||
|
* @param {string} workerFilter Worker filter for the Storage Bucket
|
||||||
|
* @returns Name of the Storage Bucket
|
||||||
|
*/
|
||||||
|
exports.createStorageBucket = async (
|
||||||
|
page,
|
||||||
|
bucketName,
|
||||||
|
region,
|
||||||
|
accessKeyId,
|
||||||
|
secretAccessKey,
|
||||||
|
workerFilter,
|
||||||
|
) => {
|
||||||
|
const storageBucketName = 'Bucket ' + nanoid();
|
||||||
|
await page
|
||||||
|
.getByRole('link', { name: 'Storage Buckets', exact: true })
|
||||||
|
.click();
|
||||||
|
await page.getByRole('link', { name: 'New Storage Bucket' }).click();
|
||||||
|
await page.getByLabel(new RegExp('Name*')).fill(storageBucketName);
|
||||||
|
await page.getByLabel('Bucket name').fill(bucketName);
|
||||||
|
await page.getByLabel('Region').fill(region);
|
||||||
|
await page.getByLabel('Access key ID').fill(accessKeyId);
|
||||||
|
await page.getByLabel('Secret access key').fill(secretAccessKey);
|
||||||
|
await page.getByLabel('Worker filter').fill(workerFilter);
|
||||||
|
await page.getByLabel('Disable credential rotation').click();
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByRole('navigation', { name: 'breadcrumbs' })
|
||||||
|
.getByText(storageBucketName),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
return storageBucketName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to enable session recording for a target. Assumes you have selected the desired target.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} storageBucketName name of the Storage Bucket used for session recording
|
||||||
|
*/
|
||||||
|
exports.enableSessionRecording = async (page, storageBucketName) => {
|
||||||
|
await page.getByText('Enable recording').click();
|
||||||
|
await page.getByLabel('Record sessions for this target').click();
|
||||||
|
await page
|
||||||
|
.getByLabel('AWS storage buckets')
|
||||||
|
.selectOption({ label: storageBucketName });
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('listitem').getByText(storageBucketName),
|
||||||
|
).toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to create a Storage Policy. Assumes you have selected the desired scope.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @returns Name of the Storage Policy
|
||||||
|
*/
|
||||||
|
exports.createStoragePolicy = async (page) => {
|
||||||
|
const storagePolicyName = 'Policy ' + nanoid();
|
||||||
|
await page
|
||||||
|
.getByRole('link', { name: 'Storage Policies', exact: true })
|
||||||
|
.click();
|
||||||
|
await page
|
||||||
|
.getByRole('link', { name: 'Create a new storage policy', exact: true })
|
||||||
|
.click();
|
||||||
|
await page.getByLabel('Name').fill(storagePolicyName);
|
||||||
|
await page.getByLabel('Retention Policy').selectOption({ label: 'Forever' });
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByRole('navigation', { name: 'breadcrumbs' })
|
||||||
|
.getByText(storagePolicyName),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
return storagePolicyName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the UI to attach Storage Policy to a scope. Assumes you have selected the desired scope.
|
||||||
|
* @param {Page} page Playwright page object
|
||||||
|
* @param {string} policyName name of the Policy to be attached to the scope
|
||||||
|
*/
|
||||||
|
exports.attachStoragePolicy = async (page, policyName) => {
|
||||||
|
await page
|
||||||
|
.getByRole('link', { name: new RegExp('/*Settings'), exact: true })
|
||||||
|
.click();
|
||||||
|
await page.getByText('Add Storage Policy').click();
|
||||||
|
await page.getByLabel('Storage Policy').selectOption({ label: policyName });
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
await expect(page.getByRole('listitem').getByText(policyName)).toBeVisible();
|
||||||
|
};
|
||||||
Loaded 100 of 142 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user