Compare commits

..
52 Commits
Author SHA1 Message Date
798cfc6828 chore: 🤖 Update desktop client changelog (#2183)
Co-authored-by: Cameron Perera <cameron.perera@hashicorp.com>
Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
2024-03-19 12:23:50 -07:00
Lisbet Alvarez d93986c4e2 refactor: 💡 remove route-action usage from credential-stores (#2172)
* refactor: 💡 remove route-action usage from credential-stores

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12604
2024-03-18 16:34:37 -07:00
Michael Li 879c196887 refact(e2e): Move env outside of helper methods (#2186)
* refact(e2e): Move env outside of UI helper methods

* refact(e2e): Move env outside of CLI helpers
2024-03-18 15:53:39 -04:00
Lisbet Alvarez 98170ed01e refactor: 💡 Remove route-action usage for managed-groups (#2168)
* refactor: 💡 Remove route-action usage for managed-groups

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12619
2024-03-18 10:34:54 -07:00
Dharini Jeeva 47a218cf8f chore: Add routes for aliases (#2185)
alias resource routes
2024-03-15 14:48:32 -07:00
Dharini Jeeva 879a58dcb8 feat: 🎸 mirage support and initial route for aliases (#2181)
* feat: 🎸 mirage support and initial route for aliases
2024-03-15 14:24:20 -07:00
Stan RyzhovandMichael Li f100190ac1 test: 💍 Add storage policies e2e-test (#2175)
* test: 💍 Add storage policies e2e-test

✅ Closes: ICU-12883

* chore: Adjust required env for e2e-tests

✅ Closes: ICU-12883

* CR: Refactor helper method to pass in env

---------

Co-authored-by: Michael Li <michael.li@hashicorp.com>
2024-03-15 11:55:13 -04:00
Lisbet Alvarez 0902e42cb0 refactor: 💡 remove route-action usage for groups (#2176)
* refactor: 💡 remove route-action usage for groups

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12605
2024-03-14 13:28:43 -07:00
Michael Li 6b0958e4d8 test(e2e): Admin UI test fixes (#2182) 2024-03-14 14:17:54 -04:00
Cameron Perera 82ea70f44b fix: 🐛 update destination path for autoUpdater workflow (#2179)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12878
2024-03-13 10:46:22 -05:00
hashicorp-copywrite[bot] 0bb72040db [COMPLIANCE] Add Copyright and License Headers (#2177)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2024-03-11 11:51:45 -05:00
Michael Li 9d9a38510b test(e2e): Add test for SSH certification injection (#2170) 2024-03-07 17:05:45 -05:00
Lisbet Alvarez a54aecee07 refactor: 💡 remove route-action usage in accounts (#2134)
* refactor: 💡 remove route-action usage in accounts

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12616
2024-03-07 09:24:28 -08:00
Devon Anderson 7dab184bb9 chore: 🤖 Migrate form.input to Hds:TExtInput::Field (#2149)
* chore: 🤖 Migrate `form.input` to `Hds:TExtInput::Field`

Migrate `form/account/oidc` and `form/account/password` forms to use
`Hds::TextInput::Field` instead of `form.input`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12457

* refactor: 💡 argument clean up

argument clean up

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12457

* refactor: 💡 switch to `readonly` arument instead of `disabled`

switch to `readonly` arument instead of `disabled`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12457

* refactor: 💡 fix incorrect attributes

fix incorrect attributes

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12457

* refactor: 💡 fix failing test

fix failing test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12457
2024-03-07 10:12:33 -05:00
Lisbet Alvarez f068ac0b0c refactor: 💡 remove usage of route-action for app route (#2135)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12615
2024-03-04 10:13:59 -08:00
Michael Li d07c33ca41 test(e2e): Add test for SSH Credential Injection with Vault SSH Private Key (#2166) 2024-03-01 13:35:47 -05:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] f073de774d Result of tsccr-helper -log-level=info gha update -latest . (#2167)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-03-01 09:48:29 -08:00
Michael Li 1040a05880 test(e2e): Add LDAP Admin UI e2e test (#2164) 2024-02-29 16:51:32 -05:00
Lisbet Alvarez 002f64d676 update cli version and update changelog (#2165)
* docs: ✏️ update cli version and update changelog
2024-02-28 10:31:34 -08:00
Devon Anderson c09efbae10 chore: 🤖 Migrate form.inut to Hds::TextInput::Field (#2151)
* chore: 🤖 Migrate `form.inut` to `Hds::TextInput::Field`

Migrate `form/create-worker-led` form to use `Hds:Text::InputField`
instead of `form.input`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12477

* refactor: 💡 Added `optional` and `requred` arguments for fields

Added `optional` and `require` arguments for all needed fields.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12477

* refactor: 💡 fix failing test

fix failing test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12477
2024-02-28 13:02:02 -05:00
Devon Anderson 060f995f94 chore: 🤖 Migrate form.input to Hds::TextInput::Field (#2143)
* chore: 🤖 Migrate `form.input` to `Hds::TextInput::Field`

Migrate `form/credential` forms for for `json`, `ssh_private_key`,
`username_password`, and `form/credential-store` forms for `static` and
`vault` to use `Hds::TextInput::Field` instead of `form.input`.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 fix broken test

fix broken test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 fix broken test

fix broken test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 added `@hasVisibilityToggle` back and clean up test

Added `@hasVisibilityToggle` back and clean up test by use `data-test-*`
attributes for error messaging

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 set `@hasVisiblityToggle` to default

set `@hasVisiblityToggle` to default

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 field argument clean up

field argument cleanup

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 switch `disabled` argument to `readonly`

switch `disabled` argument to `readonly`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461

* refactor: 💡 clean up attribute order

clean up field attribute order

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12461
2024-02-28 12:21:37 -05:00
Devon Anderson f1c5c99664 chore: 🤖 Migrate form.input to Hds::TextInput::Field (#2147)
* chore: 🤖 Migrate `form.input` to `Hds::TextInput::Field`

Migrate `form/host-set/aws`, `form/host-set/azure` and
`form/host-set/static` forms to use `Hds::TextInputField` instead of
`form.input`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12469

* Update index.hbs

removed `@isOptional` field.

* Update index.hbs

removed `@isOptional` argument

* refactor: 💡 fix lint errors

fix lint

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12469

* refactor: 💡 field argument cleanup

field argument cleanup

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12469

* refactor: 💡 switch `disabled` argument to `readonly`

switch `disabled` argument to `readonly`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12469

* refactor: 💡 order of attributes

change order of attributes

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12469
2024-02-28 10:18:22 -05:00
af5fb8e814 chore(deps): bump es5-ext from 0.10.53 to 0.10.63 (#2162)
Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.53 to 0.10.63.
- [Release notes](https://github.com/medikoo/es5-ext/releases)
- [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.53...v0.10.63)

---
updated-dependencies:
- dependency-name: es5-ext
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dharini Jeeva <dharini@hashicorp.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2024-02-27 16:49:29 -05:00
Devon Anderson cf5334556f chore: 🤖 Migrate form.input to Hds::TextInput:Field (#2150)
* chore: 🤖 Migrate `form.input` to `Hds::TextInput:Field`

Migrate  `form/manage-groups/oidc`, `form/user` and `form/worker` forms
to use `Hds::TextInput::Field` instead of `form.input`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12473

* refactor: 💡 argument clean up

argument clean up

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12473

* refactor: 💡 refactor InfoField arguments for `readonly`

Refactor InfoField component's arguments for `readonly` instead of
`disabled`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12473
2024-02-27 14:57:54 -05:00
Dharini Jeeva 5c6c9bb69f fix: 🐛 move config processing to mirage serializer (#2163) 2024-02-27 11:25:51 -08:00
Devon Anderson 26a77edbc7 chore: 🤖 Migrate form.inpu to use Hds::TextInput::Field` (#2145)
* chore: 🤖 Migrate `form.inpu` to use Hds::TextInput::Field`

Migrate `form/group`, `form/host-static`, `form/host-catalog/aws` and
`form/host-catalog/azure` forms to use `Hds::TextInputField instead of
`form.input`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12466

* refactor: 💡 use `data-test` attributes for testing

use `data-test` attributes for testing error messaging.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12466

* refactor: 💡 field argument cleanup

field argument cleanup

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12466

* refactor: 💡 switch `disabled` argument to `readonly`

switch `disabled` argument to `readonly`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12466
2024-02-27 13:52:50 -05:00
Dharini Jeeva 3fc865d226 Fix credential library mocks (#2161)
* fix: 🐛 vault cred library mocks
2024-02-27 09:41:28 -08:00
Devon Anderson 47d7ceccf7 chore: Migrate form.input to use Hds text input field (#2154)
* chore: 🤖 Migrate `form.input` to use `Hds` form input field

Migrate `form.input` for `clusterurl.hbs` to use
`Hds::Form::TextInput::Field`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12478

* refactor: 💡 removed unused `data-test` attribute

removed unused `data-test` attribute

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12478
2024-02-27 09:31:48 -05:00
Dharini Jeeva 38df28be55 fix: 🐛 change delete to detach Storage policy from a scope (#2160)
* fix: 🐛 change delete to detach Storage policy from a scope
2024-02-26 12:50:38 -08:00
Dharini Jeeva 6d7c86a08c fix: 🐛 spelling (#2159)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12813
2024-02-26 11:18:34 -08:00
Michael Li 58a31b880b test(e2e): Add enterprise tests for creating TCP/SSH targets (#2158) 2024-02-26 12:59:34 -05:00
Dharini Jeeva 453ab7911d doc: var name (#2140)
* fix: 🐛 var name
2024-02-23 15:52:08 -08:00
Dharini Jeeva 93c8d481fd Icu 12567 add new field credential mapping overrides (#2156)
* chore: 🤖 add credential type select list
2024-02-23 10:07:52 -08:00
Carlos Alcaide Corvo 956f6fd04d refactor: 💡 Adopt HDS copy snippet Desktop UI (#2146)
* refactor: 💡 Adopt HDS copy snippet Desktop UI

Adopt HDS copy snippet and delete usage of Copyable custom component
2024-02-22 13:56:56 -08:00
dependabot[bot]andZhihe Li 912be13744 chore(deps): bump ip from 1.1.8 to 1.1.9 in /ui/desktop/electron-app (#2155)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2024-02-22 15:28:18 -05:00
dependabot[bot]andZhihe Li fc1b91c8f9 chore(deps): bump ip from 1.1.5 to 1.1.9 (#2153)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2024-02-22 15:10:54 -05:00
Devon Anderson 8e40b01caf refactor: 💡 Quick fix for client_secret attributes (#2157)
`client_secret` attribute needed to be `@type` instead of `type`
2024-02-22 14:52:31 -05:00
Devon Anderson 20181c978f refactor: 💡 Migrate form.input to Hds::TextInput::Field (#2142)
* refactor: 💡 Migrate `form.input` to `Hds::TextInput::Field`

Migrate the use of `form.input` to `Hds::TextInput::Field` for OIDC and
Password auth-method form

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459

* Update index.hbs

move `name` attribute to top of attribute list for readers

* refactor: 💡 fix `step` and `min` arguments

fix `step` and `min` arguments

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459

* refactor: 💡 clean up order of arguments and add `data-test` att

clean up order of arguments and add `data-test` attributes

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459

* refactor: 💡 Add `@isOptional` attribute to all needed fields

Add `@isOptiona` attributes to all needed fields

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459

* Update index.hbs

add `@hasVisibilityToggle`

* refactor: 💡 fix lint

fix lint

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459

* refactor: 💡 quick attribute refactor

quick attribute refactor

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12459
2024-02-22 13:22:18 -05:00
Lisbet Alvarez db78f1ae4d docs: ✏️ update enos readme (#2139)
* docs: ✏️ update enos readme

* docs: ✏️ update cmd

* docs: ✏️ update wording

* docs: ✏️ add link

* docs: ✏️ use diff slack channel
2024-02-20 17:57:21 -08:00
Dharini Jeeva 592bb15022 chore: 🤖 add credential type select list (#2144)
* chore: 🤖 add credential type select list
2024-02-16 15:57:14 -08:00
Carlos Alcaide Corvo f002e14673 [Admin UI] Adopt HDS::Copy::Snippet (#2141)
* refactor: 💡 Adopt HDS copy snippet

Adopt HDS copy snippet and delete usage of Copyable custom component
within Admin UI.
2024-02-16 02:19:43 -08:00
Devon Anderson a1b455e5eb refactor: 💡 Migrate last form.fieldset over to use Hds (#2138)
* refactor: 💡 Migrate last `form.fieldset` over to use Hds

Migrate last `form.fieldset` over to use `Hds::FormFieldset`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-9427

* refactor: 💡 removed `fieldset` from `RadioCard`

Removed unneeded `fieldset` from `RadioCard`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-9427
2024-02-15 09:51:39 -05:00
Dharini Jeeva 786e6130cd feat: 🎸 allow session recording deletion (#2132)
* feat: 🎸 allow session recording deletion
2024-02-13 13:26:23 -08:00
hashicorp-copywrite[bot] 4db8b8e1e7 [COMPLIANCE] Add Copyright and License Headers (#2137)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2024-02-12 16:31:46 -05:00
Lisbet Alvarez 4be8d10323 Icu 12557 refresh not working as intended on windows (#2133)
* fix: 🐛 add flag

* refactor: 💡 add true val

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12557
2024-02-09 11:58:41 -08:00
Devon Anderson c79a1fca79 chore: 🤖 Create select field render for the Form::Field::ListWrapper (#2112)
* chore: 🤖 Create `select` field render for the `Form::Field::ListWrapper`
component

Create a `select` field render for the `Form::Field::ListWrapper`
component.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 Added additional test

Added additional testing around removing and adding an option, and
refactored for better usability

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 Refactored getter to check for option and model arg

Refactore getter to check for options and model args.

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 Use select field with `signing_algorithms`

Use select field with `signing_algorithms`

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 fix test

fix test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 fix failing test

fix failing test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271

* refactor: 💡 fix update auth-method test

fix update auth-method test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12271
2024-02-09 08:42:15 -05:00
Cameron Perera d2d332b7d1 refactor: 💡 only show scopes with auth methods at authenticate (#2129)
* refactor: 💡 only show scopes with auth methods at authenticate

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* test: 💍 verify scopes with no auth methods are hidden

when authenticating

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* refactor: 💡 update logic for fetching scopes and auth methods

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* refactor: 💡 PR feedback changes

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* refactor: 💡 added unless block back to template

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* refactor: 💡 update admin authenticate route and template

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355

* test: 💍 add test for orgs with no auth methods admin ui

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-2355
2024-02-08 10:01:20 -06:00
022ac0fffe Result of tsccr-helper -log-level=info gha update -latest . (#2126)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
2024-02-07 16:42:38 -08:00
Cameron Perera f3622fb92b feat: 🎸 remove Type of Worker from worker details page (#2131)
* feat: 🎸 remove Type of Worker from worker details page

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12409

* refactor: 💡 remove unused translation

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12409
2024-02-06 14:48:28 -06:00
Zhihe Li f8b5dcd824 fix: 🐛 Add error notification if adding token to daemon fails (#2127)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12488
2024-02-05 14:21:13 -05:00
Alex 056008e13e chore: 🤖 replace Rose::CodeEditor with Hds::CodeBlock on new worker page (#2128)
* chore: 🤖 replace `Rose::CodeEditor` with `Hds::CodeBlock` for new workers
2024-02-05 09:50:08 -08:00
Alex ea03d6e488 chore: 🤖 update HDS to 3.5.0 (#2116)
* chore: update to 3.4.1

`@hashicorp/design-system-components` to `3.4.1`
 - `@hashicorp/ember-flight-icons` to `4.0.5`

* chore: update to 3.5.0
2024-01-31 15:49:02 -05:00
274 changed files with 6039 additions and 3026 deletions

No files matched your search

+12 -12
View File
@@ -12,14 +12,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn install
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -37,17 +37,17 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn run build:ui:admin:oss
- name: Upload artifact admin-ui
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: admin-ui
path: ./ui/admin/dist/
- name: Upload artifact OSS admin UI
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: admin-ui-oss
path: ./ui/admin/dist/
@@ -57,7 +57,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -65,12 +65,12 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn run build:ui:admin:enterprise
- name: Upload artifact enterprise admin UI
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: admin-ui-ent
path: ./ui/admin/dist/
@@ -80,7 +80,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -88,12 +88,12 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn run build:ui:admin:hcp
- name: Upload artifact HCP admin UI
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: admin-ui-hcp
path: ./ui/admin/dist/
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@4b6aa0b07da05d6e43d0e5f9c8596a6532ce1c85 # codeql-bundle-v2.15.3
uses: github/codeql-action/init@592977e6ae857384aa79bb31e7a1d62d63449ec5 # codeql-bundle-v2.16.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,4 +54,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4b6aa0b07da05d6e43d0e5f9c8596a6532ce1c85 # codeql-bundle-v2.15.3
uses: github/codeql-action/analyze@592977e6ae857384aa79bb31e7a1d62d63449ec5 # codeql-bundle-v2.16.3
+8 -8
View File
@@ -7,14 +7,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn install
@@ -25,14 +25,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn run compliance:licenses
@@ -44,14 +44,14 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn lint
@@ -63,12 +63,12 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 16
- run: yarn test
@@ -21,6 +21,14 @@ export default class SessionRecordingAbility extends ModelAbility {
return !this.model.isUnknown && super.canRead;
}
/**
* Only "known" session recording types may be deleted.
* @type {boolean}
*/
get canDelete() {
return !this.model.isUnknown && super.canDelete && this.model.isAvailable;
}
get canDownload() {
return this.hasAuthorizedAction('download');
}
@@ -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;
}
@@ -75,6 +75,18 @@ export default class GeneratedCredentialLibraryModel extends BaseModel {
})
path;
@attr('string', {
for: 'vault-generic',
description: 'It indicates the type of credential the library returns.',
})
credential_type;
@attr('object', {
for: 'vault-generic',
description: 'It indicates the credential mapping overrides.',
})
credential_mapping_overrides;
@attr('string', {
for: 'vault-ssh-certificate',
isNestedAttribute: true,
+10
View File
@@ -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 {}
@@ -11,6 +11,18 @@ import GeneratedCredentialLibraryModel from '../generated/models/credential-libr
export const options = {
http_method: ['GET', 'POST'],
key_type: ['ed25519', 'ecdsa', 'rsa'],
credential_types: ['ssh_private_key', 'username_password'],
mapping_overrides: {
username_password: {
username_attribute: 'Username',
password_attribute: 'Password',
},
ssh_private_key: {
private_key_attribute: 'Private Key',
private_key_passphrase_attribute: 'Private Key Passphrase',
username_attribute: 'Username',
},
},
};
export const TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC = 'vault-generic';
@@ -7,6 +7,7 @@ import ApplicationSerializer from './application';
import {
TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
options,
} from '../models/credential-library';
export default class CredentialLibrarySerializer extends ApplicationSerializer {
@@ -31,9 +32,34 @@ export default class CredentialLibrarySerializer extends ApplicationSerializer {
return super.serialize(...arguments);
}
}
serializeAttribute(snapshot, json) {
const value = super.serializeAttribute(...arguments);
const { credential_type, type, credential_mapping_overrides, isNew } =
snapshot.record;
if (
type === TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC &&
credential_mapping_overrides &&
credential_type &&
!isNew
) {
// API expects to send null to fields if it is undefined or deleted
json.credential_mapping_overrides = Object.keys(
options.mapping_overrides[credential_type],
).reduce((obj, key) => {
if (credential_mapping_overrides[key]) {
obj[key] = credential_mapping_overrides[key];
} else {
obj[key] = null;
}
return obj;
}, {});
return value;
}
}
serializeVaultGeneric() {
const serialized = super.serialize(...arguments);
if (serialized.attributes) {
// Set `http_request_body` only if `http_method` is POST
// otherwise set to null to have the field removed
+6
View File
@@ -0,0 +1,6 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
export { default } from 'api/models/alias';
+18
View File
@@ -753,6 +753,22 @@ function routes() {
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
this.get(
'/session-recordings',
@@ -798,6 +814,8 @@ function routes() {
},
);
this.del('/session-recordings/:id');
/* Uncomment the following line and the Response import above
* Then change the response code to simulate error responses.
* this.get('/scopes', () => new Response(505));
+22
View File
@@ -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',
],
});
@@ -11,12 +11,18 @@ import {
TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
TYPES_CREDENTIAL_LIBRARY,
options,
} from 'api/models/credential-library';
export default factory.extend({
id: () => generateId('cl_'),
type: (i) => TYPES_CREDENTIAL_LIBRARY[i % TYPES_CREDENTIAL_LIBRARY.length],
credential_type(i) {
if (this.type === TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC) {
return options.credential_types[i % options.credential_types.length];
}
},
authorized_actions: () =>
permissions.authorizedActionsFor('credential-library') || [
'no-op',
+3 -2
View File
@@ -33,12 +33,13 @@ export default factory.extend({
roles: ['create', 'list'],
};
// Worker permissions only available on the global scope
// Resources that are available only on the global scope
if (this.type === 'global') {
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') {
collectionActions['storage-buckets'] = ['create', 'list'];
collectionActions['session-recordings'] = ['list'];
@@ -60,6 +60,7 @@ export default factory.extend({
'read',
'download',
'reapply-storage-policy',
'delete',
],
withNonExistingUserAndTarget: trait({
@@ -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(),
});
+11
View File
@@ -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(),
});
+3
View File
@@ -108,6 +108,9 @@ export default function (server) {
server.createList('policy', 3, { scope: globalScope });
server.createList('policy', 3, { scope: orgScope });
// Aliases
server.createList('alias', 3, { scope: globalScope });
// Other resources
server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => {
server.createList('host-catalog', 8, { scope }, 'withChildren');
@@ -14,6 +14,17 @@ export default ApplicationSerializer.extend({
arguments,
);
json.credential_store_id = model.credentialStoreId;
// BE API only updates these fields if there's an update mask, otherwise they remain unmodified
// but we don't need this flow in our mocks, so we are deleting fields with null value
if (json.credential_mapping_overrides) {
Object.keys(json.credential_mapping_overrides).forEach((key) => {
if (json.credential_mapping_overrides[key] === null) {
delete json.credential_mapping_overrides[key];
}
});
}
return json;
},
});
@@ -5,6 +5,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import {
STATE_SESSION_RECORDING_AVAILABLE,
STATE_SESSION_RECORDING_STARTED,
TYPE_SESSION_RECORDING_SSH,
} from 'api/models/session-recording';
module('Unit | Ability | session-recording', function (hooks) {
setupTest(hooks);
@@ -49,4 +54,36 @@ module('Unit | Ability | session-recording', function (hooks) {
),
);
});
test('can delete a session with proper authorization and in available state', function (assert) {
const recordingNotinAvaialableState = store.createRecord(
'session-recording',
{
authorized_actions: ['delete'],
type: TYPE_SESSION_RECORDING_SSH,
state: STATE_SESSION_RECORDING_STARTED,
},
);
const recordingInAvailableState = store.createRecord('session-recording', {
authorized_actions: ['delete'],
type: TYPE_SESSION_RECORDING_SSH,
state: STATE_SESSION_RECORDING_AVAILABLE,
});
assert.false(
canService.can('delete session-recording', recordingNotinAvaialableState),
);
assert.true(
canService.can('delete session-recording', recordingInAvailableState),
);
});
test('can re-apply Storage policy with proper authorization', function (assert) {
const recording = store.createRecord('session', {
authorized_actions: ['reapply-storage-policy'],
});
assert.true(
canService.can('reapplyStoragePolicy session-recording', recording),
);
});
});
@@ -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);
});
});
@@ -23,6 +23,8 @@ module('Unit | Serializer | credential library', function (hooks) {
path: '/vault/path',
http_method: 'GET',
version: 1,
credential_type: 'ssh_private_key',
credential_mapping_overrides: { username_attribute: 'user' },
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
@@ -32,6 +34,10 @@ module('Unit | Serializer | credential library', function (hooks) {
name: 'Name',
description: 'Description',
version: 1,
credential_type: 'ssh_private_key',
credential_mapping_overrides: {
username_attribute: 'user',
},
attributes: {
path: '/vault/path',
http_method: 'GET',
@@ -54,6 +60,8 @@ module('Unit | Serializer | credential library', function (hooks) {
path: '/vault/path',
http_method: 'GET',
version: 1,
credential_type: 'ssh_private_key',
credential_mapping_overrides: {},
},
},
});
@@ -65,6 +73,12 @@ module('Unit | Serializer | credential library', function (hooks) {
credential_store_id: null,
name: 'Name',
description: 'Description',
credential_type: 'ssh_private_key',
credential_mapping_overrides: {
private_key_attribute: null,
private_key_passphrase_attribute: null,
username_attribute: null,
},
attributes: {
path: '/vault/path',
http_method: 'GET',
@@ -80,6 +94,8 @@ module('Unit | Serializer | credential library', function (hooks) {
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
http_method: '',
path: null,
credential_type: null,
credential_mapping_overrides: {},
});
const vaultSSHCertificateRecord = store.createRecord('credential-library', {
type: TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE,
@@ -99,6 +115,8 @@ module('Unit | Serializer | credential library', function (hooks) {
description: null,
name: null,
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
credential_type: null,
credential_mapping_overrides: {},
});
assert.deepEqual(serializedVaultSSHCertificateRecord, {
attributes: {
@@ -138,6 +156,8 @@ module('Unit | Serializer | credential library', function (hooks) {
description: null,
name: null,
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
credential_type: null,
credential_mapping_overrides: {},
},
'http_request_body attribute is not expected',
);
@@ -156,6 +176,8 @@ module('Unit | Serializer | credential library', function (hooks) {
description: null,
name: null,
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
credential_type: null,
credential_mapping_overrides: {},
},
'http_request_body attribute is expected',
);
+2 -2
View File
@@ -48,6 +48,8 @@ session_max_seconds:
session_connection_limit:
label: Maximum Connections
help: The maximum number of connections allowed per session. For unlimited, specify "-1".
session_id:
label: Session ID
worker_release_version:
label: Release Version
help: The version of the Boundary binary the worker is running.
@@ -58,8 +60,6 @@ worker_last_seen:
help: Last time the worker reported status.
worker_address:
help: URL and port for this worker.
worker_type:
help: Type of worker
host_set:
label: Host Set
grant_scope:
+24 -4
View File
@@ -354,6 +354,8 @@ session-recording:
up: '{bytes} up'
down: '{bytes} down'
duration: '{time} duration'
questions:
delete: Are you sure you want to delete this recording?
connection:
title: Connection
title_index: Connection {index}
@@ -761,6 +763,12 @@ credential-library:
description: A credential library is a resource that provides credentials.
titles:
new: New Credential Library
ssh_private_key: SSH Private Key
username_password: Username & Password
username_attribute: Username
password_attribute: Password
private_key_passphrase_attribute: Private Key Passphrase
private_key_attribute: Private Key
actions:
create: New Credential Library
delete: Delete Credential Library
@@ -772,6 +780,12 @@ credential-library:
vault_path:
label: Vault Path
help: The path the library uses when requesting credentials from Vault.
credential_type:
label: Credential Type
help: Specify the type of credential this library will issue. This must be set to use credential injection. After this data is saved, it cannot be changed later.
credential_mapping_overrides:
label: Credential mapping overrides
help: Specify the credential fields to use instead of the defaults
http_method:
label: HTTP Method
help: The HTTP method the library uses when requesting credentials from Vault.
@@ -839,7 +853,6 @@ credential:
help: The private key to use when connecting with this credential store.
private_key_passphrase:
label: Passphrase
help: Optional.
json:
label: JSON
help: This is the JSON blob that provides credentials to the host. After this data is saved, you will not be able to edit it again without replacing it.
@@ -917,7 +930,7 @@ worker:
title: 1. Configure your new worker
create_directory: Create a configuration file in a new directory on your server
create_config: 'Open the file with a text editor, such as Vi. Paste the following configuration into the worker config file:'
save_config: Save the configuration in your `pki-worker.hcl` file.
save_config: Save the configuration in your <code>pki-worker.hcl</code> file.
2:
title: 2. Install & start Boundary on your server
run_command: 'Run this command on your server to install Boundary:'
@@ -944,11 +957,13 @@ policy:
create_policy_description: Enable session recording for projects in this org.
retain_until: Retain until
actions:
detach: Detach Storage Policy
delete: Delete Storage Policy
add: Add Storage Policy
apply: Apply the storage policy to this scope and its children
new: Add a new storage policy
update: Re-apply storage policy
delete_recording: Delete recording
questions:
detach: Are you sure you want to detach this storage policy?
messages:
@@ -975,8 +990,8 @@ policy:
label: Custom
do_not_protect:
label: Do not protect, allow deletion any time
hippa:
label: HIPPA (6 years)
hipaa:
label: HIPAA (6 years)
soc:
label: SOC 2 (7 years)
deletion_policy_options:
@@ -984,3 +999,8 @@ policy:
label: Do not delete
custom:
label: Custom
alias:
title: Alias
title_plural: Aliases
titles:
new: New Alias
+1 -1
View File
@@ -31,7 +31,7 @@
},
"dependencies": {
"@embroider/test-setup": "^2.1.1",
"@hashicorp/design-system-components": "^3.2.0",
"@hashicorp/design-system-components": "^3.5.0",
"codemirror": "5.65.7",
"ember-auto-import": "^2.6.3",
"ember-cli-babel": "^7.26.11",
@@ -29,4 +29,14 @@ export default class OverrideSessionRecordingAbility extends SessionRecordingAbi
? this.hasAuthorizedAction('reapply-storage-policy')
: false;
}
/**
* This override ensures that session recordings may be deleted only if the
* session-recording feature flag is enabled.
*/
get canDelete() {
return this.features.isEnabled('ssh-session-recording')
? super.canDelete
: false;
}
}
@@ -12,7 +12,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'deleteAccount' @model)}}
{{on 'click' (fn @delete @model)}}
>
{{t 'resources.account.actions.delete'}}
</dropdown.button>
@@ -8,10 +8,4 @@
<DocLink @doc='account' @iconSize='large' />
</h2>
<p>{{t 'resources.account.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -13,10 +13,4 @@
<DocLink @doc='auth-method' @iconSize='large' />
</h2>
<p>{{t 'resources.auth-method.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -12,7 +12,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'deleteManagedGroup' @model)}}
{{on 'click' (fn @delete @model)}}
>
{{t 'resources.managed-group.actions.delete'}}
</dropdown.button>
@@ -8,10 +8,4 @@
<DocLink @doc='managed-groups' @iconSize='large' />
</h2>
<p>{{t 'resources.managed-group.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -1,35 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<span
id={{this.id}}
class='copyable'
{{did-insert this.register}}
{{will-destroy this.tearDown}}
>
{{#if this.copied}}
<mark class='copyable-content'>
{{yield}}
</mark>
{{else}}
<span class='copyable-content'>
{{yield}}
</span>
{{/if}}
{{#if this.isClipboardLibrarySupported}}
<Rose::Button
id={{this.copyableButtonId}}
aria-describedby={{this.id}}
class='copyable-button'
@style='ghost'
@iconOnly={{if this.copied this.successIcon this.actionIcon}}
title={{if this.copied this.successText this.actionText}}
>
{{this.actionText}}
</Rose::Button>
{{/if}}
</span>
-68
View File
@@ -1,68 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import ClipboardJS from 'clipboard';
import { action } from '@ember/object';
import { generateComponentID } from 'rose/utilities/component-auto-id';
import { tracked } from '@glimmer/tracking';
import { later } from '@ember/runloop';
export default class CopyableComponent extends Component {
// =attributes
id = generateComponentID();
copyableButtonId = `copyable-button-${this.id}`;
actionIcon = 'flight-icons/svg/clipboard-copy-16';
successIcon = 'flight-icons/svg/clipboard-checked-16';
actionText = this.args.buttonText;
successText = this.args.acknowledgeText;
@tracked copied = false;
clipboard = null;
confirmCopy() {
this.copied = true;
later(() => (this.copied = false), 1000);
}
/**
* Checks for ClipboardJS support.
* @return {boolean}
*/
get isClipboardLibrarySupported() {
return ClipboardJS.isSupported();
}
// =actions
/**
* Initialize ClipboardJS library and register for it's events.
*/
@action
register() {
this.tearDown();
/* istanbul ignore next */
this.clipboard = new ClipboardJS(`#${this.copyableButtonId}`, {
text: () => this.args.text,
});
/* istanbul ignore next */
this.clipboard.on('success', (clipboardEvent) => {
clipboardEvent.clearSelection();
this.confirmCopy();
});
}
/**
* Destroy ClipboardJS library instance and cancel icon timer.
*/
@action
tearDown() {
this.clipboard?.destroy();
this.clipboard = null;
}
}
@@ -30,7 +30,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'delete' @model)}}
{{on 'click' @delete}}
>
{{t 'resources.credential-store.actions.delete'}}
</dropdown.button>
@@ -8,10 +8,4 @@
<DocLink @doc='credential-store' @iconSize='large' />
</h2>
<p>{{t 'resources.credential-store.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -10,58 +10,48 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
{{#unless @model.isNew}}
<form.input
@value={{@model.issuer}}
@label={{t 'form.issuer.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@value={{@model.subject}}
@label={{t 'form.subject.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@value={{@model.email}}
@label={{t 'form.email.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@value={{@model.full_name}}
@label={{t 'form.full_name.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.issuer}} readonly={{true}} as |F|>
<F.Label>{{t 'form.issuer.label'}}</F.Label>
</InfoField>
<InfoField @value={{@model.subject}} readonly={{true}} as |F|>
<F.Label>{{t 'form.subject.label'}}</F.Label>
</InfoField>
<InfoField @value={{@model.email}} readonly={{true}} as |F|>
<F.Label>{{t 'form.email.label'}}</F.Label>
</InfoField>
<InfoField @value={{@model.full_name}} readonly={{true}} as |F|>
<F.Label>{{t 'form.full_name.label'}}</F.Label>
</InfoField>
{{/unless}}
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
@@ -83,41 +73,41 @@
</Hds::Form::Textarea::Field>
{{#if @model.isNew}}
<form.input
@name='issuer'
@type='text'
<Hds::Form::TextInput::Field
name='issuer'
@value={{@model.issuer}}
@label={{t 'form.issuer.label'}}
@error={{@model.errors.issuer}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.issuer}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'issuer')}}
as |F|
>
<F.Label>{{t 'form.issuer.label'}}</F.Label>
{{#if @model.errors.issuer}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.issuer as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<form.input
@name='subject'
@type='text'
<Hds::Form::TextInput::Field
name='subject'
@value={{@model.subject}}
@label={{t 'form.subject.label'}}
@error={{@model.errors.subject}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.subject}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'subject')}}
as |F|
>
<F.Label>{{t 'form.subject.label'}}</F.Label>
{{#if @model.errors.subject}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.subject as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#if (can 'save model' @model)}}
@@ -10,37 +10,35 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -55,43 +53,45 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@name='login_name'
@type='text'
<Hds::Form::TextInput::Field
name='login_name'
@value={{@model.login_name}}
@label={{t 'form.login_name.label'}}
@error={{@model.errors.login_name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.login_name}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'login_name')}}
as |F|
>
<F.Label>{{t 'form.login_name.label'}}</F.Label>
{{#if @model.errors.login_name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.login_name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if @model.isNew}}
<form.input
@name='password'
<Hds::Form::TextInput::Field
name='password'
@type='password'
@value={{this.password}}
@label={{t 'form.password.label'}}
@error={{@model.errors.password}}
readonly={{false}}
@value={{@model.password}}
@isInvalid={{@model.errors.password}}
disabled={{form.disabled}}
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}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.password as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#if (can 'save model' @model)}}
@@ -12,37 +12,35 @@
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} disabled={{form.disabled}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<form.input
@name='name'
@type='text'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -57,24 +55,25 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@name='issuer'
@type='text'
<Hds::Form::TextInput::Field
@value={{@model.issuer}}
@label={{t 'form.issuer.label'}}
@helperText={{t 'form.issuer.help'}}
@error={{@model.errors.issuer}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.issuer}}
@isOptional={{true}}
disabled={{form.disabled}}
name='issuer'
{{on 'input' (set-from-event @model 'issuer')}}
as |F|
>
<F.Label>{{t 'form.issuer.label'}}</F.Label>
<F.HelperText>{{t 'form.issuer.help'}}</F.HelperText>
{{#if @model.errors.issuer}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.issuer as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Checkbox::Field
name='disable_discovered_config_validation'
@@ -95,164 +94,86 @@
</F.HelperText>
</Hds::Form::Checkbox::Field>
<form.input
@name='client_id'
@type='text'
<Hds::Form::TextInput::Field
name='client_id'
@value={{@model.client_id}}
@label={{t 'form.client_id.label'}}
@helperText={{t 'form.client_id.help'}}
@error={{@model.errors.client_id}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.client_id}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'client_id')}}
as |F|
>
<F.Label>{{t 'form.client_id.label'}}</F.Label>
<F.HelperText>{{t 'form.client_id.help'}}</F.HelperText>
{{#if @model.errors.client_id}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.client_id as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<form.input
@name='client_secret'
@type='password'
<Hds::Form::TextInput::Field
name='client_secret'
@value={{@model.client_secret}}
@label={{t 'form.client_secret.label'}}
@helperText={{t 'form.client_secret.help'}}
@error={{@model.errors.client_secret}}
readonly={{false}}
@isInvalid={{@model.errors.client_secret}}
@isOptional={{true}}
disabled={{form.disabled}}
@hasVisibilityToggle={{false}}
as |field|
@type='password'
{{on 'input' (set-from-event @model 'client_secret')}}
as |F|
>
<F.Label>{{t 'form.client_secret.label'}}</F.Label>
<F.HelperText>{{t 'form.client_secret.help'}}</F.HelperText>
{{#if @model.errors.client_secret}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.client_secret as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#unless @model.isNew}}
<form.input
<Hds::Form::TextInput::Field
@value={{@model.client_secret_hmac}}
@label={{t 'form.client_secret_hmac.label'}}
readonly={{true}}
@disabled={{true}}
/>
disabled={{true}}
as |F|
>
<F.Label>{{t 'form.client_secret_hmac.label'}}</F.Label>
</Hds::Form::TextInput::Field>
{{/unless}}
{{! Signing Algorithms }}
<Rose::Form::Fieldset>
<:title>{{t 'form.signing_algorithms.label'}}</:title>
<:description>{{t 'form.signing_algorithms.help'}}</:description>
<:body>
<Form::Field::ListWrapper @layout='horizontal' @disabled={{form.disabled}}>
<:fieldset as |F|>
<F.Legend>
{{t 'form.signing_algorithms.label'}}
</F.Legend>
<F.HelperText>
{{t 'form.signing_algorithms.help'}}
</F.HelperText>
{{#if @model.errors.signing_algorithms}}
<Rose::Form::Errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.signing_algorithms as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</Rose::Form::Errors>
</F.Error>
{{/if}}
<Rose::Table @style='condensed' as |table|>
<table.header as |header|>
<header.row @hidden={{true}} as |row|>
<row.headerCell>{{t 'titles.value'}}</row.headerCell>
<row.headerCell>{{t 'titles.actions'}}</row.headerCell>
</header.row>
</table.header>
<table.body as |body|>
{{#each @model.signing_algorithms as |algorithm i|}}
<body.row as |row|>
<row.cell>
</:fieldset>
<Hds::Form::Select::Field
@value={{algorithm.value}}
@isInvalid={{@model.errors.signing_algorithms}}
disabled={{form.disabled}}
{{on 'change' (set-from-event algorithm 'value')}}
as |F|
>
<F.Label class='sr-only'>{{t
'form.algorithm.label'
}}</F.Label>
<F.Options>
{{#each this.signingAlgorithms as |algorithmOption|}}
<option
value={{algorithmOption}}
selected={{eq algorithmOption algorithm.value}}
>{{algorithmOption}}</option>
{{/each}}
</F.Options>
</Hds::Form::Select::Field>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='warning'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on 'click' (fn @removeItemByIndex 'signing_algorithms' i)}}
>
{{t 'actions.remove'}}
</Rose::Button>
</row.cell>
</body.row>
{{/each}}
<body.row as |row|>
<row.cell>
<Hds::Form::Select::Field
@value={{this.newSigningAlgorithm}}
disabled={{form.disabled}}
{{on 'change' (set-from-event this 'newSigningAlgorithm')}}
as |F|
>
<F.Label class='sr-only'>{{t
'resources.auth-method.titles.new-signing-algorithm'
}}</F.Label>
<F.Options>
{{#each this.signingAlgorithms as |algorithmOption|}}
<option
value={{algorithmOption}}
selected={{eq algorithmOption this.newSigningAlgorithm}}
>{{algorithmOption}}</option>
{{/each}}
</F.Options>
</Hds::Form::Select::Field>
</row.cell>
<row.cell @shrink={{true}}>
<Rose::Button
@style='secondary'
title={{t 'actions.add'}}
@disabled={{if
@model.isNew
false
(if form.isEditable false true)
}}
{{on
'click'
(fn
@addStringItem 'signing_algorithms' this.newSigningAlgorithm
)
}}
>
{{t 'actions.add'}}
</Rose::Button>
</row.cell>
</body.row>
</table.body>
</Rose::Table>
</:body>
</Rose::Form::Fieldset>
<:field as |F|>
<F.Select
@name='signing_algorithms'
@model={{@model}}
@selectOptions={{this.signingAlgorithms}}
@width='100%'
/>
</:field>
</Form::Field::ListWrapper>
{{! Allowed Audiences }}
<Form::Field::ListWrapper
@@ -488,45 +409,43 @@
</Form::Field::ListWrapper>
<form.input
@name='max_age'
@type='number'
<Hds::Form::TextInput::Field
name='max_age'
@value={{@model.max_age}}
@label={{t 'form.max_age.label'}}
@helperText={{t 'form.max_age.help'}}
@error={{@model.errors.max_age}}
readonly={{false}}
step='1'
min='0'
as |field|
@isInvalid={{@model.errors.max_age}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'max_age')}}
as |F|
>
<F.Label>{{t 'form.max_age.label'}}</F.Label>
<F.HelperText>{{t 'form.max_age.help'}}</F.HelperText>
{{#if @model.errors.max_age}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.max_age as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<form.input
@name='api_url_prefix'
@type='test'
<Hds::Form::TextInput::Field
name='api_url_prefix'
@value={{@model.api_url_prefix}}
@label={{t 'form.api_url_prefix.label'}}
@helperText={{t 'form.api_url_prefix.help'}}
@error={{@model.errors.api_url_prefix}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.api_url_prefix}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'api_url_prefix')}}
as |F|
>
<F.Label>{{t 'form.api_url_prefix.label'}}</F.Label>
<F.HelperText>{{t 'form.api_url_prefix.help'}}</F.HelperText>
{{#if @model.errors.api_url_prefix}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.api_url_prefix as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Fieldset @isOptional={{true}} as |F|>
<F.Legend>{{t 'form.prompts.label'}}</F.Legend>
@@ -581,13 +500,14 @@
</Hds::Form::Fieldset>
{{#unless @model.isNew}}
<form.input
<Hds::Form::TextInput::Field
@value={{@model.callback_url}}
@label={{t 'form.callback_url.label'}}
@helperText={{t 'form.callback_url.help'}}
readonly={{true}}
@disabled={{true}}
/>
disabled={{true}}
as |F|
>
<F.Label>{{t 'form.callback_url.label'}}</F.Label>
<F.HelperText>{{t 'form.callback_url.help'}}</F.HelperText>
</Hds::Form::TextInput::Field>
{{/unless}}
{{#if (can 'save model' @model)}}
@@ -10,49 +10,40 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} disabled={{form.disabled}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
name='description'
@isOptional={{true}}
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
disabled={{form.disabled}}
as |F|
>
<F.Label>{{t 'form.description.label'}}</F.Label>
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
{{#if @model.errors.description}}
<F.Error as |E|>
{{#each @model.errors.description as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Textarea::Field>
{{#if (can 'save model' @model)}}
@@ -108,6 +108,78 @@
{{/if}}
</Hds::Form::TextInput::Field>
<Hds::Form::Select::Field
@isOptional={{true}}
@width='100%'
name='credential_type'
disabled={{or form.disabled (not @model.isNew)}}
{{on 'input' this.selectCredentialType}}
as |F|
>
<F.Label>
{{t 'resources.credential-library.form.credential_type.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.credential-library.form.credential_type.help'}}
</F.HelperText>
<F.Options>
<option value='' />
{{#each this.credentialTypes as |type|}}
<option value={{type}} selected={{eq type @model.credential_type}}>
{{t (concat 'resources.credential-library.titles.' type)}}
</option>
{{/each}}
</F.Options>
{{#if @model.errors.credential_type}}
<F.Error as |E|>
{{#each @model.errors.credential_type as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Select::Field>
{{#if @model.credential_type}}
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
@disabled={{form.disabled}}
>
<:fieldset as |F|>
<F.Legend>
{{t
'resources.credential-library.form.credential_mapping_overrides.label'
}}
</F.Legend>
<F.HelperText>
{{t
'resources.credential-library.form.credential_mapping_overrides.help'
}}
</F.HelperText>
{{#if @model.errors.credential_mapping_overrides}}
<F.Error as |E|>
{{#each @model.errors.credential_mapping_overrides as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</:fieldset>
<:field as |F|>
<F.SelectText
@name='credential_mapping_overrides'
@selectOptions={{this.mappingOverrides}}
@model={{@model}}
@disabled={{form.disabled}}
@removeDuplicates={{true}}
@width='400px'
/>
</:field>
</Form::Field::ListWrapper>
{{/if}}
<Hds::Form::Radio::Group
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'http_method')}}
@@ -8,7 +8,7 @@ import {
options,
TYPES_CREDENTIAL_LIBRARY,
} from 'api/models/credential-library';
import { action } from '@ember/object';
export default class FormCredentialLibraryVaultGenericComponent extends Component {
// =properties
/**
@@ -16,6 +16,7 @@ export default class FormCredentialLibraryVaultGenericComponent extends Componen
*/
httpMethodOptions = options.http_method;
credentialTypes = options.credential_types;
/**
*
* @type {Array.<string>}
@@ -29,4 +30,17 @@ export default class FormCredentialLibraryVaultGenericComponent extends Componen
get isHttpRequestBodyAllowed() {
return this.args.model.http_method?.match(/post/i);
}
get mappingOverrides() {
return options.mapping_overrides[this.args.model.credential_type];
}
/**
* Clear the previously selected key value pair when toggling between credential types on a new form
*/
@action
selectCredentialType({ target: { value } }) {
this.args.model.credential_mapping_overrides = {};
this.args.model.credential_type = value;
}
}
@@ -10,30 +10,33 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -11,30 +11,33 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -76,6 +79,7 @@
<InfoField
@value={{t (concat 'resources.credential-store.types.' @model.type)}}
@icon='vault'
readonly={{true}}
as |F|
>
<F.Label>{{t 'form.type.label'}}</F.Label>
@@ -85,119 +89,135 @@
</InfoField>
{{/if}}
{{/if}}
<form.input
@name='address'
@type='text'
<Hds::Form::TextInput::Field
name='address'
@value={{@model.address}}
@label={{t 'resources.credential-store.form.address.label'}}
@error={{@model.errors.address}}
@helperText={{t 'resources.credential-store.form.address.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.address}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'address')}}
as |F|
>
<F.Label>{{t 'resources.credential-store.form.address.label'}}</F.Label>
<F.HelperText>
{{t 'resources.credential-store.form.address.help'}}
</F.HelperText>
{{#if @model.errors.address}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.address as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (feature-flag 'vault-worker-filter')}}
<form.input
@name='worker_filter'
@type='text'
<Hds::Form::TextInput::Field
name='worker_filter'
@value={{@model.worker_filter}}
@label={{t 'resources.credential-store.form.worker_filter.label'}}
@error={{@model.errors.worker_filter}}
@helperText={{t 'resources.credential-store.form.worker_filter.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'worker-filters'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.worker_filter}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'worker_filter')}}
as |F|
>
<F.Label>
{{t 'resources.credential-store.form.worker_filter.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.credential-store.form.worker_filter.help'}}
<Hds::Link::Inline @href={{doc-url 'worker-filters'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
{{#if @model.errors.worker_filter}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.worker_filter as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#if (or @model.isNew form.isEditable)}}
<form.input
@name='token'
@type='text'
<Hds::Form::TextInput::Field
name='token'
@value={{@model.token}}
@label={{t 'resources.credential-store.form.token.label'}}
@error={{@model.errors.token}}
@helperText={{t 'resources.credential-store.form.token.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.token}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'token')}}
as |F|
>
<F.Label>{{t 'resources.credential-store.form.token.label'}}</F.Label>
<F.HelperText>
{{t 'resources.credential-store.form.token.help'}}
</F.HelperText>
{{#if @model.errors.token}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.token as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#unless @model.isNew}}
<form.input
<Hds::Form::TextInput::Field
@value={{@model.token_hmac}}
@label={{t 'resources.credential-store.form.token_hmac.label'}}
readonly={{true}}
@disabled={{true}}
/>
disabled={{true}}
as |F|
>
<F.Label>
{{t 'resources.credential-store.form.token_hmac.label'}}
</F.Label>
</Hds::Form::TextInput::Field>
{{/unless}}
<form.input
@name='namespace'
@type='text'
<Hds::Form::TextInput::Field
name='namespace'
@value={{@model.namespace}}
@label={{t 'resources.credential-store.form.namespace.label'}}
@error={{@model.errors.namespace}}
@helperText={{t 'resources.credential-store.form.namespace.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.namespace}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'namespace')}}
as |F|
>
<F.Label>{{t 'resources.credential-store.form.namespace.label'}}</F.Label>
<F.HelperText>
{{t 'resources.credential-store.form.namespace.help'}}
</F.HelperText>
{{#if @model.errors.namespace}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.namespace as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<form.input
@name='tls_server_name'
@type='text'
<Hds::Form::TextInput::Field
name='tls_server_name'
@value={{@model.tls_server_name}}
@label={{t 'resources.credential-store.form.tls_server_name.label'}}
@error={{@model.errors.tls_server_name}}
@helperText={{t 'resources.credential-store.form.tls_server_name.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.tls_server_name}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'tls_server_name')}}
as |F|
>
<F.Label>
{{t 'resources.credential-store.form.tls_server_name.label'}}
</F.Label>
<F.HelperText>
{{t 'resources.credential-store.form.tls_server_name.help'}}
</F.HelperText>
{{#if @model.errors.tls_server_name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.tls_server_name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Checkbox::Field
name='tls_skip_verify'
@@ -225,10 +245,10 @@
</Hds::Form::Checkbox::Field>
<Hds::Form::Textarea::Field
name='client_certificate'
@value={{@model.client_certificate}}
@isInvalid={{@model.errors.client_certificate}}
@isOptional={{true}}
name='client_certificate'
disabled={{form.disabled}}
as |F|
>
@@ -248,10 +268,10 @@
</Hds::Form::Textarea::Field>
<Hds::Form::Textarea::Field
name='client_certificate_key'
@value={{@model.client_certificate_key}}
@isOptional={{true}}
@isInvalid={{@model.errors.client_certificate_key}}
name='client_certificate_key'
disabled={{form.disabled}}
as |F|
>
@@ -271,21 +291,24 @@
</Hds::Form::Textarea::Field>
{{#unless @model.isNew}}
<form.input
<Hds::Form::TextInput::Field
@value={{@model.client_certificate_key_hmac}}
@label={{t
'resources.credential-store.form.client_certificate_key_hmac.label'
}}
readonly={{true}}
@disabled={{true}}
/>
disabled={{true}}
as |F|
>
<F.Label>
{{t
'resources.credential-store.form.client_certificate_key_hmac.label'
}}
</F.Label>
</Hds::Form::TextInput::Field>
{{/unless}}
<Hds::Form::Textarea::Field
name='ca_cert'
@value={{@model.ca_cert}}
@isOptional={{true}}
@isInvalid={{@model.errors.ca_cert}}
@name='ca_cert'
disabled={{form.disabled}}
as |F|
>
@@ -11,28 +11,33 @@
as |form|
>
<form.input
@name='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@error={{@model.errors.name}}
@helperText={{t 'form.name.help'}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -48,42 +53,38 @@
</Hds::Form::Textarea::Field>
{{#if @model.isNew}}
<form.fieldset>
<:body>
<Hds::Form::RadioCard::Group as |G|>
<G.Legend>{{t 'form.type.label'}}</G.Legend>
{{#each @types as |credentialType|}}
<G.RadioCard
@name={{credentialType}}
@value={{credentialType}}
@checked={{eq credentialType @model.type}}
@maxWidth='20rem'
{{on 'input' (fn @changeType credentialType)}}
as |R|
>
<R.Label>{{t
(concat 'resources.credential.types.' credentialType)
}}</R.Label>
<R.Description>{{t
(concat 'resources.credential.help.' credentialType)
}}</R.Description>
</G.RadioCard>
{{/each}}
</Hds::Form::RadioCard::Group>
</:body>
</form.fieldset>
<Hds::Form::RadioCard::Group as |G|>
<G.Legend>{{t 'form.type.label'}}</G.Legend>
{{#each @types as |credentialType|}}
<G.RadioCard
@name={{credentialType}}
@value={{credentialType}}
@checked={{eq credentialType @model.type}}
@maxWidth='20rem'
{{on 'input' (fn @changeType credentialType)}}
as |R|
>
<R.Label>{{t (concat 'resources.credential.types.' credentialType)}}
</R.Label>
<R.Description>{{t
(concat 'resources.credential.help.' credentialType)
}}
</R.Description>
</G.RadioCard>
{{/each}}
</Hds::Form::RadioCard::Group>
<form.fieldset>
<:title>{{t 'resources.credential.form.json.label'}}</:title>
<:description>{{t 'resources.credential.form.json.help'}}</:description>
<:body>
<Hds::Form::Fieldset as |F|>
<F.Legend>{{t 'resources.credential.form.json.label'}}</F.Legend>
<F.HelperText>{{t 'resources.credential.form.json.help'}}</F.HelperText>
<F.Control>
<Form::Field::JsonSecret
@value={{@model.json_object}}
@onInput={{fn (mut @model.json_object)}}
@showEditButton={{false}}
/>
</:body>
</form.fieldset>
</F.Control>
</Hds::Form::Fieldset>
{{else}}
<InfoField
@value={{t (concat 'resources.credential.types.' @model.type)}}
@@ -94,18 +95,18 @@
</F.HelperText>
</InfoField>
<form.fieldset>
<:title>{{t 'resources.credential.form.json.label'}}</:title>
<:description>{{t 'resources.credential.form.json.help'}}</:description>
<:body>
<Hds::Form::Fieldset as |F|>
<F.Legend>{{t 'resources.credential.form.json.label'}}</F.Legend>
<F.HelperText>{{t 'resources.credential.form.json.help'}}</F.HelperText>
<F.Control>
<Form::Field::JsonSecret
@disabled={{form.disabled}}
@value={{@model.json_object}}
@onInput={{fn (mut @model.json_object)}}
@showEditButton={{true}}
/>
</:body>
</form.fieldset>
</F.Control>
</Hds::Form::Fieldset>
{{/if}}
{{#if (can 'save model' @model)}}
@@ -11,28 +11,33 @@
as |form|
>
<form.input
@name='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@error={{@model.errors.name}}
@helperText={{t 'form.name.help'}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -71,6 +76,7 @@
{{else}}
<InfoField
@value={{t (concat 'resources.credential.types.' @model.type)}}
readonly={{true}}
as |F|
>
<F.Label>{{t 'form.type.label'}}</F.Label>
@@ -79,29 +85,33 @@
</InfoField>
{{/if}}
<form.input
@name='username'
<Hds::Form::TextInput::Field
name='username'
@value={{@model.username}}
@label={{t 'resources.credential.form.username.label'}}
@helperText={{t 'resources.credential.form.username.help'}}
@error={{@model.errors.username}}
as |field|
@isInvalid={{@model.errors.username}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'username')}}
as |F|
>
<F.Label>{{t 'resources.credential.form.username.label'}}</F.Label>
<F.HelperText>{{t 'resources.credential.form.username.help'}}</F.HelperText>
{{#if @model.errors.username}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.username as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-username>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (or @model.isNew form.isEditable)}}
<Hds::Form::Textarea::Field
@isOptional={{false}}
name='private_key'
@value={{@model.private_key}}
@isInvalid={{@model.errors.private_key}}
name='private_key'
@isRequired={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -112,30 +122,36 @@
{{#if @model.errors.private_key}}
<F.Error as |E|>
{{#each @model.errors.private_key as |error|}}
<E.Message>{{error.message}}</E.Message>
<E.Message data-test-error-message-private-key>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
<Hds::Form::TextInput::Field
name='private_key_passphrase'
@type='password'
@name='private_key_passphrase'
@value={{@model.private_key_passphrase}}
@label={{t 'resources.credential.form.private_key_passphrase.label'}}
@helperText={{t 'resources.credential.form.private_key_passphrase.help'}}
@error={{@model.errors.private_key_passphrase}}
@hasVisibilityToggle={{false}}
@isInvalid={{@model.errors.private_key_passphrase}}
@isOptional={{true}}
disabled={{form.disabled}}
autocomplete='new-password'
as |field|
{{on 'input' (set-from-event @model 'private_key_passphrase')}}
as |F|
>
<F.Label>
{{t 'resources.credential.form.private_key_passphrase.label'}}
</F.Label>
{{#if @model.errors.private_key_passphrase}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.private_key_passphrase as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#if (can 'save model' @model)}}
@@ -11,27 +11,33 @@
as |form|
>
<form.input
@name='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@error={{@model.errors.name}}
@helperText={{t 'form.name.help'}}
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<Hds::Form::Textarea::Field
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<E.Message data-test-error-message-name>
{{error.message}}
</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -70,6 +76,7 @@
{{else}}
<InfoField
@value={{t (concat 'resources.credential.types.' @model.type)}}
readonly={{true}}
as |F|
>
<F.Label>{{t 'form.type.label'}}</F.Label>
@@ -78,42 +85,50 @@
</InfoField>
{{/if}}
<form.input
@name='username'
<Hds::Form::TextInput::Field
name='username'
@value={{@model.username}}
@label={{t 'resources.credential.form.username.label'}}
@helperText={{t 'resources.credential.form.username.help'}}
@error={{@model.errors.username}}
as |field|
@isInvalid={{@model.errors.username}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'username')}}
as |F|
>
<F.Label>{{t 'resources.credential.form.username.label'}}</F.Label>
<F.HelperText>{{t 'resources.credential.form.username.help'}}</F.HelperText>
{{#if @model.errors.username}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.username as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (or @model.isNew form.isEditable)}}
<form.input
@name='password'
<Hds::Form::TextInput::Field
name='password'
@type='password'
@value={{@model.password}}
@label={{t 'resources.credential.form.password.label'}}
@helperText={{t 'resources.credential.form.password.help'}}
@error={{@model.errors.password}}
@isInvalid={{@model.errors.password}}
disabled={{form.disabled}}
autocomplete='new-password'
as |field|
{{on 'input' (set-from-event @model 'password')}}
as |F|
>
<F.Label>{{t 'resources.credential.form.password.label'}}</F.Label>
<F.HelperText>{{t
'resources.credential.form.password.help'
}}</F.HelperText>
{{#if @model.errors.password}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.password as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-password>
{{error.message}}
</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{/if}}
{{#if (can 'save model' @model)}}
@@ -24,6 +24,10 @@
Textarea=(component
'form/field/list-wrapper/textarea' disabled=@disabled
)
Select=(component 'form/field/list-wrapper/select' disabled=@disabled)
SelectText=(component
'form/field/list-wrapper/select-text' disabled=@disabled
)
)
to='field'
}}
@@ -0,0 +1,116 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
{{#let (unique-id) (unique-id) as |keyHeaderID valueHeaderID|}}
<Hds::Table class='list-wrapper-field' name={{@name}}>
<:head as |H|>
<H.Tr>
<H.Th id={{keyHeaderID}}>{{t 'form.key.label'}}</H.Th>
<H.Th id={{valueHeaderID}}>{{t 'form.value.label'}}</H.Th>
<H.Th>
{{t 'titles.actions'}}
</H.Th>
</H.Tr>
</:head>
<:body as |B|>
{{#each-in this.options as |select val|}}
<B.Tr>
<B.Td>
<Hds::Form::Select::Base
@value={{select}}
disabled={{@disabled}}
@width={{@width}}
aria-labelledby={{keyHeaderID}}
{{on 'change' (fn this.selectChange select val)}}
as |F|
>
<F.Options>
{{#each-in @selectOptions as |key value|}}
<option value={{key}} selected={{eq select key}}>
{{value}}
</option>
{{/each-in}}
</F.Options>
</Hds::Form::Select::Base>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{val}}
@type='text'
@width={{@width}}
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'change' (fn this.updateInput select)}}
/>
</B.Td>
<B.Td>
{{#if this.removeOptionByKey}}
<Hds::Button
data-test-remove-button={{select}}
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn this.removeOptionByKey select)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each-in}}
{{#if this.showNewRow}}
<B.Tr>
<B.Td>
<Hds::Form::Select::Base
@value={{this.newOptionKey}}
disabled={{@disabled}}
aria-labelledby={{keyHeaderID}}
@width={{@width}}
{{on 'change' (set-from-event this 'newOptionKey')}}
as |F|
>
<F.Options>
<option hidden selected value=''>
{{t 'titles.choose-an-option'}}
</option>
{{#each-in this.selectOptions as |key value|}}
<option value={{key}} selected={{eq key this.newOptionKey}}>
{{value}}
</option>
{{/each-in}}
</F.Options>
</Hds::Form::Select::Base>
</B.Td>
<B.Td>
<Hds::Form::TextInput::Field
@value={{this.newOptionValue}}
@type='text'
@width={{@width}}
disabled={{@disabled}}
aria-labelledby={{valueHeaderID}}
{{on 'input' (set-from-event this 'newOptionValue')}}
/>
</B.Td>
<B.Td>
<Hds::Button
data-test-select-text-add-btn
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionKey)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
{{/if}}
</:body>
</Hds::Table>
{{/let}}
@@ -0,0 +1,137 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { set } from '@ember/object';
export default class MappingListComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionKey = '';
/**
* @type {string}
*/
@tracked newOptionValue = '';
/**
* Returns an object of key/value pair that the user enters
* @type {object}
*/
get options() {
return this.args?.options || this.args?.model?.[this.args.name];
}
/**
* Determines if we need to show an empty row to the users to enter more key/value pairs based on removeDuplicates arg,
* by default it is true
* @type {object}
*/
get showNewRow() {
if (this.args.removeDuplicates) {
return (
Object.keys(this.options || {}).length !==
Object.keys(this.args.selectOptions || {}).length
);
} else {
return true;
}
}
/**
* Prevents users from selecting duplicate keys from the select list if there's the arg is set to true
* @type {object}
*/
get selectOptions() {
const previouslySelectedKeys = Object.keys(this.options || {});
if (this.args.removeDuplicates && previouslySelectedKeys.length) {
const newObj = { ...this.args.selectOptions };
for (const key of Object.keys(newObj)) {
if (previouslySelectedKeys.includes(key)) {
delete newObj[key];
}
}
return newObj;
} else {
return this.args.selectOptions;
}
}
// =actions
/**
* Handles when then user makes changes to the select list
* @param oldkey {string}
* @param oldVal {string}
*/
@action
selectChange(oldkey, oldVal, { target: { value: newKey } }) {
const field = this.args.name;
const newObj = { ...this.args.model[field], [newKey]: oldVal };
delete newObj[oldkey];
set(this.args.model, field, newObj);
}
/**
* Handles input changes
* @param key {string}
*/
@action
updateInput(key, { target: { value: newValue } }) {
const field = this.args.name;
this.args.model[field] = {
...this.args.model[field],
[key]: newValue,
};
}
/**
* If a new key value is entered and an addOption method was specified,
* calls addOption with the new key and value. Resets key and value.
* Otherwise use the model argument to create the array and update the model
*/
@action
addOption() {
if (this.args.addOption) {
if (this.newOptionKey) {
this.args.addOption({
key: this.newOptionKey,
value: this.newOptionValue,
});
}
} else {
const field = this.args.name;
this.args.model[field] = {
...this.args.model[field],
[this.newOptionKey]: this.newOptionValue,
};
}
this.newOptionKey = '';
this.newOptionValue = '';
}
/**
* If removeOptionByIndex method was passed, use that.
* Otherwise, use the model to remove an option by key
* @param index {number}
*/
@action
removeOptionByKey(selectedKey) {
if (this.args.removeOptionByKey) {
this.args.removeOptionByKey(selectedKey);
} else {
const field = this.args.name;
const newObj = { ...this.args.model[field] };
delete newObj[selectedKey];
set(this.args.model, field, newObj);
}
}
}
@@ -0,0 +1,90 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<Hds::Table class='list-wrapper-field' name={{@name}}>
<:body as |B|>
{{#each this.options as |select index|}}
<B.Tr>
<B.Td>
<Hds::Form::Select::Base
@value={{select.value}}
@width={{@width}}
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
{{on 'change' (set-from-event select 'value')}}
as |F|
>
<F.Options>
{{#each @selectOptions as |selectOption|}}
<option
value={{selectOption}}
selected={{eq select.value selectOption}}
>
{{selectOption}}
</option>
{{/each}}
</F.Options>
</Hds::Form::Select::Base>
</B.Td>
<B.Td>
{{#if this.removeOptionByIndex}}
<Hds::Button
data-test-remove-option-button={{select.value}}
@text={{t 'actions.remove'}}
@color='critical'
@icon='trash'
@isIconOnly={{true}}
disabled={{@disabled}}
{{on 'click' (fn this.removeOptionByIndex index)}}
/>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
<B.Tr>
<B.Td>
<Hds::Form::Select::Base
@value={{this.newOptionValue}}
@width={{@width}}
disabled={{@disabled}}
aria-label={{t 'titles.value'}}
{{on 'change' (set-from-event this 'newOptionValue')}}
as |F|
>
<F.Options>
<option disabled={{@disabled}} hidden selected value=''>
{{t 'titles.choose-an-option'}}
</option>
{{#each @selectOptions as |selectOption|}}
<option
value={{selectOption}}
selected={{eq selectOption this.newOptionValue}}
>
{{selectOption}}
</option>
{{/each}}
</F.Options>
</Hds::Form::Select::Base>
</B.Td>
<B.Td>
<Hds::Button
data-test-add-option-button
@text={{t 'actions.add'}}
@color='secondary'
type='button'
disabled={{or @disabled (not this.newOptionValue)}}
{{on 'click' this.addOption}}
/>
</B.Td>
</B.Tr>
</:body>
</Hds::Table>
@@ -0,0 +1,61 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { set } from '@ember/object';
export default class MappingListSelectComponent extends Component {
// =attributes
/**
* @type {string}
*/
@tracked newOptionValue = '';
get options() {
return this.args.options || this.args.model[this.args.name];
}
/**
* If a new input is entered and an addOption method was specified,
* calls addOption with the new input. Resets previous value.
* Otherwise use the model argument to create the array and update the model
*/
@action
addOption() {
if (this.args.addOption && this.newOptionValue) {
this.args.addOption({
value: this.newOptionValue,
});
} else {
const field = this.args.name;
const existingArray = this.args.model[field] ?? [];
const newArray = [...existingArray, { value: this.newOptionValue }];
set(this.args.model, field, newArray);
}
this.newOptionValue = '';
}
/**
* If removeOptionByIndex method was passed, use that.
* Otherwise, use the model to remove an option by index. We recreate a new array after
* splicing out the item so that ember is aware that the array has been modified.
* @param index {number}
*/
@action
removeOptionByIndex(index) {
if (this.args.removeOptionByIndex) {
this.args.removeOptionByIndex(index);
} else {
const field = this.args.name;
const newArray = this.args.model[field].filter((_, i) => i !== index);
set(this.args.model, field, newArray);
}
}
}
+15 -14
View File
@@ -11,30 +11,31 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -10,30 +10,31 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -94,7 +95,7 @@
<InfoField
@value={{t 'resources.host-catalog.types.aws'}}
@icon='aws-color'
disabled={{form.disabled}}
readonly={{true}}
as |F|
>
<F.Label>{{t 'titles.provider'}}</F.Label>
@@ -103,38 +104,53 @@
</InfoField>
{{/if}}
<form.input
@name='region'
@type='region'
<Hds::Form::TextInput::Field
name='region'
@value={{@model.region}}
@label={{t 'resources.host-catalog.form.aws_region.label'}}
@helperText={{t 'resources.host-catalog.form.aws_region.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.aws.region'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'region')}}
as |F|
>
<F.Label>{{t 'resources.host-catalog.form.aws_region.label'}}</F.Label>
<F.HelperText>
{{t 'resources.host-catalog.form.aws_region.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws.region'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='access_key_id'
@type='access_key_id'
<Hds::Form::TextInput::Field
name='access_key_id'
@value={{@model.access_key_id}}
@label={{t 'form.access_key_id.label'}}
@helperText={{t 'form.access_key_id.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.aws'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'access_key_id')}}
as |F|
>
<F.Label>{{t 'form.access_key_id.label'}}</F.Label>
<F.HelperText>
{{t 'form.access_key_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='secret_access_key'
@type='secret_access_key'
<Hds::Form::TextInput::Field
name='secret_access_key'
@value={{@model.secret_access_key}}
@label={{t 'form.secret_access_key.label'}}
@helperText={{t 'form.secret_access_key.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.aws'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'secret_access_key')}}
as |F|
>
<F.Label>{{t 'form.secret_access_key.label'}}</F.Label>
<F.HelperText>
{{t 'form.secret_access_key.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.aws'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<Hds::Form::Checkbox::Field
name='disable_credential_rotation'
@@ -10,30 +10,31 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -94,7 +95,7 @@
<InfoField
@value={{t 'resources.host-catalog.types.azure'}}
@icon='azure-color'
disabled={{form.disabled}}
readonly={{true}}
as |F|
>
<F.Label>{{t 'titles.provider'}}</F.Label>
@@ -103,49 +104,69 @@
</InfoField>
{{/if}}
<form.input
@name='tenant_id'
@type='tenant_id'
<Hds::Form::TextInput::Field
name='tenant_id'
@value={{@model.tenant_id}}
@label={{t 'form.tenant_id.label'}}
@helperText={{t 'form.tenant_id.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.azure'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'tenant_id')}}
as |F|
>
<F.Label>{{t 'form.tenant_id.label'}}</F.Label>
<F.HelperText>
{{t 'form.tenant_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.azure'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='subscription_id'
@type='subscription_id'
<Hds::Form::TextInput::Field
name='subscription_id'
@value={{@model.subscription_id}}
@label={{t 'form.subscription_id.label'}}
@helperText={{t 'form.subscription_id.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.azure'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'subscription_id')}}
as |F|
>
<F.Label>{{t 'form.subscription_id.label'}}</F.Label>
<F.HelperText>
{{t 'form.subscription_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.azure'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='client_id'
@type='client_id'
<Hds::Form::TextInput::Field
name='client_id'
@value={{@model.client_id}}
@label={{t 'resources.host-catalog.form.azure_client_id.label'}}
@helperText={{t 'resources.host-catalog.form.azure_client_id.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.azure'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'client_id')}}
as |F|
>
<F.Label>{{t 'resources.host-catalog.form.azure_client_id.label'}}</F.Label>
<F.HelperText>
{{t 'resources.host-catalog.form.azure_client_id.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.azure'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='secret_value'
@type='secret_value'
<Hds::Form::TextInput::Field
name='secret_value'
@value={{@model.secret_value}}
@label={{t 'form.secret.label'}}
@helperText={{t 'form.secret.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'host-catalog.azure'}}
readonly={{false}}
/>
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'secret_value')}}
as |F|
>
<F.Label>{{t 'form.secret.label'}}</F.Label>
<F.HelperText>
{{t 'form.secret.help'}}
<Hds::Link::Inline @href={{doc-url 'host-catalog.azure'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<Hds::Form::Checkbox::Field
name='disable_credential_rotation'
@@ -10,31 +10,31 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -12,30 +12,31 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isOptional={{true}}
@isInvalid={{@model.errors.name}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@value={{@model.description}}
@isOptional={{true}}
@isInvalid={{@model.errors.description}}
disabled={{form.disabled}}
as |F|
>
@@ -50,12 +51,9 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} disabled={{form.disabled}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<InfoField
@value={{t 'descriptions.provider'}}
@@ -125,27 +123,30 @@
</:field>
</Form::Field::ListWrapper>
<form.input
@name='sync_interval_seconds'
@type='number'
<Hds::Form::TextInput::Field
name='sync_interval_seconds'
@value={{@model.sync_interval_seconds}}
@label={{t 'form.sync-interval.label'}}
@error={{@model.errors.sync_interval_seconds}}
@helperText={{t 'form.sync-interval.help'}}
@link={{doc-url 'host-set.sync-interval-seconds'}}
@linkText={{t 'actions.learn-more'}}
min='0'
step='1'
as |field|
@isInvalid={{@model.errors.sync_interval_seconds}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'sync_interval_seconds')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>
{{t 'form.name.help'}}
<Hds::Link::Inline @href={{doc-url 'host-set.sync-interval-seconds'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
{{#if @model.errors.sync_interval_seconds}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.sync_interval_seconds as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (can 'save model' @model)}}
<form.actions
@enableEditText={{t 'actions.edit-form'}}
@@ -12,30 +12,31 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isOptional={{true}}
@isInvalid={{@model.errors.name}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
name='description'
@isOptional={{true}}
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
disabled={{form.disabled}}
as |F|
>
@@ -50,17 +51,14 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<InfoField
@value={{t 'descriptions.provider'}}
@icon='azure-color'
disabled={{form.disabled}}
readonly={{true}}
as |F|
>
<F.Label>{{t 'titles.provider'}}</F.Label>
@@ -101,10 +99,10 @@
</Form::Field::ListWrapper>
<Hds::Form::Textarea::Field
name='filter'
@value={{@model.filter_string}}
@isOptional={{true}}
@isInvalid={{@model.errors.filter_string}}
name='filter'
disabled={{form.disabled}}
as |F|
>
@@ -121,27 +119,29 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@name='sync_interval_seconds'
@type='number'
<Hds::Form::TextInput::Field
name='sync_interval_seconds'
@value={{@model.sync_interval_seconds}}
@label={{t 'form.sync-interval.label'}}
@error={{@model.errors.sync_interval_seconds}}
@helperText={{t 'form.sync-interval.help'}}
@link={{doc-url 'host-set.sync-interval-seconds'}}
@linkText={{t 'actions.learn-more'}}
min='0'
step='1'
as |field|
@isInvalid={{@model.errors.sync_interval_seconds}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'sync_interval_seconds')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>
{{t 'form.name.help'}}
<Hds::Link::Inline @href={{doc-url 'host-set.sync-interval-seconds'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
{{#if @model.errors.sync_interval_seconds}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.sync_interval_seconds as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (can 'save model' @model)}}
<form.actions
@@ -11,30 +11,31 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isOptional={{true}}
@isInvalid={{@model.errors.name}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@value={{@model.description}}
@isOptional={{true}}
@isInvalid={{@model.errors.description}}
disabled={{form.disabled}}
as |F|
>
@@ -11,37 +11,35 @@
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<Hds::Form::TextInput::Field @value={{@model.type}} disabled={{true}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</Hds::Form::TextInput::Field>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@error={{@model.errors.name}}
@helperText={{t 'form.name.help'}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -56,24 +54,26 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@name='address'
@type='text'
<Hds::Form::TextInput::Field
name='address'
@value={{@model.address}}
@label={{t 'form.address.label'}}
@error={{@model.errors.address}}
@helperText={{t 'form.address.help'}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.address}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'address')}}
as |F|
>
<F.Label>{{t 'form.address.label'}}</F.Label>
<F.HelperText>{{t 'form.address.help'}}</F.HelperText>
{{#if @model.errors.address}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.address as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (can 'save model' @model)}}
<form.actions
@enableEditText={{t 'actions.edit-form'}}
@@ -10,31 +10,29 @@
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<InfoField @value={{@model.type}} readonly={{true}} as |F|>
<F.Label>{{t 'form.type.label'}}</F.Label>
</InfoField>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.error.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
@@ -55,23 +53,25 @@
{{/if}}
</Hds::Form::Textarea::Field>
<form.input
@name='filter'
@type='text'
<Hds::Form::TextInput::Field
name='filter'
@value={{@model.filter_string}}
@label={{t 'form.filter.label'}}
@error={{@model.errors.filter_string}}
@helperText={{t 'form.filter.help'}}
as |field|
@isInvalid={{@model.errors.filter_string}}
@isRequired={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'filter_string')}}
as |F|
>
<F.Label>{{t 'form.filter.label'}}</F.Label>
<F.HelperText>{{t 'form.filter.help'}}</F.HelperText>
{{#if @model.errors.filter_string}}
<field.errors as |errors|>
{{#each @model.errors.filter_string as |error|}}
<errors.message>{{error.message}}</errors.message>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
{{#if (can 'save model' @model)}}
<form.actions
+1 -1
View File
@@ -10,7 +10,7 @@ const RETENTION_POLICY = {
custom: 1,
do_not_protect: 0,
soc: 2555,
hippa: 2190,
hipaa: 2190,
};
const DELETION_POLICY = { do_not_delete: 0, custom: 1 };
+15 -14
View File
@@ -11,30 +11,31 @@
as |form|
>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
@isOptional={{true}}
name='description'
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
@isOptional={{true}}
disabled={{form.disabled}}
as |F|
>
@@ -12,45 +12,75 @@
<h3>{{t 'resources.worker.form.steps.1.title'}}</h3>
<div class='worker-create-section'>
{{#if (feature-flag 'byow-pki-hcp-cluster-id')}}
<form.input
@name='cluster_id'
<Hds::Form::TextInput::Field
name='cluster_id'
@value={{this.clusterID}}
@label={{t 'resources.worker.form.cluster_id.label'}}
@helperText={{t 'resources.worker.form.cluster_id.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'worker.manage-workers'}}
@isOptional={{true}}
placeholder='69b6ddb3-ffec-42ab-a994-f43a6519470a'
/>
{{on 'input' (set-from-event this 'clusterID')}}
as |F|
>
<F.Label>{{t 'resources.worker.form.cluster_id.label'}}</F.Label>
<F.HelperText>
{{t 'resources.worker.form.cluster_id.help'}}
<Hds::Link::Inline @href={{doc-url 'worker.manage-workers'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
{{/if}}
<form.input
@name='ip_address'
<Hds::Form::TextInput::Field
name='ip_address'
@value={{this.ipAddress}}
@label={{t 'resources.worker.form.ip_address.label'}}
@helperText={{t 'resources.worker.form.ip_address.help'}}
@linkText={{t 'actions.learn-more'}}
@link={{doc-url 'worker'}}
@isOptional={{true}}
placeholder='worker1.example.com'
/>
{{on 'input' (set-from-event this 'ipAddress')}}
as |F|
>
<F.Label>{{t 'resources.worker.form.ip_address.label'}}</F.Label>
<F.HelperText>
{{t 'resources.worker.form.ip_address.help'}}
<Hds::Link::Inline @href={{doc-url 'worker'}}>
{{t 'actions.learn-more'}}
</Hds::Link::Inline>
</F.HelperText>
</Hds::Form::TextInput::Field>
<form.input
@name='config_file_path'
<Hds::Form::TextInput::Field
name='config_file_path'
@value={{this.configFilePath}}
@label={{t 'resources.worker.form.config_file_path.label'}}
@helperText={{t 'resources.worker.form.config_file_path.help'}}
@isOptional={{true}}
placeholder='/home/ubuntu/boundary'
/>
{{on 'input' (set-from-event this 'configFilePath')}}
as |F|
>
<F.Label>{{t 'resources.worker.form.config_file_path.label'}}</F.Label>
<F.HelperText>
{{t 'resources.worker.form.config_file_path.help'}}
</F.HelperText>
</Hds::Form::TextInput::Field>
{{#if (not (feature-flag 'byow-pki-hcp-cluster-id'))}}
<form.input
@name='initial_upstreams'
<Hds::Form::TextInput::Field
name='initial_upstreams'
@value={{this.initialUpstreams}}
@label={{t 'resources.worker.form.initial_upstreams.label'}}
@helperText={{t 'resources.worker.form.initial_upstreams.help'}}
@isOptional={{true}}
placeholder='10.0.0.1, 10.0.0.2, 10.0.0.3'
/>
{{on 'input' (set-from-event this 'initialUpstreams')}}
as |F|
>
<F.Label>{{t 'resources.worker.form.initial_upstreams.label'}}</F.Label>
<F.HelperText>
{{t 'resources.worker.form.initial_upstreams.help'}}
</F.HelperText>
</Hds::Form::TextInput::Field>
{{/if}}
<Form::Field::ListWrapper @layout='horizontal' @disabled={{form.disabled}}>
<Form::Field::ListWrapper
@layout='horizontal'
@disabled={{form.disabled}}
@isOptional={{true}}
>
<:fieldset as |F|>
<F.Legend>{{t 'resources.worker.form.worker_tags.label'}}</F.Legend>
<F.HelperText>
@@ -70,7 +100,7 @@
</Form::Field::ListWrapper>
{{#if (feature-flag 'ssh-session-recording')}}
<Hds::Form::Fieldset as |F|>
<Hds::Form::Fieldset @isOptional={{true}} as |F|>
<F.Legend>{{t
'resources.worker.form.local_session_recording_storage.label'
}}</F.Legend>
@@ -94,80 +124,108 @@
</F.Control>
{{#if this.enableRecordingStoragePath}}
<F.Control>
<form.input
@name='recording_storage_path'
<Hds::Form::TextInput::Field
name='recording_storage_path'
@value={{this.recording_storage_path}}
@label={{t 'resources.worker.form.recording_storage_path.label'}}
@helperText={{t
'resources.worker.form.recording_storage_path.help'
}}
placeholder='/tmp/worker1'
/>
as |F|
>
<F.Label>{{t
'resources.worker.form.recording_storage_path.label'
}}</F.Label>
<F.HelperText>
{{t 'resources.worker.form.recording_storage_path.help'}}
</F.HelperText>
</Hds::Form::TextInput::Field>
</F.Control>
{{/if}}
</Hds::Form::Fieldset>
{{/if}}
<Rose::CodeEditor @codeValue={{this.createConfigText}} as |c|>
<c.toolbar>
<:action>
<span>{{t 'resources.worker.form.steps.1.create_directory'}}</span>
</:action>
</c.toolbar>
<c.fieldEditor class='rose-autosize' @options={{this.shellCodeEditor}} />
</Rose::CodeEditor>
<p>
{{t 'resources.worker.form.steps.1.create_directory'}}
</p>
<Rose::CodeEditor @codeValue={{this.workerConfigText}} as |c|>
<c.toolbar>
<:action>
<span>{{t 'resources.worker.form.steps.1.create_config'}}</span>
</:action>
</c.toolbar>
<c.fieldEditor class='rose-autosize' @options={{this.hclCodeEditor}} />
</Rose::CodeEditor>
<p>{{t 'resources.worker.form.steps.1.save_config'}}</p>
<Hds::CodeBlock
@language='bash'
@value={{this.createConfigText}}
@hasCopyButton={{true}}
@hasLineNumbers={{false}}
/>
<p>
{{t 'resources.worker.form.steps.1.create_config'}}
</p>
<Hds::CodeBlock
@language='hcl'
@value={{this.workerConfigText}}
@hasCopyButton={{true}}
@hasLineNumbers={{false}}
/>
<p>
{{t 'resources.worker.form.steps.1.save_config' htmlSafe=true}}
</p>
</div>
<h3>{{t 'resources.worker.form.steps.2.title'}}</h3>
<div class='worker-create-section'>
<Rose::CodeEditor @codeValue={{this.installBoundaryText}} as |c|>
<c.toolbar>
<:action>
<span>{{t 'resources.worker.form.steps.2.run_command'}}</span>
</:action>
</c.toolbar>
<c.fieldEditor class='rose-autosize' @options={{this.shellCodeEditor}} />
</Rose::CodeEditor>
<span>{{t 'resources.worker.form.steps.2.copy_registration_request'}}</span>
<code>{{t
'resources.worker.form.steps.3.worker_auth_registration_request.label'
}}
</code>
<p>
{{t 'resources.worker.form.steps.2.run_command'}}
</p>
<Hds::CodeBlock
@language='bash'
@value={{this.installBoundaryText}}
@hasCopyButton={{true}}
@hasLineNumbers={{false}}
/>
<p>
{{t 'resources.worker.form.steps.2.copy_registration_request'}}
<code>{{t
'resources.worker.form.steps.3.worker_auth_registration_request.label'
}}</code>.
</p>
</div>
<h3>{{t 'resources.worker.form.steps.3.title'}}</h3>
<div class='worker-create-section'>
<div class='worker-auth-token'>
<form.input
@name='worker_auth_registration_request'
<Hds::Form::TextInput::Field
name='worker_auth_registration_request'
@value={{this.generatedWorkerAuthToken}}
@disabled={{@model.cannotSave}}
@label={{t
'resources.worker.form.steps.3.worker_auth_registration_request.label'
}}
@helperText={{t
'resources.worker.form.steps.3.worker_auth_registration_request.help'
}}
/>
@isRequired={{true}}
@isInvalid={{@model.errors.worker_generated_auth_token}}
disabled={{form.disabled}}
{{on 'input' (set-from-event this 'generatedWorkerAuthToken')}}
as |F|
>
<F.Label>{{t
'resources.worker.form.steps.3.worker_auth_registration_request.label'
}}</F.Label>
<F.HelperText>
{{t
'resources.worker.form.steps.3.worker_auth_registration_request.help'
}}
</F.HelperText>
{{#if @model.errors.worker_generated_auth_token}}
<F.Error as |E|>
{{#each @model.errors.worker_generated_auth_token as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
{{#if (can 'save worker' @model)}}
<Rose::Button
@onSubmit={{true}}
@style='primary'
<Hds::Button
@text={{t 'resources.worker.actions.register'}}
type='submit'
disabled={{@model.cannotSave}}
class='nowrap'
@disabled={{@model.cannotSave}}
>
{{t 'resources.worker.actions.register'}}
</Rose::Button>
/>
{{/if}}
</div>
+30 -46
View File
@@ -11,39 +11,31 @@
as |form|
>
<form.input
@name='type'
@value={{@model.type}}
@label={{t 'form.type.label'}}
@helperText={{t 'form.worker_type.help'}}
@disabled={{true}}
readonly={{true}}
/>
<form.input
@name='name'
@type='name'
<Hds::Form::TextInput::Field
name='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
@isInvalid={{@model.errors.name}}
@isOptional={{true}}
disabled={{form.disabled}}
{{on 'input' (set-from-event @model 'name')}}
as |F|
>
<F.Label>{{t 'form.name.label'}}</F.Label>
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
{{#if @model.errors.name}}
<field.errors as |errors|>
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</field.errors>
</F.Error>
{{/if}}
</form.input>
</Hds::Form::TextInput::Field>
<Hds::Form::Textarea::Field
name='description'
@isOptional={{true}}
@value={{@model.description}}
@isInvalid={{@model.errors.description}}
name='description'
disabled={{form.disabled}}
as |F|
>
@@ -82,42 +74,34 @@
</F.Control>
</Hds::Form::Fieldset>
<form.input
@name='address'
@type='url'
@value={{@model.address}}
@label={{t 'form.address.label'}}
@helperText={{t 'form.worker_address.help'}}
@disabled={{true}}
readonly={{true}}
/>
<InfoField @value={{@model.address}} disabled={{form.disabled}} as |F|>
<F.Label>{{t 'form.address.label'}}</F.Label>
<F.HelperText>{{t 'form.worker_address.help'}}</F.HelperText>
</InfoField>
{{#let
(relative-datetime-live @model.last_status_time)
(format-date @model.last_status_time format='yymmddt')
as |relativeDate customFormat|
}}
<form.input
@name='last seen'
@type='datetime'
<InfoField
@value={{if
@model.last_status_time
(concat relativeDate ', ' customFormat)
}}
@label={{t 'form.worker_last_seen.label'}}
@helperText={{t 'form.worker_last_seen.help'}}
@disabled={{true}}
readonly={{true}}
/>
as |F|
>
<F.Label>{{t 'form.worker_last_seen.label'}}</F.Label>
<F.HelperText>{{t 'form.worker_last_seen.help'}}</F.HelperText>
</InfoField>
{{/let}}
<form.input
@name='release version'
@value={{@model.release_version}}
@label={{t 'form.worker_release_version.label'}}
@helperText={{t 'form.worker_release_version.help'}}
@disabled={{true}}
readonly={{true}}
/>
<InfoField @value={{@model.release_version}} readonly={{true}} as |F|>
<F.Label>{{t 'form.worker_release_version.label'}}</F.Label>
<F.HelperText>{{t 'form.worker_release_version.help'}}</F.HelperText>
</InfoField>
{{#if (can 'save worker' @model)}}
<form.actions
@@ -18,7 +18,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'delete' @model)}}
{{on 'click' @delete}}
>
{{t 'resources.group.actions.delete'}}
</dropdown.button>
@@ -8,10 +8,4 @@
<DocLink @doc='group' @iconSize='large' />
</h2>
<p>{{t 'resources.group.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='host-catalog' @iconSize='large' />
</h2>
<p>{{t 'resources.host-catalog.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='host-set' @iconSize='large' />
</h2>
<p>{{t 'resources.host-set.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='host' @iconSize='large' />
</h2>
<p>{{t 'resources.host.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='host' @iconSize='large' />
</h2>
<p>{{t 'resources.host.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='role' @iconSize='large' />
</h2>
<p>{{t 'resources.role.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='target' @iconSize='large' />
</h2>
<p>{{t 'resources.target.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -8,10 +8,4 @@
<DocLink @doc='user' @iconSize='large' />
</h2>
<p>{{t 'resources.user.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
<Hds::Copy::Snippet @textToCopy={{@model.id}} @color='secondary' />
@@ -5,9 +5,45 @@
import Controller from '@ember/controller';
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 {
// =services
@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');
}
}
+63 -1
View File
@@ -5,11 +5,73 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { action } from '@ember/object';
export default class ApplicationController extends Controller {
// =services
@service session;
@service flashMessages;
@service features;
@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;
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
}
}
@@ -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;
}
@@ -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 { action } from '@ember/object';
export default class ScopesScopeAuthMethodsIndexController extends Controller {
// =attributes
@@ -15,4 +16,35 @@ export default class ScopesScopeAuthMethodsIndexController extends Controller {
get hasPrimaryAuthMethod() {
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);
}
}
@@ -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;
}
@@ -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 } });
}
}
@@ -3,10 +3,13 @@
* 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 {
// =services
@controller(
'scopes/scope/credential-stores/credential-store/credential-libraries/index',
)
credentialLibraries;
// =attributes
queryParams = ['type'];
@@ -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;
}
@@ -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 } });
}
}
@@ -3,10 +3,13 @@
* 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 {
// =services
@controller(
'scopes/scope/credential-stores/credential-store/credentials/index',
)
credentials;
// =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
*/
import Controller from '@ember/controller';
import Controller, { inject as controller } from '@ember/controller';
export default class ScopesScopeCredentialStoresNewController extends Controller {
// =services
@controller('scopes/scope/credential-stores/index') credentialStores;
// =attributes
queryParams = ['type'];
@@ -4,10 +4,16 @@
*/
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 {
// =services
@service router;
// =attributes
/**
@@ -26,4 +32,29 @@ export default class ScopesScopeGroupsGroupAddMembersController extends Controll
const sorted = [global, ...orgsAndProjects].flat();
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();
}
}
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more