Compare commits

...
119 Commits
Author SHA1 Message Date
Cameron Perera cb895024e0 fix: 🐛 update hidden secrets and flatten-nested-object logic (#2672) 2025-01-30 18:00:26 -06:00
Cameron Perera 2e94ebdb64 chore: 🤖 update cli version and CHANGELOG (#2670)
* chore: 🤖 update cli version and CHANGELOG

* chore: 🤖 fix dc release date in CHANGELOG
2025-01-29 12:06:27 -06:00
Cameron Perera ae1e6d2748 feat: 🎸 add showFilterGenerator to WorkerFilterGenerator comp (#2669)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-16282
2025-01-28 16:24:38 -08:00
Laurenandcameronperera c72d4f35a0 Vault cred store filter create (#2668)
* refactor: 💡 add worker filter back to create form

✅ Closes: ICU-16281

* add helper text to worker filter generator at creation state

* set isOptional to true

* set isOptional to true

---------

Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
2025-01-28 16:46:11 -05:00
Michael Li 9742d824c4 test(e2e/desktop): Address failures in session tests (#2664)
* test(e2e): Address failures in session tests

* CR
2025-01-28 11:04:46 -05:00
Lisbet Alvarez 639aa47e19 refactor: 💡 Update text for fields in AWS & GCP host catalogs (#2667) 2025-01-27 15:36:30 -08:00
Michael Li 5fa0d11aa9 test(e2e/admin): Updates for 0.19.0 (#2665)
* test(e2e): Update "Inactive" locators

* test(e2e): Update worker filter locators

* test(e2e): Address flake

Ensure that the test has reached the org page before trying to click the Auth Methods link (there is an auth method link on both the global page and the org page).
2025-01-27 10:01:44 -05:00
Dharini Jeeva fed32aa57b chore: 🤖 add doc links for GCP plugin (#2666) 2025-01-24 12:45:56 -08:00
Carlos Alcaide Corvo 1e2134bd7e Refactor auth methods red test selectors (#2656)
* Refactor auth-methods list acceptance test selectors
2025-01-24 12:11:19 -08:00
Dharini Jeeva 064375d457 test: 💍 add e2e tests for credentials display in desktop (#2660)
* test: 💍 add e2e tests for credentials display in desktop
2025-01-23 14:30:54 -08:00
Dharini Jeeva deff38de56 fix: 🐛 include GCP plugin text (#2662)
* fix: 🐛 include GCP plugin text
2025-01-23 13:34:33 -08:00
Carlos Alcaide Corvo 15950d3a09 fix: 🐛 Fix HDS deprecation message (#2661)
Fix HDS deprecation message for HDS Dropdown interactive component using
@text rather than yielding it.
2025-01-23 08:15:12 -08:00
Carlos Alcaide Corvo c4539b8e30 Refactor auth-methods list acceptance test selectors (#2654)
Refactor auth-methods list acceptance test selectors
2025-01-22 14:32:16 -08:00
Carlos Alcaide Corvo 904864b14e Test icu 15459 auth method create test selectors refactor (#2653)
Refactor selectors
2025-01-20 09:45:15 -08:00
Zhihe Li 390d179917 feat: 🎸 Added GCP host sets and hosts (#2655)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15994
2025-01-10 14:18:48 -08:00
Dharini Jeeva 3b6a421af2 chore: Updates to gcp hc form (#2649)
* chore: 🤖 add worker_filter field, updates to helper text
2025-01-10 14:18:48 -08:00
Dharini Jeeva cc8d4fe952 test: GCP plugin acceptance tests (#2648) 2025-01-10 14:18:48 -08:00
Dharini Jeeva dfa62dd04b chore: 🤖 gcp plugin creation in the UI (#2645)
gcp plugin creation and mirage changes
2025-01-10 14:18:48 -08:00
Dharini Jeeva 520020f2c6 Chore: refactor host catalog model and serializer to accommodate GCP plugin (#2627)
* chore: 🤖 GCP plugin support
2025-01-10 14:18:48 -08:00
hashiccandhashicc 31f1ed8dd4 test(e2e): add e2e test for scope chooser with filtered targets (#2644)
* test(e2e): add e2e test for scope chooser with filtered targets

Closes: https://hashicorp.atlassian.net/browse/ICU-16121

* Add missing copyright headers

* chore(e2e): use prettier on scope.spec.js

* refactor(e2e): use explicit checks for scope in summary

* refactor(e2e): move test hooks and associated variables to top-level

Move beforeEach and afterEach and the associated variables setup in
hooks to top-level within scope.spec.js. This matches the patterns
used in other tests.

* refactor(e2e): use simpler scoped locator for summary element within header-nav

---------

Co-authored-by: hashicc <hashicc@users.noreply.github.com>
2025-01-10 17:00:49 -05:00
Cameron Perera 5abd4f1f4f Icu 15470 credential store create test (#2650)
* refactor: 💡 clean up urls in create test file

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

* test: 💍 update cred store create to use selectors

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15470
2025-01-10 09:57:32 -06:00
Lisbet Alvarez 862ce645cf fix: 🐛 hds dialog text wrong color when system mode is dark (#2651) 2025-01-09 15:30:06 -08:00
Lisbet Alvarez 9eccbb91db refactor: 💡 Update name radio option label & set toggle default to on (#2652)
* refactor: 💡 Update name radio option label & default toggle on
2025-01-09 15:18:17 -08:00
Carlos Alcaide Corvo 343e1d2193 test: 💍 Refactor host list test selectors (#2629)
* test: 💍 Refactor host list test selectors
2025-01-08 09:14:29 -08:00
Zhihe Li 9a11925216 docs: ✏️ Update readme docs for e2e tests (#2647) 2025-01-07 17:28:28 -05:00
Lisbet Alvarez f7f1041daa fix: 🐛 oidc retry not working for secure links in desktop ui (#2641)
* fix: 🐛 oidc retry not working for secure links in desktop ui
2025-01-07 10:56:32 -08:00
Michael Li 1cbf3b227e test(e2e): Add additional assertions to UI tests (#2634)
* test(e2e): Address deprecation

* test(e2e): Add additional connection assertions on tests

* CR: Fix flake when waiting for sessions

If no sessions are available and the page needs to reload, we should wait to ensure that the page has loaded before checking what the state of the sessions are

* CR: Address flake in connect methods

Noticed an issue where a session recording was not playable. The theory is that the session didn't last long enough before it was cancelled. This adds a 2 second wait after establishing a connection before continuing.

* CR: Use authenticated state by default

* CR: Update dynamic host catalog assertion

* CR: Simplify host set checks

- Use .all() when looping
- Add check to see if we need to reload the page or not

* CR: Be more specific with locator

Ran into a conflict with the Static/Dynamic host catalog type and the Static/Dynamic credential type.

* CR: Update connection methods to be more resilient

Rather than using a hardcoded sleep, we will now run a command on the target and wait for a specific string to return before continuing.

* CR
2025-01-06 11:40:32 -05:00
Lisbet Alvarez faf4ecfac6 Fix memory leak issue when using fetch in oidc authentication (#2640)
* test: 💍 test 1

* chore: 🤖 remove waitForPromise use

* chore: 🤖 remove all other waitforpromise usage

* test: 💍 turn down polling

* test: 💍 use .json

* refactor: 💡 add waitForPromise back in & consume response body

* test: 💍 some testing

* test: 💍 more testing

* refactor: 💡 clean-up a bit

* refactor: 💡 use head method request & clean up comments

* refactor: 💡 use get method & consume response body

* test: 💍 fix token validation test cases
2025-01-03 14:06:27 -08:00
Michael Li ca4aad0a2b test(e2e): Add Admin UI test for OIDC auth method (#2638)
* test(e2e): Add managed group check in LDAP test

* test(e2e): Add oidc test

* CR: Suppress warning

* CR: Use toHaveText
2025-01-03 14:19:33 -05:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] ed569306bb Result of tsccr-helper -log-level=info gha update -latest .github/ (#2642)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2025-01-02 09:41:32 -08:00
Lisbet Alvarez 65abd7a4e3 fix: 🐛 code block error in console when value is empty on save (#2637) 2024-12-20 14:03:35 -08:00
Lauren 422cff4547 [ICU-15647] Replace Rose::Dialog with Hds::Modal in admin ui only (#2623)
* sort list

* refactor: 💡 Replace Rose::Dialog with Hds::Modal

✅ Closes: ICU-15647

* refactor targets-host-sources-test

* refactor update-test

* update test selectors

* refactor delete test

* refactor org scope test

* refactor delete scope and auth-method tests

* refactor update host catalogs tests

* refactor credential-stores credential update tests

* refactor credential-stores credentials delete tests

* refactor scopes update tests

* refactor storage buckets delete test

* refactor storage buckets delete test

* refactor credential library update test

* refactor targest workers test

* refactor workers create tags test

* refactor global scope test

* refactor users update test

* refactor host catalogs delete test

* refactor credential-store update tests

* refactor host catalogs, hosts and sets update tests

* remove unused vars

* rename selectors, remove attrs

* add @onDismiss callback
2024-12-20 14:16:43 -05:00
Lisbet Alvarez 7695135302 refactor: 💡 application state ability dependent descriptions (#2631)
* refactor: 💡 application state ability dependent descriptions

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15813
2024-12-17 16:03:56 -08:00
Cameron Perera 5be4868e9a refactor: 💡 rename arialabel to aria-labelledby for key/value (#2633)
* refactor: 💡 rename arialabel to aria-labelledby for key/value

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

* refactor: 💡 rename ariaLabelledBy to naming conventions

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15701
2024-12-17 13:58:04 -06:00
Cameron Perera fce97fd57a fix: 🐛 change disabled to readonly for form inputs (#2618)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15666
2024-12-17 13:31:47 -06:00
Michael Li af8655ec45 test(e2e): Refactor imports for boundary cli helper methods (#2632)
* test(e2e): Update imports to match boundary-http conventions

* test(e2e): Remove cli from method name
2024-12-17 10:17:32 -05:00
Cameron Perera daed714b98 test: 💍 update selectors for credential-library tests (#2630)
delete and list

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15468
2024-12-16 16:31:07 -06:00
Carlos Alcaide Corvo 584e62cff3 test: 💍 Refactor host delete test selectors (#2626)
Refactor host-catalog host delete acceptance test selectors
2024-12-13 15:57:10 -08:00
Lisbet Alvarez 6d2f1dcbbf Cleanup worker filter translations (#2621)
* refactor: 💡 clean-up worker filter translations
2024-12-12 16:06:38 -08:00
Cameron Perera 5e7b74a7ed test: 💍 use selectors in update credential-libraries (#2624)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15467
2024-12-12 12:26:52 -06:00
Dharini Jeeva 6de20b03ef chore: handle nested data from static json credentials (#2611) 2024-12-11 15:08:51 -08:00
2869020e65 Llb/worker filter improvements (#2614)
* test: 💍 clean up worker filter component tests (#2559)

* Icu 15741 add show filter generator toggle to worker filter component (#2562)

* feat: 🎸 add filter generator to worker filter component

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

* feat: 🎸 show add worker filter when none set for targets (#2564)

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

* Icu 15743 tag workflow (#2565)

* feat: 🎸 add tag generator workflow

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

* test: 💍 add test cases

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

* refactor: 💡 update selected type name to selectedGeneratorType

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

* Apply suggestions from code review

minor translation change

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>

* refactor: 💡 update id for generated-value

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

---------

Co-authored-by: Lisbet Alvarez <lisbet.alvarez@hashicorp.com>
Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>

* Icu 15744 name workflow (#2566)

* feat: 🎸 add name workflow

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

* test: 💍 add "name" workflow tests for filter generator

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

* refactor: 💡 use const test selectors for filter generator

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

* refactor: 💡 add default option

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

* docs: ✏️ fix comment

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

---------

Co-authored-by: cameronperera <cameron.perera@hashicorp.com>

* refactor: 💡 move worker filter generator outside of rose::form (#2574)

* refactor: 💡 move worker filter generator outside of rose::form

* [ICU-15718] Add worker filter route, template for empty state (#2576)

* feat: 🎸 Vault Credential Store Worker Filter

The Worker Filters tab will display the associated worker filter and
have an empty state for when there is no worker filter assigned.

* add template

* Add missing copyright headers

* update styling, remove Rose component

* color primary is already default, remove

* add rose component, remove custom css

* fix test

* singularize lang

---------

Co-authored-by: laurenolivia <laurenolivia@users.noreply.github.com>

* feat: 🎸 update worker filter field to use code editor (#2578)

* feat: 🎸 update worker filter field to use code editor

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

* test: 💍 fix failing storage bucket tests

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

* test: 💍 update tests for new worker filter field (#2580)

* test: 💍 update tests for new worker filter field

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

* Update ui/admin/tests/acceptance/storage-buckets/update-test.js

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>

* [ICU-15719] Create edit worker filter route and template (#2582)

* add codeblock to template

* add cancel btn fn

* add controller test

* add save fn

* display formatted worker filter text inside codeblock

* move codeblock component outside rose::layout::centered

* remove console.log

* remove router.refresh() and refactor translations yaml

* refactor translations, add breadcrumb::item

* replace html elements with hds

* move component, delete file

* [ICU-15720] Add edit worker filter action to manage dropdown (#2593)

* add acceptance test file

* delete test file

* add edit option to manage dropdown

* update route in dropdown

* add check for model isVault

* add acceptance test file

* delete test file

* remove data test selectors, fix syntax

* [ICU-15721] Remove worker filter input field (#2596)

* remove worker filter input field from vault credential store form

* revert yaml

* [ICU-15838] Add vault credential store worker filter tests (#2595)

* add data test elements

* add subtests to update-test.js

* add subtext to check for .hasText()

* add subtest when worker filter exists

* update subtest name

* remove console.log

* clean up

* add new selectors

* refactor: 💡 worker filter clean-up (#2592)

* refactor: 💡 worker filter clean-up

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

* refactor: 💡 add aria labels & missing can save check

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

* refactor: 💡 pr feedback of using uuid

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

* Add missing copyright headers

* refactor: 💡 use unique-id helper for aria labels (pr feedback)

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

---------

Co-authored-by: lisbet-alvarez <lisbet-alvarez@users.noreply.github.com>

* refactor: 💡 update icon logic for egress worker filter (#2612)

* refactor: 💡 update worker_filter help translation

* refactor: 💡 add maxHeight to worker filter code blocks

* refactor: 💡 use existing default option for dropdowns

* refactor: 💡 hide copy button when no result to copy

and remove unused styles

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Co-authored-by: Lisbet Alvarez <lisbet.alvarez@hashicorp.com>
Co-authored-by: Lauren <lauren.burwell@hashicorp.com>
Co-authored-by: laurenolivia <laurenolivia@users.noreply.github.com>
Co-authored-by: lisbet-alvarez <lisbet-alvarez@users.noreply.github.com>
2024-12-11 14:00:27 -06:00
Carlos Alcaide Corvo 40c8ceeca6 Change validation field from name (optional) to address (required) (#2619) 2024-12-11 08:04:45 -08:00
Lisbet Alvarez 0623d4ad47 Fix e2e test page selectors due to hds refactor (#2610)
* chore: 🤖 fix e2e broken tests due to hds refactor

* chore: 🤖 remove unneeded change & fix another selector

* chore: 🤖 upgrade playwright

* chore: 🤖 remove unneeded dependency in desktop ui
2024-12-10 16:24:25 -08:00
dependabot[bot]andZhihe Li 0b9c235dd2 chore(deps-dev): bump nanoid from 5.0.8 to 5.0.9 (#2620)
Bumps [nanoid](https://github.com/ai/nanoid) from 5.0.8 to 5.0.9.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/5.0.8...5.0.9)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: direct:development
...

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-12-10 16:53:38 -05:00
Zhihe Li e16b4e68c8 Icu 14778 add more sessions tests (#2615)
* test: 💍 Add desktop sessions tests

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

* chore: 🤖 Add support for dotenv and stop using relative paths

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

* refactor: 💡 Force usage of node 20

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14778
2024-12-10 16:11:12 -05:00
Cameron Perera 078e36f9b8 fix: 🐛 update @name attribute for RadioCard Groups (#2617)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15676
2024-12-10 14:12:43 -06:00
Zhihe Li 8874d0be86 docs: ✏️ Add to readme how to run admin e2e against local branch (#2616)
* docs: ✏️ Add to readme how to run admin e2e against local branch

* chore: 🤖 Add packageManager field for corepack users
2024-12-10 12:30:57 -05:00
Carlos Alcaide Corvo b59afd3455 test: 💍 Update test selectors for update host test (#2609) 2024-12-09 09:42:16 -08:00
Lauren 85a2684532 refactor: 💡 Replace rose::separator comp with hds::separator (#2613)
* refactor: 💡 Replace rose::separator comp with hds::separator

This PR replaces the rose::separator with the hds::separator and removes
an integration test that is no longer needed

✅ Closes: ICU-15646

* retire rose::separator and remove styling

* remove separator import

* remove separator.js
2024-12-06 12:54:47 -05:00
Carlos Alcaide Corvo f9a7864b07 fix: 🐛 Delete peerDeps within our addons (#2605)
* fix: 🐛 Delete peerDeps within our addons
2024-12-04 17:17:51 -08:00
Lisbet Alvarez 371301b4ba refactor: 💡 use hds::applicationstate instead of rose::message (#2577)
* refactor: 💡 use hds::applicationstate instead of rose::message

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15639
2024-12-04 14:16:05 -08:00
Zhihe Li 9c10d79661 Icu 14777 add more target tests (#2594)
* refactor: 💡 Move global setup to root
* refactor: 💡 Move env variable test fixture to global setup file
* test: 💍 Add more DC e2e tests for targets
* docs: ✏️ Updated e2e README

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14777
2024-12-03 18:51:22 -05:00
Carlos Alcaide Corvo 018d879a0a fix: 🐛 Fix addons/auth yarn warning (#2604)
Fix unmet peer dep within addons/auth
2024-12-03 13:35:01 -08:00
Carlos Alcaide Corvo b64f1876dc Refactor icu 15872 await authenticatesession admin (#2603)
* Add await for all authenticateSession within /tests/acceptance

* Add await for all authenticateSession within /tests/unit
2024-12-03 10:27:27 -08:00
Carlos Alcaide Corvo 055607b3dd refactor: 💡 Clean up release triggers (#2602) 2024-12-03 07:44:32 -08:00
Carlos Alcaide Corvo 8a7fa26cb5 refactor: 💡 Delete build desktop win docker (#2601) 2024-12-02 15:47:09 -08:00
Carlos Alcaide Corvo 9cac16fe6a refactor: 💡 Add await to authenticateSession within desktop (#2600) 2024-12-02 15:30:28 -08:00
Carlos Alcaide Corvo 61297dba02 refactor: 💡 Add await to invalidSession within admin (#2599) 2024-12-02 15:05:26 -08:00
Lisbet Alvarez 2f8a53e3ea refactor: 💡 remove rose table references (#2589)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15644
2024-12-02 12:22:01 -08:00
Carlos Alcaide Corvo 96b9a20844 refactor: 💡 Add await to invalidSession within desktop (#2598) 2024-12-02 11:51:12 -08:00
Carlos Alcaide Corvo 443f230251 refactor: 💡 Migrate ember-uuid to uuid (#2597)
* refactor: 💡 Migrate ember-uuid to uuid
2024-12-02 11:36:25 -08:00
86ee79e6bf Result of tsccr-helper -log-level=info gha update -latest .github/ (#2608)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
Co-authored-by: Carlos Alcaide Corvo <ccorvo@hashicorp.com>
2024-12-02 11:22:03 -08:00
Cameron Perera b24e5a2c22 fix: 🐛 default to light theme for acceptance tests (#2587)
* fix: 🐛 default to light theme for acceptance tests

* refactor: 💡 add async/await to authenticateSession
2024-12-02 12:51:24 -06:00
Carlos Alcaide Corvo 8c6b1aa69a Clean up auto-generated TODO comments (#2607) 2024-12-02 09:02:44 -08:00
Dharini Jeeva 36c2aee271 fix vault brokered credentials display (#2583)
* fix: 🐛 brokered credential display in credential panel
2024-11-26 14:34:10 -08:00
Carlos Alcaide Corvo b049635d28 chore: 🤖 Upgrade uuid (#2591)
Upgrade uuid
2024-11-26 09:23:41 -08:00
Carlos Alcaide Corvo 42dd029611 refactor: 💡 Remove rose:linkbutton (#2590)
Remove rose:linkbutton references and tests
2024-11-26 08:58:09 -08:00
Carlos Alcaide Corvo e02c50aef8 chore: 🤖 Upgrade sinon (#2585)
Upgrade sinon to v19
2024-11-26 08:41:23 -08:00
Carlos Alcaide Corvo d9cf112694 chore: 🤖 Upgrade lint-staged to v15 (#2586)
Upgrade lint-staged to v15
2024-11-22 15:17:07 -08:00
Carlos Alcaide Corvo a4456f50cd refactor: 💡 Delete not necessary comments (#2588) 2024-11-22 11:27:53 -08:00
Carlos Alcaide Corvo a358061f22 chore: 🤖 Delete ember-cli-app-version (#2584)
Delete ember-cli-app-version
2024-11-22 09:09:56 -08:00
Carlos Alcaide Corvo f155e8ad44 Migrate embe-autofocus-modifier to HTML autofocus standard (#2581) 2024-11-22 08:48:53 -08:00
Lisbet Alvarez c930bb235b style: 💄 fix z-index for rose-dialog modal (#2579) 2024-11-20 12:23:52 -08:00
Lisbet Alvarez 543cf4697f refactor: 💡 remove rose::notification in favor of hds (#2557)
* refactor: 💡 remove rose::notification in favor of hds

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15634
2024-11-19 15:18:18 -08:00
Carlos Alcaide Corvo 66e9e971a1 Upgrade ember-simple-auth to latest (#2573)
Upgrade ember-simple-auth to latest

* Change ember-auto-import from devDependencies to dependencies
2024-11-19 13:47:36 -08:00
Cameron Perera a961b5e4be chore: 🤖 bump fast-sourcemap-concat version (#2575) 2024-11-19 15:28:20 -06:00
Dharini Jeeva f8796b362e chore: 🤖 remove ember radio btn, update it with hds (#2556)
* chore: 🤖 remove ember radio btn, update it with hds
2024-11-19 13:03:29 -08:00
Dharini Jeeva 415f638028 Icu 15713 address deprecation warning for dropdown interactive element (#2561) 2024-11-19 11:32:06 -08:00
Carlos Alcaide Corvo 4523615829 chore: 🤖 Upgrade ember-cookies (#2572)
* chore: 🤖 Upgrade ember-cookies
2024-11-18 12:28:18 -08:00
Carlos Alcaide Corvo 61214af01a chore: 🤖 Upgrade and Migrate husky from v8 to v9 (#2571)
Upgrade and migrate Husky from v8 to v9.1.6
2024-11-18 11:51:30 -08:00
Zhihe Li 2f0bf5dc17 fix: 🐛 Fix unintentional changes to yarn lock (#2570) 2024-11-14 17:18:46 -05:00
Zhihe Li 6e71712092 refactor: 💡 Fix linting errors in e2e (#2563) 2024-11-14 16:44:11 -05:00
Zhihe Li 7e56e1cccf style: 💄 Run prettier on e2e tests (#2560) 2024-11-12 18:11:00 -05:00
Cameron Perera 1e3f533d83 refactor: 💡 move WorkerFilter component to more generic folder (#2558)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15742
2024-11-12 14:10:06 -06:00
Lisbet Alvarez 57568f9a8c test: 💍 add missing controller unit tests in admin-ui (#2553)
* test: 💍 add missing controller unit tests in admin-ui

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14740
2024-11-12 09:27:07 -08:00
Zhihe Li 7c1814118d refactor: 💡 Move e2e tests to new folder (#2546)
* refactor: 💡 Move e2e tests to new folder

* refactor: 💡 Convert desktop tests to mjs and use es6 imports

* refactor: 💡 Use module setting for e2e directory and remove mjs

* refactor: 💡 Remove "e2e" in all scripts in package.json
2024-11-12 11:21:37 -05:00
Michael Li ca50a963fd test(e2e): Add extra timeout for "delete resources" test (#2524) 2024-11-11 09:10:13 -05:00
Cameron Perera 6e188c6996 Icu 15465 credential library create test (#2555)
* test: 💍 use commonSelectors in cred-lib tests

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

* test: 💍 create selectors for cred-lib acceptance tests

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

* test: 💍 finish selector refactor for cred-lib create

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15465
2024-11-08 15:12:43 -06:00
Carlos Alcaide Corvo 0095c5c666 chore: 🤖 Upgrade qunit-dom (#2552)
* chore: 🤖 Upgrade qunit-dom and qunit
2024-11-08 09:09:22 -08:00
Carlos Alcaide Corvo bdd7f303c4 test: 💍 Update test selectors for read hosts (#2549)
* test: 💍 Update test selectors for read hosts
2024-11-07 13:54:27 -08:00
Lisbet Alvarez f0383522d8 chore: 🤖 audit remaining resolutions (#2550) 2024-11-07 13:30:21 -08:00
Dharini Jeeva a32cc13171 Icu 15481 groups read and list test (#2551)
* refactor: 💡 test selectors in read and list groups
2024-11-06 15:13:17 -08:00
Dharini Jeeva dbda8c5674 Icu 15480 groups create test (#2544) 2024-11-06 10:59:16 -08:00
Carlos Alcaide Corvo 186873d9d4 Remove build debian in Mac OS and all its dependencies (#2543) 2024-11-06 10:19:49 -08:00
Carlos Alcaide Corvo 37482c0b36 chore: 🤖 Upgrade Concurrently (#2547)
Upgrade Concurrently from 8.2.0 to 9.1.0

✅ Closes: ICU-15651
2024-11-06 09:27:11 -08:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] ccdc5a03ac Result of tsccr-helper -log-level=info gha update -latest .github/ (#2548)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-11-04 10:54:51 -08:00
Carlos Alcaide Corvo 2ae24c4627 Change boundary-ui-releases to boundary-desktop-releases (#2535) 2024-11-04 10:16:09 -08:00
Carlos Alcaide Corvo b9e9fd21ee Chore icu 15637 upgrade ember test helpers (#2545)
* Upgrade ember-test helpers

* Upgrade ember-int and ember-cli-clipboard, as ember-test-helpers introduces a breaking change
2024-11-04 09:57:07 -08:00
Zack Moore 6eb83b2a25 Upgrade HDS dependency to 4.13 & migrate FlightIcon to Hds::Icon (#2540)
* updated hds and replaced deprecated flight-icon

* removed code trying to import from ember-flight-icons

* fixing linting issue

* fixing linting issue

* fixing dropdown test selectors

* updated dropdown selectors

* updated dropdown selectors

* updated dropdown selectors

* addressing pr feedback

* fixing linting error

* updating dropdown selectors
2024-11-04 11:29:56 -06:00
Carlos Alcaide Corvo 70aceb1699 Update test selectors for create hosts (#2542)
* Use of common and domain specific selectors for create host test.
2024-11-04 08:43:41 -08:00
Dharini Jeeva 5e208c4115 Enable storage bucket deletion (#2541)
* fix: 🐛 enable storage bucket deletion
2024-10-31 09:36:19 -07:00
Lisbet Alvarez 390f051e43 chore: 🤖 audit most recent resolutions (#2531)
* chore: 🤖 audit most recent resolutions
2024-10-28 16:28:26 -07:00
Cameron Perera ab0f1fd549 test: 💍 update test selectors for accounts update (#2539)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14042
2024-10-28 14:27:55 -05:00
Zhihe Li 788df7b16d test: 💍 Add some settings page tests (#2538) 2024-10-28 15:10:54 -04:00
Lisbet Alvarez 01a2d8e9a6 refactor: 💡 remove route-action usage in admin ui (#2534)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12945
2024-10-28 09:23:08 -07:00
Cameron Perera e842eb001d Icu 14039 accounts list, read, and set-password test (#2537)
* test: 💍 update test selectors for accounts list

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

* test: 💍 update test selectors for accounts read

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

* test: 💍 update test selectors for accounts set password

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14039
2024-10-25 15:58:09 -05:00
Cameron Perera f195e5dc9d Icu 15405 remove patch and resolutions due to ember cli upgrade (#2536)
* chore: 🤖 remove lodash.template patch

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

* chore: 🤖 remove markdown-it resolution

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

* chore: 🤖 async dependency clean up

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

* chore: 🤖 remove clean-css resolution

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

* chore: 🤖 remove winston/async resolution

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15405
2024-10-24 06:57:49 -05:00
Cameron Perera 7f7f0f1859 test: 💍 update test selectors for accounts delete (#2532)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14038
2024-10-23 11:33:55 -05:00
Cameron Perera b494a703eb chore: 🤖 upgrade ember-cli version (#2533)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15393
2024-10-23 09:40:45 -05:00
Lisbet Alvarez 0f52b86364 refactor: 💡 remove route-action usage for targets and sessions (#2528)
* refactor: 💡 remove route-action usage for targets and sessions

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15040
2024-10-22 13:16:58 -07:00
Cameron Perera 1b31188d5e test: 💍 selectors refactor for accounts create (#2527)
* test: 💍 selectors refactor for accounts create

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

* refactor: 💡 remove name & description duplicate selectors

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14037
2024-10-21 17:12:00 -05:00
Lisbet Alvarez 30e7268297 chore: 🤖 resolve socks to new version to fix ip vulnerability (#2529)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14207
2024-10-21 10:22:32 -07:00
Dharini Jeeva 6956b30fd6 Update credentials (#2523)
* feat: 🎸 allow AWS host catalog credential type update
2024-10-18 14:33:14 -07:00
Cameron Perera 98d19ef591 test: 💍 add new selectors for accounts acceptance tests (#2525)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14036
2024-10-18 09:56:31 -05:00
Carlos Alcaide Corvo f0ffcea424 chore: 🤖 Clean up Desktop out directory (#2521)
* chore: 🤖 Clean up Desktop out directory
2024-10-18 09:27:40 +02:00
Michael Li 6454fb7061 test(e2e): Add check for worker filters (#2513)
* test(e2e): Add check to add an ingress worker filter

* test(e2e): Add check for editing a worker filter
2024-10-17 12:51:49 -04:00
Lisbet Alvarez c0d7af7899 fix: 🐛 alias disappearing on target details page (#2519)
* fix: 🐛 alias disappearing on target details page

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

* test: 💍 fix test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15296
2024-10-15 12:58:43 -07:00
Cameron Perera 2424395415 fix: 🐛 add bottom border back to ListWrapper component (#2522) 2024-10-15 13:54:14 -05:00
683 changed files with 14990 additions and 9548 deletions

No files matched your search

+16 -16
View File
@@ -11,15 +11,15 @@ jobs:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: "**/node_modules"
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn install
@@ -28,8 +28,8 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -37,17 +37,17 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn run build:ui:admin:oss
- name: Upload artifact admin-ui
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: admin-ui
path: ./ui/admin/dist/
- name: Upload artifact OSS admin UI
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: admin-ui-oss
path: ./ui/admin/dist/
@@ -56,8 +56,8 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -65,12 +65,12 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn run build:ui:admin:enterprise
- name: Upload artifact enterprise admin UI
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: admin-ui-ent
path: ./ui/admin/dist/
@@ -79,8 +79,8 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: "**/node_modules"
@@ -88,12 +88,12 @@ jobs:
restore-keys: |
node-modules-
- name: Build
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn run build:ui:admin:hcp
- name: Upload artifact HCP admin UI
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: admin-ui-hcp
path: ./ui/admin/dist/
@@ -1,34 +0,0 @@
name: "Trigger Build Artifacts for Testing"
on:
workflow_dispatch:
inputs:
branch:
description: Please use tag-like format n.n.n-someword (1.111.11-beta)
required: true
product_version:
required: true
type: string
description: The version number belonging to the oss binary to be used
commit:
required: true
type: string
description: The git SHA of the oss artifacts to be used
jobs:
trigger-gha-build-artifacts-for-testing:
runs-on: ubuntu-latest
steps:
- name: Release description
if: github.event_name == 'workflow_dispatch'
run: |
echo "Triggering Build Artifacts for Testing using BRANCH and TEST_TAG ${{ github.event.inputs.branch }}"
- name: Trigger Build Artifacts for Testing in boundary-ui-releases
run: |
export GITHUB_TOKEN="${{ secrets.ELEVATED_GITHUB_TOKEN }}"
gh workflow run build-artifacts-for-testing.yml \
--repo hashicorp/boundary-ui-releases \
--ref main \
-f TAG="${{ inputs.branch }}" \
-f PRODUCT_VERSION="${{ inputs.product_version }}" \
-f COMMIT="${{ inputs.commit }}" \
+3 -3
View File
@@ -30,11 +30,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5618c9fc1e675841ca52c1c6b1304f5255a905a0 # codeql-bundle-v2.19.0
uses: github/codeql-action/init@3096afedf9873361b2b2f65e1445b13272c83eb8 # codeql-bundle-v2.20.0
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@5618c9fc1e675841ca52c1c6b1304f5255a905a0 # codeql-bundle-v2.19.0
uses: github/codeql-action/analyze@3096afedf9873361b2b2f65e1445b13272c83eb8 # codeql-bundle-v2.20.0
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 # v1.1.3
- name: Add missing Copyright Headers
run: copywrite headers
+12 -12
View File
@@ -6,15 +6,15 @@ jobs:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn install
@@ -24,15 +24,15 @@ jobs:
needs: [dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn run compliance:licenses
@@ -43,15 +43,15 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
node-modules-
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn lint
@@ -62,13 +62,13 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
id: node-modules-cache
with:
path: '**/node_modules'
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
- run: yarn test
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
- run: |
@@ -1,25 +0,0 @@
name: "Trigger Test Boundary UI release"
on:
workflow_dispatch:
inputs:
branch:
description: Please use tag-like format n.n.n-someword (1.111.11-beta)
required: true
jobs:
trigger-gha-release:
runs-on: ubuntu-latest
steps:
- name: Release description
if: github.event_name == 'workflow_dispatch'
run: |
echo "Triggering Test release using BRANCH and TEST_TAG ${{ github.event.inputs.branch }}"
- name: Trigger Release workflow in boundary-ui-releases
run: |
export GITHUB_TOKEN="${{ secrets.ELEVATED_GITHUB_TOKEN }}"
gh workflow run release.yml \
--repo hashicorp/boundary-ui-releases \
--ref main \
-f TAG="${{ github.event.inputs.branch }}"
+4 -4
View File
@@ -1,4 +1,4 @@
name: "Trigger Boundary UI release"
name: "Trigger Boundary Desktop release"
on:
push:
@@ -9,14 +9,14 @@ jobs:
trigger-gha-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Release description
run: |
echo "Triggering release with a TAG ${GITHUB_REF#refs/tags/}"
- name: Trigger Release workflow in boundary-ui-releases
- name: Trigger Release workflow in boundary-desktop-releases
run: |
export GITHUB_TOKEN="${{ secrets.ELEVATED_GITHUB_TOKEN }}"
gh workflow run release.yml \
--repo hashicorp/boundary-ui-releases \
--repo hashicorp/boundary-desktop-releases \
--ref main \
-f TAG="${GITHUB_REF#refs/tags/}"
-3
View File
@@ -1,5 +1,2 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn compliance:licenses
npx lint-staged
-3
View File
@@ -1,6 +1,3 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
@@ -55,23 +55,37 @@ export default class GeneratedHostCatalogModel extends BaseModel {
})
plugin;
// AWS & Azure
// AWS, Azure & GCP
@attr('boolean', {
for: {
type: 'plugin',
name: ['aws', 'azure', 'gcp'],
},
isNestedAttribute: true,
description: '',
})
disable_credential_rotation;
// AWS specific
// AWS and GCP specific
@attr('string', {
for: {
type: 'plugin',
name: ['aws', 'gcp'],
},
description:
'An expression used to filter the workers that have network access to a service that is hosting the external object store.',
})
worker_filter;
// AWS specific
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
},
isNestedAttribute: true,
description: '',
})
@@ -79,12 +93,22 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS static credentials
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'static-credential',
},
isNestedSecret: true,
description: '',
})
access_key_id;
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'static-credential',
},
isNestedSecret: true,
description: '',
})
@@ -92,24 +116,44 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS dynamic credentials
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'dynamic-credential',
},
isNestedAttribute: true,
description: 'The role ARN to use.',
})
role_arn;
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'dynamic-credential',
},
isNestedAttribute: true,
description: 'The role external ID to use.',
})
role_external_id;
@attr('string', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'dynamic-credential',
},
isNestedAttribute: true,
description: 'The role session to use.',
})
role_session_name;
@attr('object-as-array', {
for: {
type: 'plugin',
name: 'aws',
credentialType: 'dynamic-credential',
},
isNestedAttribute: true,
description: 'The role tags to use.',
})
@@ -118,32 +162,117 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// Azure specific
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
isNestedAttribute: true,
description: '',
})
tenant_id;
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
isNestedAttribute: true,
description: '',
})
client_id;
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
isNestedAttribute: true,
description: '',
})
subscription_id;
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
isNestedSecret: true,
description: '',
})
secret_id;
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
isNestedSecret: true,
description: '',
})
secret_value;
// GCP specific
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
isNestedAttribute: true,
description: 'The project ID associated with the service account.',
})
project_id;
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
isNestedAttribute: true,
description: 'The deployment area within a region.',
})
zone;
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
isNestedAttribute: true,
description:
'The email address associated with the service account. The email address used to uniquely identify the service account. It is required for authentication and authorization.',
})
client_email;
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
isNestedAttribute: true,
description:
'The unique identifier for the service account that will be impersonated.',
})
target_service_account_id;
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
description:
'The ID of the private key used to sign the JWT and authentication.',
isNestedSecret: true,
})
private_key_id;
@attr('string', {
for: {
type: 'plugin',
name: 'gcp',
},
description:
'The private key used to sign the JWT and obtain a OAuth 2.0 access token.',
isNestedSecret: true,
})
private_key;
}
+17 -1
View File
@@ -63,6 +63,10 @@ export default class GeneratedHostSetModel extends BaseModel {
plugin;
@attr('number', {
for: {
type: 'plugin',
name: ['aws', 'azure', 'gcp'],
},
description:
"The number of seconds between the time boundary syncs the hosts in this set using this host set's plugin. If not provided a system determined default is used",
})
@@ -79,12 +83,20 @@ export default class GeneratedHostSetModel extends BaseModel {
host_ids;
@attr('string-array', {
for: {
type: 'plugin',
name: ['aws', 'azure', 'gcp'],
},
emptyArrayIfMissing: true,
})
preferred_endpoints;
// AWS specific
// AWS & GCP specific
@attr('string-array', {
for: {
type: 'plugin',
name: ['aws', 'gcp'],
},
isNestedAttribute: true,
emptyArrayIfMissing: true,
})
@@ -92,6 +104,10 @@ export default class GeneratedHostSetModel extends BaseModel {
// Azure specific, comes in from API as "filter". This is to avoid collisions with filter
@attr('string', {
for: {
type: 'plugin',
name: 'azure',
},
description: '',
isNestedAttribute: true,
})
+12 -3
View File
@@ -8,7 +8,16 @@ import GeneratedCredentialModel from '../generated/models/credential';
/**
* Supported Credential types.
*/
export const types = ['username_password', 'ssh_private_key', 'json'];
export const TYPE_CREDENTIAL_USERNAME_PASSWORD = 'username_password';
export const TYPE_CREDENTIAL_SSH_PRIVATE_KEY = 'ssh_private_key';
export const TYPE_CREDENTIAL_JSON = 'json';
export const TYPES_CREDENTIAL = Object.freeze([
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
TYPE_CREDENTIAL_JSON,
]);
export default class CredentialModel extends GeneratedCredentialModel {
// =attributes
@@ -28,7 +37,7 @@ export default class CredentialModel extends GeneratedCredentialModel {
* @type {boolean}
*/
get isJSON() {
return this.type === 'json';
return this.type === TYPE_CREDENTIAL_JSON;
}
/**
@@ -36,6 +45,6 @@ export default class CredentialModel extends GeneratedCredentialModel {
* @type {boolean}
*/
get isUnknown() {
return !types.includes(this.type);
return !TYPES_CREDENTIAL.includes(this.type);
}
}
+21 -3
View File
@@ -14,9 +14,11 @@ export const TYPES_HOST_CATALOG = Object.freeze([
export const TYPE_HOST_CATALOG_PLUGIN_AWS = 'aws';
export const TYPE_HOST_CATALOG_PLUGIN_AZURE = 'azure';
export const TYPE_HOST_CATALOG_PLUGIN_GCP = 'gcp';
export const TYPES_HOST_CATALOG_PLUGIN = [
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
TYPE_HOST_CATALOG_PLUGIN_GCP,
];
export const TYPE_CREDENTIAL_STATIC = 'static-credential';
@@ -27,6 +29,14 @@ export const TYPES_CREDENTIALS = Object.freeze([
TYPE_CREDENTIAL_DYNAMIC,
]);
export const DYNAMIC_CREDENTIAL_FIELDS = [
'role_arn',
'role_external_id',
'role_session_name',
'role_tags',
];
export const STATIC_CREDENTIAL_FIELDS = ['access_key_id, secret_access_key'];
export default class HostCatalogModel extends GeneratedHostCatalogModel {
// =attributes
@@ -53,7 +63,7 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel {
*/
get credentialType() {
if (!this.#credentialType) {
if (this.role_arn) {
if (DYNAMIC_CREDENTIAL_FIELDS.some((field) => this[field])) {
this.#credentialType = TYPE_CREDENTIAL_DYNAMIC;
} else {
this.#credentialType = TYPE_CREDENTIAL_STATIC;
@@ -85,7 +95,7 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel {
* @type {boolean}
*/
get isAWS() {
return this.compositeType === 'aws';
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_AWS;
}
/**
@@ -93,7 +103,15 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel {
* @type {boolean}
*/
get isAzure() {
return this.compositeType === 'azure';
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE;
}
/**
* True if host catalog plugin type is GCP.
* @type {boolean}
*/
get isGCP() {
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_GCP;
}
/**
+16 -3
View File
@@ -4,7 +4,12 @@
*/
import GeneratedHostSetModel from '../generated/models/host-set';
import { TYPES_HOST_CATALOG_PLUGIN } from './host-catalog';
import {
TYPES_HOST_CATALOG_PLUGIN,
TYPE_HOST_CATALOG_PLUGIN_GCP,
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
} from './host-catalog';
export default class HostSetModel extends GeneratedHostSetModel {
// =attributes
@@ -40,14 +45,22 @@ export default class HostSetModel extends GeneratedHostSetModel {
* @type {boolean}
*/
get isAWS() {
return this.compositeType === 'aws';
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_AWS;
}
/**
* Return if a host-set plugin is Azure or not.
*/
get isAzure() {
return this.compositeType === 'azure';
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE;
}
/**
* True if host catalog plugin type is GCP.
* @type {boolean}
*/
get isGCP() {
return this.compositeType === TYPE_HOST_CATALOG_PLUGIN_GCP;
}
/**
+43 -6
View File
@@ -8,6 +8,9 @@ import { equal } from '@ember/object/computed';
import { tracked } from '@glimmer/tracking';
import { A } from '@ember/array';
import { inject as service } from '@ember/service';
import { flattenObject } from '../utils/flatten-nested-object';
import { TYPES_CREDENTIAL_LIBRARY } from 'api/models/credential-library';
import { TYPE_CREDENTIAL_JSON } from 'api/models/credential';
export const STATUS_SESSION_ACTIVE = 'active';
export const STATUS_SESSION_PENDING = 'pending';
@@ -23,7 +26,7 @@ export const statusTypes = [
/**
*
*/
class SessionCredential {
export class SessionCredential {
// =classes
/**
@@ -34,12 +37,14 @@ class SessionCredential {
name;
description;
type;
credentialtype;
constructor(id, name, description, type) {
constructor(id, name, description, type, credentialtype) {
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.credentialType = credentialtype;
}
};
@@ -75,8 +80,10 @@ class SessionCredential {
get secrets() {
if (this.#payloadSecret?.decoded) {
const secretJSON = this.#payloadSecret.decoded;
return Object.keys(secretJSON).map(
(key) => new SessionCredential.SecretItem(key, secretJSON[key]),
return this.extractSecrets(
secretJSON,
this.source.type,
this.source.credentialType,
);
} else {
// decode from base64
@@ -85,11 +92,41 @@ class SessionCredential {
}
}
/**
* Extracts secrets from the payload secret JSON object.
* We only need to flatten the object if the type is vault or if its a json type credential.
* @param {object} secretJSON - The payload secret JSON object.
* @param {string} type - The credential source type.
* @param {string} credentialType - The credential type.
* @returns {SessionCredential.SecretItem[]} - The array of secret items.
*/
extractSecrets(secretJSON, type, credentialType) {
let source;
if (credentialType === TYPE_CREDENTIAL_JSON) {
source = flattenObject(secretJSON);
} else if (TYPES_CREDENTIAL_LIBRARY.includes(type) && secretJSON?.data) {
source = flattenObject(secretJSON.data);
} else {
source = secretJSON;
}
return Object.entries(source).map(
([key, value]) => new SessionCredential.SecretItem(key, value),
);
}
// =methods
constructor(cred) {
const { id, name, description, type } = cred.credential_source;
this.source = new SessionCredential.Source(id, name, description, type);
const { id, name, description, type, credential_type } =
cred.credential_source;
this.source = new SessionCredential.Source(
id,
name,
description,
type,
credential_type,
);
this.#payloadSecret = cred.secret;
this.rawCredential = cred;
}
+33 -12
View File
@@ -6,6 +6,7 @@
import RESTSerializer from '@ember-data/serializer/rest';
import { underscore } from '@ember/string';
import { get } from '@ember/object';
import { typeOf } from '@ember/utils';
/**
* Manages serialization/normalization of data to/from the API.
@@ -42,7 +43,7 @@ export default class ApplicationSerializer extends RESTSerializer {
*/
serializeAttribute(snapshot, json, key, attribute) {
const { type, options } = attribute;
const { type: recordType } = snapshot.record;
const { type: recordType, compositeType } = snapshot.record;
let value = super.serializeAttribute(...arguments);
// Remove secret attributes that are null or empty
if (options.isSecret) {
@@ -74,18 +75,38 @@ export default class ApplicationSerializer extends RESTSerializer {
delete json[key];
}
// If an attribute has a `for` option, it must match the
// record's `type`, else the attribute is excluded
// record's `type`, otherwise the attribute is excluded
// from serialization.
//note: This doesn't handle secrets yet, we can add support for them if needed.
if (
options?.for &&
options.for !== recordType &&
!options.for.includes(recordType)
) {
if (options.isNestedAttribute) {
delete json?.attributes?.[key];
} else {
delete json[key];
if (options?.for) {
// Check the typeof the `for` option to determine if it is a plugin or not
const isPlugin = options.for?.type === 'plugin';
// Helper function to handle deleting attribute and secret keys
const deleteKey = (json) => {
if (options.isNestedAttribute) {
delete json?.attributes?.[key];
} else if (options.isNestedSecret && json?.secrets?.[key]) {
delete json?.secrets?.[key];
if (Object.keys(json.secrets).length === 0) {
delete json.secrets;
}
} else {
delete json[key];
}
};
// For plugins, the `for` option is an object with a `name` key
// For non-plugins, the `for` option can be string or an array
if (
(isPlugin && !options.for?.name.includes(compositeType)) ||
(!isPlugin &&
!options.for?.includes(recordType) &&
typeOf(options.for) === 'array') ||
(!isPlugin &&
options.for !== recordType &&
typeOf(options.for) === 'string')
) {
deleteKey(json);
}
}
return value;
+20 -76
View File
@@ -4,101 +4,45 @@
*/
import ApplicationSerializer from './application';
import {
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
TYPE_CREDENTIAL_DYNAMIC,
} from '../models/host-catalog';
// These fields are Azure specific
const azureFields = [
'disable_credential_rotation',
'tenant_id',
'client_id',
'subscription_id',
'secret_id',
'secret_value',
];
// These fields are AWS specific, AWS provider now has two credentialTypes: static and dynamic [not to be confused with static and dynamic host-catalogs]
const AWSfieldsWithCredentialType = {
'static-credential': [
'access_key_id',
'secret_access_key',
'region',
'disable_credential_rotation',
'worker_filter',
],
'dynamic-credential': [
'role_arn',
'role_external_id',
'role_session_name',
'role_tags',
'region',
'disable_credential_rotation',
'worker_filter',
],
};
import { TYPE_CREDENTIAL_DYNAMIC } from '../models/host-catalog';
export default class HostCatalogSerializer extends ApplicationSerializer {
serialize(snapshot) {
const { compositeType, credentialType } = snapshot.record;
const { credentialType } = snapshot.record;
const serialized = super.serialize(...arguments);
// This is a hack to remove the worker_filter field from the serialized data if the compositeType is not AWS
// Ideally we should refactor the model to include the compositeType using `for` attribute
if (compositeType !== TYPE_HOST_CATALOG_PLUGIN_AWS) {
delete serialized.worker_filter;
}
// By default, disable credential rotation for dynamic credentials
if (credentialType === TYPE_CREDENTIAL_DYNAMIC) {
serialized.attributes.disable_credential_rotation = true;
}
switch (compositeType) {
case 'static':
return this.serializeStatic(...arguments);
default:
return serialized;
}
return serialized;
}
serializeAttribute(snapshot, json, key, attribute) {
const value = super.serializeAttribute(...arguments);
const { compositeType, credentialType, isPlugin } = snapshot.record;
const { compositeType, credentialType } = snapshot.record;
const { options } = attribute;
const isSamePluginName = options?.for?.name?.includes(compositeType);
const fields =
compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE
? azureFields
: AWSfieldsWithCredentialType[credentialType];
// Delete any nested attribute fields that don't belong to the record type
if (isPlugin && options.isNestedAttribute && json.attributes) {
if (!fields.includes(key)) {
delete json.attributes[key];
}
}
// Delete any secret fields that don't belong to the record type
if (isPlugin && options.isNestedSecret && json.secrets) {
if (!fields.includes(key)) {
// Remove attributes that are not applicable to the current credential type for the same plugin
// For example, 'aws' dynamic plugin has two credential types: 'static' and 'dynamic'
// API expects the fields to be 'null' for the fields that are not from the current credential type (that's why we are not using the application serializer for this, where we delete attrs)
// Empty secrets should be removed from the JSON
if (
isSamePluginName &&
options.for.credentialType &&
options.for.credentialType !== credentialType
) {
if (options.isNestedAttribute) {
json.attributes[key] = null;
} else if (options.isNestedSecret && json.secrets[key]) {
delete json.secrets[key];
if (json['secrets'] && Object.keys(json.secrets).length === 0) {
if (Object.keys(json.secrets).length === 0) {
delete json.secrets;
}
} else {
delete json[key];
}
}
return value;
}
serializeStatic() {
const serialized = super.serialize(...arguments);
// Delete unnecessary fields for static host-catalog
delete serialized.attributes;
delete serialized.secrets;
delete serialized.worker_filter;
return serialized;
}
}
+1 -13
View File
@@ -5,10 +5,6 @@
import ApplicationSerializer from './application';
const fieldByType = {
aws: ['preferred_endpoints', 'filters', 'sync_interval_seconds'],
azure: ['preferred_endpoints', 'filter_string', 'sync_interval_seconds'],
};
export default class HostSetSerializer extends ApplicationSerializer {
// =properties
@@ -63,16 +59,8 @@ export default class HostSetSerializer extends ApplicationSerializer {
return serialized;
}
serializeAttribute(snapshot, json, key, attribute) {
serializeAttribute(snapshot, json, key) {
const value = super.serializeAttribute(...arguments);
const { isPlugin, compositeType } = snapshot.record;
const { options } = attribute;
if (isPlugin && options.isNestedAttribute) {
if (!fieldByType[compositeType].includes(key)) {
delete json.attributes[key];
}
}
if (key === 'filter_string') {
const { filter_string } = json.attributes;
@@ -0,0 +1,27 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { typeOf } from '@ember/utils';
/**
* Flattens a nested object into a single-level object.
* @param obj
* @param result
* @param parentKey
* @returns {Object}
*/
export const flattenObject = (obj, result = {}, parentKey = '') => {
for (const key in obj) {
// Check if the value is an object and it is not null
if (typeOf(obj[key]) === 'object' && obj[key] !== null) {
// Recursively flatten the object
flattenObject(obj[key], result, `${parentKey}${key}.`);
} else if (obj[key] !== null && obj[key] !== '') {
// Only add to result if the value is not null or an empty string
result[`${parentKey}${key}`] = obj[key];
}
}
return result;
};
+41 -10
View File
@@ -8,11 +8,23 @@ import { trait } from 'miragejs';
import permissions from '../helpers/permissions';
import { faker } from '@faker-js/faker';
import generateId from '../helpers/id';
import {
TYPE_HOST_CATALOG_PLUGIN_AZURE,
TYPE_HOST_CATALOG_PLUGIN_GCP,
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_STATIC,
TYPE_HOST_CATALOG_DYNAMIC,
TYPES_HOST_CATALOG,
} from 'api/models/host-catalog';
const types = ['static', 'plugin'];
// Represents known plugin types, except "foobar" which models the possibility
// of receiving an _unknown_ type, which the UI must handle gracefully.
const pluginTypes = ['aws', 'azure', 'foobar'];
const pluginTypes = [
TYPE_HOST_CATALOG_PLUGIN_GCP,
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
'foobar',
];
let pluginTypeCounter = 1;
@@ -20,7 +32,7 @@ export default factory.extend({
id: () => generateId('hc_'),
// Cycle through available types
type: (i) => types[i % types.length],
type: (i) => TYPES_HOST_CATALOG[i % TYPES_HOST_CATALOG.length],
authorized_actions: () =>
permissions.authorizedActionsFor('host-catalog') || [
@@ -30,7 +42,7 @@ export default factory.extend({
'delete',
],
authorized_collection_actions: function () {
const isStatic = this.type === 'static';
const isStatic = this.type === TYPE_HOST_CATALOG_STATIC;
return {
// only static catalogs allow host create at this time
hosts: isStatic ? ['create', 'list'] : ['list'],
@@ -39,13 +51,17 @@ export default factory.extend({
},
worker_filter: function () {
if (this.type === 'plugin' && this.plugin?.name === 'aws') {
if (
this.type === TYPE_HOST_CATALOG_DYNAMIC &&
(this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_AWS ||
this.plugin?.name === TYPE_HOST_CATALOG_PLUGIN_GCP)
) {
return `"${faker.word.noun()}" in "${faker.system.directoryPath()}"`;
}
},
plugin: function (i) {
if (this.type === 'plugin') {
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
return {
id: `plugin-id-${i}`,
name: pluginTypes[pluginTypeCounter++ % pluginTypes.length],
@@ -56,35 +72,50 @@ export default factory.extend({
attributes() {
switch (this.plugin?.name) {
case 'aws':
case TYPE_HOST_CATALOG_PLUGIN_AWS:
return {
disable_credential_rotation: faker.datatype.boolean(),
region: `us-${faker.location.cardinalDirection()}-${faker.number.int(
9,
)}`,
};
case 'azure':
case TYPE_HOST_CATALOG_PLUGIN_AZURE:
return {
disable_credential_rotation: faker.datatype.boolean(),
tenant_id: faker.string.uuid(),
client_id: faker.string.uuid(),
subscription_id: faker.string.uuid(),
};
case TYPE_HOST_CATALOG_PLUGIN_GCP:
return {
disable_credential_rotation: faker.datatype.boolean(),
project_id: faker.string.uuid(),
client_email: faker.internet.email(),
target_service_account_id: faker.string.uuid(),
zone: `us-${faker.location.cardinalDirection()}${faker.number.int(
9,
)}-${faker.string.fromCharacters('abc')}`,
};
}
},
secrets() {
switch (this.plugin?.name) {
case 'aws':
case TYPE_HOST_CATALOG_PLUGIN_AWS:
return {
access_key_id: faker.string.nanoid(),
secret_access_key: faker.string.nanoid(),
};
case 'azure':
case TYPE_HOST_CATALOG_PLUGIN_AZURE:
return {
secret_id: faker.string.nanoid(),
secret_value: faker.string.nanoid(),
};
case TYPE_HOST_CATALOG_PLUGIN_GCP:
return {
private_key: faker.string.nanoid(),
private_key_id: faker.string.nanoid(),
};
}
},
withChildren: trait({
+25 -16
View File
@@ -7,6 +7,22 @@ import factory from '../generated/factories/host-set';
import permissions from '../helpers/permissions';
import { faker } from '@faker-js/faker';
import generateId from '../helpers/id';
import {
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_HOST_CATALOG_PLUGIN_AZURE,
TYPE_HOST_CATALOG_PLUGIN_GCP,
} from 'api/models/host-catalog';
function generateFilters(prefix) {
const filtersAmount = faker.number.int({ min: 1, max: 5 });
let filters = [];
for (let i = 0; i < filtersAmount; ++i) {
filters.push(`${prefix}${faker.word.words(1)}=${faker.word.words(1)}`);
}
return {
filters,
};
}
export default factory.extend({
id: () => generateId('hs_'),
@@ -42,22 +58,15 @@ export default factory.extend({
},
attributes() {
// AWS specific
if (this.plugin?.name === 'aws') {
const filtersAmount = faker.number.int({ min: 1, max: 5 });
let filters = [];
for (let i = 0; i < filtersAmount; ++i) {
filters.push(`${faker.word.words(1)}=${faker.word.words(1)}`);
}
return {
filters,
};
}
// Azure specific
if (this.plugin?.name === 'azure') {
return {
filter: `${faker.database.column()}=${faker.database.collation()}`,
};
switch (this.plugin?.name) {
case TYPE_HOST_CATALOG_PLUGIN_AWS:
return generateFilters('tag:');
case TYPE_HOST_CATALOG_PLUGIN_AZURE:
return {
filter: `${faker.database.column()}=${faker.database.collation()}`,
};
case TYPE_HOST_CATALOG_PLUGIN_GCP:
return generateFilters('labels.');
}
},
});
+9 -5
View File
@@ -7,24 +7,28 @@ import factory from '../generated/factories/host';
import permissions from '../helpers/permissions';
import { faker } from '@faker-js/faker';
import generateId from '../helpers/id';
import {
TYPE_HOST_CATALOG_STATIC,
TYPE_HOST_CATALOG_DYNAMIC,
} from 'api/models/host-catalog';
export default factory.extend({
id: () => generateId('h_'),
name() {
if (this.type === 'static') return faker.word.words();
if (this.type === TYPE_HOST_CATALOG_STATIC) return faker.word.words();
},
type() {
return this.hostCatalog?.type || factory.attrs.type;
},
authorized_actions: function () {
const isStatic = this.type === 'static';
const isStatic = this.type === TYPE_HOST_CATALOG_STATIC;
const defaults = ['no-op', 'read'];
// Only static allows update/delete at this time.
if (isStatic) defaults.push('update', 'delete');
return permissions.authorizedActionsFor('host') || defaults;
},
plugin() {
if (this.type === 'plugin') {
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
return this.hostCatalog.plugin;
}
},
@@ -38,12 +42,12 @@ export default factory.extend({
},
// Return external fields only for plugins
external_id() {
if (this.type === 'plugin') {
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
return faker.string.uuid();
}
},
external_name(i) {
if (this.type === 'plugin') {
if (this.type === TYPE_HOST_CATALOG_DYNAMIC) {
return `${this.plugin.name} provided name ${i}`;
}
},
+1 -1
View File
@@ -15,7 +15,7 @@ export default factory.extend({
authorized_actions() {
const authorizedActions = ['no-op', 'read', 'update'];
// Storage policies can only be attatched to global and org scopes.
// Storage policies can only be attached to global and org scopes.
if (this.type === 'global' || this.type === 'org') {
authorizedActions.push('attach-storage-policy', 'detach-storage-policy');
}
-1
View File
@@ -4,7 +4,6 @@
*/
// import config from '../../config/environment';
// import { v1 } from 'ember-uuid';
import { Response } from 'miragejs';
// /**
+32
View File
@@ -83,6 +83,37 @@ export default function initializeMockIPC(server, config) {
raw: btoa('just-a-random-string'),
},
},
{
credential_source: {
id: 'clvlt_4cvscMTl0N',
name: 'Credential Library 0',
description: 'Source Description',
credential_store_id: 'csvlt_Q1HFGt7Jpm',
type: 'vault-generic',
},
secret: {
raw: 'eyJhcnJheSI6WyJvbmUiLCJ0d28iLCJ0aHJlZSIsIm9uZSIsInR3byIsInRocmVlIiwib25lIiwidHdvIiwidGhyZWUiLCJvbmUiLCJ0d28iLCJ0aHJlZSJdLCJuZXN0ZWQiOnsiYm9vbCI6dHJ1ZSwibG9uZyI6IjEyMjM1MzQ1NmFzZWRmYTQzd3J0ZjIzNGYyM2FzZGdmYXNkZnJnYXdzZWZhd3NlZnNkZjQiLCJzZWNlcmV0Ijoic28gbmVzdGVkIn0sInRlc3QiOiJwaHJhc2UifQ',
decoded: {
data: {
backslash: 'password\\withslash\\',
backslash1: 'password\\fwithslash\\f',
password: '123',
username: 'test',
email: {
address: 'test.com',
},
},
metadata: {
created_time: '2024-04-12T18:38:36.226715555Z',
custom_metadata: null,
deletion_time: '',
destroyed: false,
version: 8,
},
},
},
},
{
credential_source: {
id: 'credjson_7cKBbBEkC3',
@@ -98,6 +129,7 @@ export default function initializeMockIPC(server, config) {
nested_secret: {
session_token: 'ZXCVBNMLKJHGFDSAQWERTYUIOP0987654321',
complex_nest: {
blackslash: 'password\\withslash\\',
hash: 'qazxswedcvfrtgbnjhyujm.1234567890',
},
},
+6 -9
View File
@@ -24,7 +24,7 @@
},
"dependencies": {
"dexie": "^3.2.4",
"ember-auto-import": "^2.6.3",
"ember-auto-import": "^2.8.1",
"ember-can": "^4.2.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.2.0",
@@ -39,15 +39,15 @@
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^3.3.1",
"@ember/test-helpers": "^4.0.4",
"@embroider/test-setup": "^2.1.1",
"@faker-js/faker": "^8.0.2",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"concurrently": "^9.1.0",
"doctoc": "^2.2.0",
"ember-cli": "^4.12.2",
"ember-cli": "^5.12.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
@@ -68,13 +68,10 @@
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
"webpack": "^5.94.0"
},
"peerDependencies": {
"ember-source": ">4.0.0"
},
"engines": {
"node": "18.* || 20.*"
},
@@ -81,6 +81,19 @@ module('Unit | Model | host catalog', function (hooks) {
assert.false(modelRandom.isAzure);
});
test('it has isGCP property and return the expected values', async function (assert) {
const store = this.owner.lookup('service:store');
const modelGCP = store.createRecord('host-catalog', {
type: 'plugin',
plugin: { name: 'gcp' },
});
const modelRandom = store.createRecord('host-catalog', {
plugin: { name: 'random' },
});
assert.true(modelGCP.isGCP);
assert.false(modelRandom.isGCP);
});
test('get compositeType returns expected values', async function (assert) {
const store = this.owner.lookup('service:store');
const modelA = store.createRecord('host-catalog', {
@@ -94,8 +107,13 @@ module('Unit | Model | host catalog', function (hooks) {
type: 'plugin',
plugin: { name: 'no-such-type' },
});
const modelD = store.createRecord('host-catalog', {
type: 'plugin',
plugin: { name: 'gcp' },
});
assert.strictEqual(modelA.compositeType, 'static');
assert.strictEqual(modelB.compositeType, 'aws');
assert.strictEqual(modelD.compositeType, 'gcp');
assert.strictEqual(modelC.compositeType, 'unknown');
});
@@ -107,8 +125,12 @@ module('Unit | Model | host catalog', function (hooks) {
const modelStatic = store.createRecord('host-catalog', {
compositeType: 'static',
});
const modelGCP = store.createRecord('host-catalog', {
compositeType: 'gcp',
});
assert.strictEqual(modelPlugin.type, 'plugin');
assert.strictEqual(modelPlugin.plugin.name, 'aws');
assert.strictEqual(modelStatic.type, 'static');
assert.strictEqual(modelGCP.plugin.name, 'gcp');
});
});
@@ -5,6 +5,8 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { SessionCredential } from 'api/models/session';
import { TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC } from 'api/models/credential-library';
module('Unit | Model | session', function (hooks) {
setupTest(hooks);
@@ -16,6 +18,46 @@ module('Unit | Model | session', function (hooks) {
assert.notOk(model.isAvailable);
});
test('`extractSecrets` method should return an array of secret items', function (assert) {
const sessionCredential = new SessionCredential({
credential_source: {
id: '123',
name: 'Test Source',
description: 'Test Description',
type: 'Test Type',
},
});
const secrets = {
username: 'user',
password: 'pass',
};
const formattedSecretes = sessionCredential.extractSecrets(
secrets,
TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
);
assert.deepEqual(formattedSecretes, [
new SessionCredential.SecretItem('username', 'user'),
new SessionCredential.SecretItem('password', 'pass'),
]);
// check for nested data, empty values should be filtered out
const nestedSecrets = {
data: { username: 'user', password: '', email: 'test.com' },
};
const formattedNestedSecrets = sessionCredential.extractSecrets(
nestedSecrets,
TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
);
assert.deepEqual(formattedNestedSecrets, [
new SessionCredential.SecretItem('username', 'user'),
new SessionCredential.SecretItem('email', 'test.com'),
]);
});
test('it allows cancellation of an active session', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('session', {
@@ -6,6 +6,7 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method';
import { TYPE_HOST_CATALOG_PLUGIN_GCP } from 'api/models/host-catalog';
module('Unit | Serializer | application', function (hooks) {
setupTest(hooks);
@@ -133,7 +134,7 @@ module('Unit | Serializer | application', function (hooks) {
});
});
test('it serializes attributes with `for` option containing array or string correctly', function (assert) {
test('it serializes attributes with `for` option containing array or string correctly for non plugins', function (assert) {
let store = this.owner.lookup('service:store');
let record = store.createRecord('auth-method', {
type: TYPE_AUTH_METHOD_OIDC, //has both string and array `for` option
@@ -184,6 +185,54 @@ module('Unit | Serializer | application', function (hooks) {
});
});
test('it serializes attributes with `for` option containing array or string correctly for plugin types', function (assert) {
let store = this.owner.lookup('service:store');
let record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_GCP,
name: 'GCP plugin',
description: 'this is a GCP plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
access_key_id: 'foobars',
secret_access_key: 'testing',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
role_external_id: 'test',
role_session_name: 'test',
role_tags: [
{ key: 'Project', value: 'Automation' },
{ key: 'foo', value: 'bar' },
],
project_id: 'project',
zone: 'zone',
client_email: 'email',
target_service_account_id: 'service-account',
private_key_id: 'key-id',
private_key: 'key',
});
let serializedRecord = record.serialize();
const expectedResult = {
name: 'GCP plugin',
description: 'this is a GCP plugin host-catalog',
type: 'plugin',
worker_filter: 'workerfilter',
attributes: {
disable_credential_rotation: true,
project_id: 'project',
zone: 'zone',
target_service_account_id: 'service-account',
client_email: 'email',
},
secrets: {
private_key_id: 'key-id',
private_key: 'key',
},
};
assert.deepEqual(serializedRecord, expectedResult);
});
test('it normalizes array records from an `items` root key', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('user');
@@ -9,6 +9,7 @@ import {
TYPE_HOST_CATALOG_PLUGIN_AWS,
TYPE_CREDENTIAL_STATIC,
TYPE_CREDENTIAL_DYNAMIC,
TYPE_HOST_CATALOG_PLUGIN_GCP,
} from 'api/models/host-catalog';
module('Unit | Serializer | host catalog', function (hooks) {
@@ -32,6 +33,53 @@ module('Unit | Serializer | host catalog', function (hooks) {
name: 'static',
description: 'this is a static host-catalog',
type: 'static',
attributes: {},
};
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new gcp plugin as expected', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_GCP,
name: 'GCP',
description: 'this is a GCP plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
access_key_id: 'foobars',
secret_access_key: 'testing',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
role_external_id: 'test',
role_session_name: 'test',
role_tags: [
{ key: 'Project', value: 'Automation' },
{ key: 'foo', value: 'bar' },
],
project_id: 'project',
zone: 'zone',
client_email: 'email',
target_service_account_id: 'service-account',
private_key_id: 'key-id',
private_key: 'key',
});
const expectedResult = {
name: 'GCP',
description: 'this is a GCP plugin host-catalog',
type: 'plugin',
worker_filter: 'workerfilter',
attributes: {
disable_credential_rotation: true,
project_id: 'project',
zone: 'zone',
target_service_account_id: 'service-account',
client_email: 'email',
},
secrets: {
private_key_id: 'key-id',
private_key: 'key',
},
};
assert.deepEqual(record.serialize(), expectedResult);
});
@@ -45,14 +93,9 @@ module('Unit | Serializer | host catalog', function (hooks) {
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
// these are AWS fields and should be included
region: 'west',
access_key_id: 'foobars',
secret_access_key: 'testing',
// these are Azure fields and should be excluded
tenant_id: 'a1b2c3',
client_id: 'a1b2c3',
subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
@@ -71,6 +114,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: true,
region: 'west',
role_arn: null,
role_external_id: null,
role_session_name: null,
role_tags: null,
},
secrets: {
access_key_id: 'foobars',
@@ -80,7 +127,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new aws dynamic plugin as expected', async function (assert) {
test('it serializes a new aws dynamic credential type plugin as expected', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_AWS,
@@ -89,7 +136,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
// these are AWS fields and should be included
// these are static AWS fields and should be excluded
region: 'west',
access_key_id: 'foobars',
secret_access_key: 'testing',
@@ -124,7 +171,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new aws static plugin as expected, ignoring azure fields', async function (assert) {
test('it serializes a new aws static credential type plugin as expected, ignoring azure and GCP fields', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: TYPE_HOST_CATALOG_PLUGIN_AWS,
@@ -143,6 +190,8 @@ module('Unit | Serializer | host catalog', function (hooks) {
subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
project_id: 'project',
zone: 'zone',
});
const expectedResult = {
name: 'AWS',
@@ -152,6 +201,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: true,
region: 'spain',
role_arn: null,
role_external_id: null,
role_session_name: null,
role_tags: null,
},
secrets: {
access_key_id: 'foobars',
@@ -161,7 +214,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
assert.deepEqual(record.serialize(), expectedResult);
});
test('it serializes a new azure record as expected, ignoring aws fields', async function (assert) {
test('it serializes a new azure record as expected, ignoring aws and GCP fields', async function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('host-catalog', {
compositeType: 'azure',
@@ -178,6 +231,8 @@ module('Unit | Serializer | host catalog', function (hooks) {
subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
project_id: 'project',
zone: 'zone',
});
const expectedResult = {
name: 'Azure',
@@ -225,6 +280,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: false,
region: 'andorra',
role_arn: null,
role_external_id: null,
role_session_name: null,
role_tags: null,
},
};
assert.deepEqual(record.serialize(), expectedResult);
@@ -264,7 +323,7 @@ module('Unit | Serializer | host catalog', function (hooks) {
assert.deepEqual(record.serialize(), expectedResult);
});
test('it normalizes an static host catalog as expected', function (assert) {
test('it normalizes a static host catalog as expected', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('host-catalog');
const hostCatalog = store.createRecord('host-catalog').constructor;
@@ -293,6 +352,8 @@ module('Unit | Serializer | host catalog', function (hooks) {
secret_access_key: null,
secret_id: null,
secret_value: null,
private_key_id: null,
private_key: null,
},
relationships: {},
},
@@ -351,6 +412,8 @@ module('Unit | Serializer | host catalog', function (hooks) {
secret_access_key: 'zq{2:IVc8@W^',
secret_id: null,
secret_value: null,
private_key_id: null,
private_key: null,
},
relationships: {},
},
@@ -411,6 +474,70 @@ module('Unit | Serializer | host catalog', function (hooks) {
subscription_id: 'subscription',
access_key_id: null,
secret_access_key: null,
private_key_id: null,
private_key: null,
},
relationships: {},
},
included: [],
});
});
test('it normalizes a GCP plugin type host catalog as expected', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('host-catalog');
const hostCatalog = store.createRecord('host-catalog').constructor;
const payload = {
id: '1',
name: 'Host catalog test',
description: 'Test description',
authorized_actions: ['no-op', 'read', 'update', 'delete'],
type: 'plugin',
plugin: {
id: 'plugin-id-7',
name: 'gcp',
description: 'gcp host catalog',
},
attributes: {
disable_credential_rotation: true,
project_id: 'project_id',
zone: 'zone',
target_service_account_id: 'target_service_account_id',
},
secrets: {
private_key_id: '0xF3B0a6f8',
private_key: 'zq{2:IVc8@W^',
},
};
const normalized = serializer.normalizeSingleResponse(
store,
hostCatalog,
payload,
);
assert.deepEqual(normalized, {
data: {
id: '1',
type: 'host-catalog',
attributes: {
name: 'Host catalog test',
type: 'plugin',
description: 'Test description',
authorized_actions: ['no-op', 'read', 'update', 'delete'],
plugin: {
id: 'plugin-id-7',
name: 'gcp',
description: 'gcp host catalog',
},
disable_credential_rotation: true,
private_key_id: '0xF3B0a6f8',
private_key: 'zq{2:IVc8@W^',
project_id: 'project_id',
zone: 'zone',
target_service_account_id: 'target_service_account_id',
access_key_id: null,
secret_access_key: null,
secret_id: null,
secret_value: null,
},
relationships: {},
},
@@ -12,7 +12,6 @@ class SessionService extends Service {}
module('Unit | Service | indexed-db', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
this.owner.register('service:session', SessionService);
@@ -9,7 +9,6 @@ import { setupTest } from 'dummy/tests/helpers';
module('Unit | Service | store', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let service = this.owner.lookup('service:store');
assert.ok(service);
@@ -0,0 +1,47 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { flattenObject } from 'api/utils/flatten-nested-object';
import { module, test } from 'qunit';
module('Unit | Utility | flatten-nested-object', function () {
test('it flattens a nested object to single level object', function (assert) {
const input = {
a: 1,
b: { c: 2, d: 3, e: { f: { g: { h: 10 } } } },
};
const expected = {
a: 1,
'b.c': 2,
'b.d': 3,
'b.e.f.g.h': 10,
};
assert.deepEqual(flattenObject(input), expected);
});
test('removes keys with null/empty values', function (assert) {
const inputOne = {
a: null,
b: { c: 2 },
d: {
e: { f: { g: { h: '' } } },
},
i: 0,
j: false,
};
const inputTwo = {
d: { e: null },
};
const expectedResultOne = {
'b.c': 2,
i: 0,
j: false,
};
const expectedResultTwo = {};
assert.deepEqual(flattenObject(inputOne), expectedResultOne);
assert.deepEqual(flattenObject(inputTwo), expectedResultTwo);
});
});
+7
View File
@@ -58,6 +58,7 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator {
*/
async validateToken(token, tokenID) {
const tokenValidationURL = this.buildTokenValidationEndpointURL(tokenID);
// Note: waitForPromise is needed to provide the necessary integration with @ember/test-helpers
// visit https://www.npmjs.com/package/@ember/test-waiters for more info.
const response = await waitForPromise(
@@ -66,6 +67,12 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator {
headers: { Authorization: `Bearer ${token}` },
}),
);
// Note: Always consume response body in order to avoid memory leaks
// visit https://undici.nodejs.org/#/?id=garbage-collection for more info.
// We do not use the undici package but the link informs us that garbage
// collection is undefined when response body is not consumed.
await response.json();
// 401 and 404 responses mean the token is invalid, whereas other types of
// error responses do not tell us about the validity of the token.
if (response.status === 401 || response.status === 404) return reject();
+8 -2
View File
@@ -9,7 +9,7 @@ import { reject } from 'rsvp';
import { waitForPromise } from '@ember/test-waiters';
/**
* The OIDC base authenticator encapsulates the multistep OIDC flow.
* The OIDC base authenticator encapsulates the multi-step OIDC flow.
*
* 1. Start authentication flow: this step is actually a combination of two
* sub steps:
@@ -97,13 +97,19 @@ export default class OIDCAuthenticator extends BaseAuthenticator {
});
// Fetch the endpoint and get the response JSON
const response = await waitForPromise(fetch(url, { method: 'post', body }));
// Note: Always consume response body in order to avoid memory leaks
// visit https://undici.nodejs.org/#/?id=garbage-collection for more info.
// We do not use the undici package but the link informs us that garbage
// collection is undefined when response body is not consumed.
const json = await response.json();
if (response.status === 202) {
// The token isn't ready yet, keep trying.
return false;
} else if (response.status < 400) {
// Response was successful, meaning a token was obtained.
// Authenticate with the session service using the response JSON.
const json = await response.json();
await this.session.authenticate('authenticator:oidc', json);
return true;
} else {
+9 -8
View File
@@ -21,30 +21,31 @@
"doc:toc": "doctoc README.md"
},
"dependencies": {
"ember-auto-import": "2.8.1",
"ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.2.0",
"ember-simple-auth": "^4.2.2"
"ember-simple-auth": "^6.1.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.21.3",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^3.3.1",
"@ember/test-helpers": "^4.0.4",
"@ember/test-waiters": "^3.1.0",
"@embroider/test-setup": "^2.1.1",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"concurrently": "^9.1.0",
"doctoc": "^2.2.0",
"ember-auto-import": "^2.6.3",
"ember-cli": "^4.12.2",
"ember-cli": "^5.12.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-cookies": "^0.5.2",
"ember-cookies": "^1.2.0",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-export-application-global": "^2.0.1",
"ember-load-initializers": "^2.1.2",
@@ -62,8 +63,8 @@
"loader.js": "^4.7.0",
"pretender": "^3.4.3",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
"webpack": "^5.94.0"
},
"engines": {
@@ -52,7 +52,7 @@ module('Unit | Authenticator | base', function (hooks) {
const authenticator = this.owner.lookup('authenticator:base');
server.get(authenticator.buildTokenValidationEndpointURL(id), () => {
assert.ok(true, 'token validation was requested');
return [200];
return [200, {}, '{}'];
});
await authenticator.restore(mockData);
});
@@ -64,7 +64,7 @@ module('Unit | Authenticator | base', function (hooks) {
const authenticator = this.owner.lookup('authenticator:base');
server.get(authenticator.buildTokenValidationEndpointURL(id), () => {
assert.ok(true, 'token validation was requested');
return [401];
return [401, {}, '{}'];
});
try {
await authenticator.restore(mockData);
@@ -80,7 +80,7 @@ module('Unit | Authenticator | base', function (hooks) {
const authenticator = this.owner.lookup('authenticator:base');
server.get(authenticator.buildTokenValidationEndpointURL(id), () => {
assert.ok(true, 'token validation was requested');
return [404];
return [404, {}, '{}'];
});
try {
await authenticator.restore(mockData);
@@ -3,16 +3,21 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Rose::Message
@title={{t (concat 'errors.' this.messageCode '.title')}}
@subtitle={{concat (t 'errors.generic.title') ' ' @status}}
@icon={{this.icon}}
as |message|
>
<message.description>
{{t (concat 'errors.' this.messageCode '.description')}}
</message.description>
{{#if this.helpRoute}}
<message.link @route={{this.helpRoute}}>{{t 'actions.help'}}</message.link>
<Hds::ApplicationState data-test-error-application-state as |A|>
<A.Header
@title={{t (concat 'errors.' this.messageCode '.title')}}
@icon={{this.icon}}
@errorCode={{@status}}
/>
<A.Body @text={{t (concat 'errors.' this.messageCode '.description')}} />
{{#if @docLink}}
<A.Footer as |F|>
<F.LinkStandalone
@icon='help'
@text={{t 'actions.help'}}
@href={{@docLink}}
@iconPosition='trailing'
/>
</A.Footer>
{{/if}}
</Rose::Message>
</Hds::ApplicationState>
@@ -3,8 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import MessageComponent from 'rose/components/rose/message';
import { computed } from '@ember/object';
import Component from '@glimmer/component';
/*
* Helpful error booleans are attached based on the error status code:
@@ -22,40 +21,29 @@ import { computed } from '@ember/object';
const statuses = ['401', '403', '404', '500'];
export default class ErrorMessageComponent extends MessageComponent {
export default class ErrorMessageComponent extends Component {
// =methods
/**
* Returns an icon for error status.
* @return {string}
*/
@computed('args.status')
get icon() {
switch (this.args.status) {
case '404':
return 'flight-icons/svg/help-16';
return 'help';
case '401':
case '403':
default:
return 'flight-icons/svg/alert-circle-16';
return 'alert-circle';
}
}
// TODO: Enable when help documentation can be linked in.
/**
* Returns an route string for help text in message.
* @return {string}
*/
// get helpRoute() {
// return 'index'
// }
/**
* Returns 'unknown' status code when provided error status
* isn't part of predefined statuses.
* @return {string}
*/
@computed('args.status')
get messageCode() {
let messageCode = this.args.status;
if (!statuses.includes(messageCode)) messageCode = 'unknown';
-69
View File
@@ -1,69 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { join } from '@ember/runloop';
import { runInDebug, assert } from '@ember/debug';
import { A } from '@ember/array';
/**
* This helper is a modern update on ember-route-action-helper, which is
* now unmaintained.
*
* See https://github.com/DockYard/ember-route-action-helper
*/
export default class RouteActionHelper extends Helper {
// =services
@service router;
// =attributes
get routes() {
const { currentInfos, mapBy } = this.currentInfos;
return A(currentInfos).mapBy(mapBy).reverse();
}
get currentInfos() {
// eslint-disable-next-line ember/no-private-routing-service
const routerLib = this.router._router._routerMicrolib;
return {
currentInfos:
routerLib.currentRouteInfos || routerLib.currentHandlerInfos,
mapBy: (routerLib.currentRouteInfos && 'route') || 'handler',
};
}
// =methods
compute([actionName, ...outerArgs]) {
const { action, handler } = this.getRouteWithAction(actionName);
runInDebug(() => {
assert(
`
[boundary-core-helper-route-action]
Unable to find action ${actionName}
`,
handler,
);
});
return (...innerArgs) =>
join(handler, action, ...outerArgs.concat(innerArgs));
}
getRouteWithAction(actionName) {
let action;
const handler = A(this.routes).find((route) => {
const actions = route.actions || route._actions;
action = actions[actionName];
return typeof action === 'function';
});
return { action, handler };
}
}
+7
View File
@@ -65,3 +65,10 @@
overflow: hidden;
}
}
// Utility classes
.container-inputs__margin-bottom {
input:not(:last-child) {
margin-bottom: 0.5rem;
}
}
-6
View File
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'core/helpers/route-action';
+9 -12
View File
@@ -34,16 +34,17 @@
"@formatjs/intl-durationformat": "^0.2.4",
"api": "*",
"auth": "*",
"ember-auto-import": "^2.6.3",
"ember-auto-import": "^2.8.1",
"ember-browser-services": "^4.0.3",
"ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.2.0",
"ember-cli-sass": "^11.0.1",
"ember-feature-flags": "^6.0.0",
"ember-intl": "^7.0.5",
"ember-intl": "^7.0.7",
"ember-loading": "^2.0.0",
"ember-truth-helpers": "^3.0.0",
"filesize": "^10.0.7",
"lodash": "^4.17.21",
"sass": "^1.69.5"
},
"devDependencies": {
@@ -51,15 +52,15 @@
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^3.3.1",
"@ember/test-helpers": "^4.0.4",
"@embroider/test-setup": "^2.1.1",
"@faker-js/faker": "^8.0.2",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"concurrently": "^9.1.0",
"doctoc": "^2.2.0",
"ember-cli": "^4.12.2",
"ember-cli": "^5.12.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
@@ -81,16 +82,12 @@
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
"rose": "*",
"sinon": "^18.0.0",
"sinon": "^19.0.2",
"webpack": "^5.94.0"
},
"peerDependencies": {
"ember-source": "^4.0.0",
"lodash": "^4.17.21"
},
"engines": {
"node": "18.* || 20.*"
},
@@ -5,7 +5,7 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find } from '@ember/test-helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupIntl } from 'ember-intl/test-support';
@@ -13,61 +13,65 @@ module('Integration | Component | error/message', function (hooks) {
setupRenderingTest(hooks);
setupIntl(hooks, 'en-us');
const ERROR_MSG_TITLE =
'[data-test-error-application-state] .hds-application-state__title';
const ERROR_MSG_CODE =
'[data-test-error-application-state] .hds-application-state__error-code';
const ERROR_MSG_BODY =
'[data-test-error-application-state] .hds-application-state__body-text';
const ERROR_MSG_LINK =
'[data-test-error-application-state] .hds-link-standalone';
const ERROR_MSG_LINK_TEXT =
'[data-test-error-application-state] .hds-link-standalone__text';
test('it renders predefined message for known error status', async function (assert) {
await render(hbs`<Error::Message @status='401' />`);
assert.strictEqual(
find('.rose-message-title').textContent.trim(),
'You are not signed in',
);
assert.strictEqual(
find('.rose-message-subtitle').textContent.trim(),
'Error 401',
);
assert.strictEqual(
find('.rose-message-description').textContent.trim(),
'You are not signed in. Please sign in and try again later.',
);
assert.dom(ERROR_MSG_TITLE).hasText('You are not signed in');
assert.dom(ERROR_MSG_CODE).hasText('ERROR 401');
assert
.dom(ERROR_MSG_BODY)
.hasText('You are not signed in. Please sign in and try again later.');
await render(hbs`<Error::Message @status='403' />`);
assert.strictEqual(
find('.rose-message-title').textContent.trim(),
'You are not authorized',
);
assert.strictEqual(
find('.rose-message-subtitle').textContent.trim(),
'Error 403',
);
assert.strictEqual(
find('.rose-message-description').textContent.trim(),
'You must be granted permissions to view this data. Ask your administrator if you think you should have access.',
);
assert.dom(ERROR_MSG_TITLE).hasText('You are not authorized');
assert.dom(ERROR_MSG_CODE).hasText('ERROR 403');
assert
.dom(ERROR_MSG_BODY)
.hasText(
'You must be granted permissions to view this data. Ask your administrator if you think you should have access.',
);
await render(hbs`<Error::Message @status='404' />`);
assert.strictEqual(
find('.rose-message-title').textContent.trim(),
'Resource not found',
);
assert.strictEqual(
find('.rose-message-subtitle').textContent.trim(),
'Error 404',
);
assert.strictEqual(
find('.rose-message-description').textContent.trim(),
'We could not find the requested resource. You can ask your administrator or try again later.',
);
assert.dom(ERROR_MSG_TITLE).hasText('Resource not found');
assert.dom(ERROR_MSG_CODE).hasText('ERROR 404');
assert
.dom(ERROR_MSG_BODY)
.hasText(
'We could not find the requested resource. You can ask your administrator or try again later.',
);
});
test('it renders default error for unknown error status', async function (assert) {
await render(hbs`<Error::Message @status='599' />`);
assert.strictEqual(
find('.rose-message-title').textContent.trim(),
'Something went wrong',
);
assert.strictEqual(
find('.rose-message-subtitle').textContent.trim(),
'Error 599',
);
assert.strictEqual(
find('.rose-message-description').textContent.trim(),
"We're not sure what happened. Please contact your administrator or try again later.",
assert.dom(ERROR_MSG_TITLE).hasText('Something went wrong');
assert.dom(ERROR_MSG_CODE).hasText('ERROR 599');
assert
.dom(ERROR_MSG_BODY)
.hasText(
"We're not sure what happened. Please contact your administrator or try again later.",
);
});
test('it renders help link with provided doc url', async function (assert) {
await render(
hbs`<Error::Message @status='599' @docLink='https://support' />`,
);
assert.dom(ERROR_MSG_LINK_TEXT).hasText('Need help');
assert.dom(ERROR_MSG_LINK).hasAttribute('href', 'https://support');
});
});
@@ -15,7 +15,7 @@ module('Integration | Component | session-status', function (hooks) {
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'active' });
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-check'));
assert.ok(find('.hds-icon-check'));
assert.ok(find('.hds-badge--color-success'));
assert.ok(find('.hds-badge--type-filled'));
});
@@ -24,7 +24,7 @@ module('Integration | Component | session-status', function (hooks) {
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'canceling' });
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-alert-triangle'));
assert.ok(find('.hds-icon-alert-triangle'));
assert.ok(find('.hds-badge--color-warning'));
assert.ok(find('.hds-badge--type-filled'));
});
@@ -33,7 +33,7 @@ module('Integration | Component | session-status', function (hooks) {
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'terminated' });
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-x'));
assert.ok(find('.hds-icon-x'));
assert.ok(find('.hds-badge--color-critical'));
assert.ok(find('.hds-badge--type-filled'));
});
@@ -42,7 +42,7 @@ module('Integration | Component | session-status', function (hooks) {
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'pending' });
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.ok(find('.flight-icon-delay'));
assert.ok(find('.hds-icon-delay'));
assert.ok(find('.hds-badge--color-neutral'));
assert.ok(find('.hds-badge--type-filled'));
});
@@ -51,7 +51,7 @@ module('Integration | Component | session-status', function (hooks) {
const store = this.owner.lookup('service:store');
this.model = store.createRecord('session', { status: 'any string' });
await render(hbs`<SessionStatus @model={{this.model}} />`);
assert.notOk(find('.flight-icon'));
assert.notOk(find('.hds-icon'));
assert.ok(find('.hds-badge--color-neutral'));
assert.ok(find('.hds-badge--type-outlined'));
});
@@ -30,7 +30,6 @@ module('Unit | Initializer | deprecations', function (hooks) {
run(this.application, 'destroy');
});
// TODO: Replace this with your real tests.
test('it works', async function (assert) {
await this.application.boot();
@@ -9,7 +9,6 @@ import { setupTest } from 'ember-qunit';
module('Unit | Service | clock-tick', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let service = this.owner.lookup('service:clock-tick');
assert.ok(service);
+3 -2
View File
@@ -8,7 +8,6 @@ delete: Delete
remove: Remove
discard: Discard
disconnect: Disconnect
dismiss: Dismiss
save: Save
submit: Submit
manage: Manage
@@ -16,7 +15,7 @@ confirm: Confirm
done: Done
ok: OK
deny: Deny
help: Need help?
help: Need help
next: Next
back: Back
view: View
@@ -64,3 +63,5 @@ narrow-results: Narrow results
overflow-options: Overflow Options
show-errors: Show Errors
hide-errors: Hide Errors
edit-worker-filter: Edit Worker Filter
add-worker-filter: Add Worker Filter
+25
View File
@@ -44,6 +44,8 @@ titles:
not-set: Not set
year: '{numberOfYears, plural, =1 {# year} other {# years}}'
days: '{days, plural, =1 {# day} other {# days}}'
workers: Workers
no-worker-filter: No worker filter added
descriptions:
empty-set: There are no items to display yet. You may be able to add items or try back later.
cluster-url-initialization: To get started, please enter your cluster URL
@@ -133,3 +135,26 @@ settings:
alerts:
cache-daemon: There may be a problem with the cache daemon
client-agent: There may be a problem with the client agent
worker-filter-generator:
title: Filter generator
description: Choose what you want to format into a filter.
link: Learn more about formatting a worker filter.
toggle:
title: Show filter generator
description: The generator will automatically generate a pre-formatted filter for you. You can copy the result to the editor above.
input-values:
title: Input values
description: Next, input what you want to format.
formatted-result:
title: Formatted result
description: Copy and paste this into the editor above.
label: Generated result
tag:
label: Tag
helper: Format a worker tag
name:
label: Name
helper: Format a worker name
operator:
matches: matches (regex)
contains: contains
+8 -2
View File
@@ -18,6 +18,8 @@ description:
type:
label: Type
help: The type of the resource, to help differentiate schemas
plugin_type:
label: plugin_type
login_name:
label: Login Name
help: The account login name
@@ -134,6 +136,8 @@ key:
label: Key
value:
label: Value
operator:
label: Operator
callback_url:
label: Callback URL
help: The URL to which the third-party provider sends the authentication response. Paste this into the provider configuration.
@@ -148,10 +152,10 @@ subscription_id:
help: The subscription ID for the subscription with read access.
access_key_id:
label: Access Key ID
help: The access key ID generated by AWS for the IAM user to use with this host catalog.
help: The access key ID generated by AWS for the IAM user to use with this AWS Host Catalog.
secret_access_key:
label: Secret Access Key
help: The secret access key generated by AWS for the IAM user to use with this host catalog.
help: The secret access key generated by AWS for the IAM user to use with this AWS Host Catalog.
secret:
label: Client Secret Value
help: The client secret value generated by Azure for Boundary to use for authentication.
@@ -179,3 +183,5 @@ created:
other_fields:
label: Other Fields
help: This information is provided by the server and cannot be edited in Boundary
worker_filter:
label: Worker Filter
+32 -15
View File
@@ -211,7 +211,7 @@ host-catalog:
delete: Delete Host Catalog
help:
static: Provide a list of hosts and host sets with static addresses
plugin: Automate discovery of target hosts and services on Azure or AWS
plugin: Automate discovery of target hosts and services on Azure, AWS, or GCP
role_arn: The ARN (Amazon Resource Name) used by this instance.
disable_credential_rotation: Credential rotation is automatically disabled when you use dynamic credentials.
types:
@@ -220,6 +220,7 @@ host-catalog:
aws: AWS
azure: Azure
plugin: Dynamic
gcp: GCP
credential:
label: Credential type
help: Select the credential type that you want to use.
@@ -234,13 +235,30 @@ host-catalog:
form:
aws_region:
label: AWS Region
help: AWS region of the hosts to be added to the catalog.
help: AWS region of the hosts to be added to this AWS Host Catalog.
azure_client_id:
label: Client/Application ID
help: The client (application) ID of an Azure service principal that Boundary will use to authenticate and discover hosts from Azure.
worker_filter:
label: Worker Filter
help: Insert a worker filter in order to route requests to the correct worker.
project_id:
label: Project ID
help: Project ID of the instances you want to add to this GCP Host Catalog.
zone:
label: Zone
help: Zone of the instances you want to add to this GCP Host Catalog.
client_email:
label: Client Email
help: The email address used to uniquely identify the service account.
target_service_account_id:
label: Target Service Account ID
help: The unique identifier for the service account that will be impersonated. This is only used when authenticating with service account impersonation.
private_key_id:
label: Private Key ID
help: The unique identifier of private key.
private_key:
label: Private Key
help: The private key used to obtain a OAuth 2.0 access token. Key must be PEM encoded.
host-set:
title: Host Set
title_plural: Host Sets
@@ -257,9 +275,11 @@ host-set:
filter:
label: Filter
aws:
help: Create a filter to select resources using values such as tag name or tag value. The AWS filter format is tag:<tag-name>=<tag-value>. For example:tag:application=production
help: 'Create a filter to select resources using values such as tag name or tag value. The AWS filter format is tag:<tag-name>=<tag-value>. For example: tag:application=production'
azure:
help: Create a filter to select resources using values such as tag name or tag value. The Azure filter format is:tagName eq ‘<tag-name>’ and tagValue eq ‘<tag-value>'. For example:tagName eq 'application' and tagValue eq 'production'
gcp:
help: 'Create a filter to select resources using values such as label name or label value. The GCP filter format is labels.<label-name>=<label-value>. For example: labels.application=production'
preferred_endpoints:
label: Preferred Endpoints
help: Preferred address at which the host should be accessed when multiple options are present, in cidr:<valid IPv4/6 CIDR> or dns:<globbed name> format.
@@ -501,8 +521,7 @@ storage-bucket:
label: Secret access key
help: The secret access key generated by your storage provider for the IAM user to use with this storage bucket. We will not show this data after it is saved, but you can replace it.
worker_filter:
label: Worker filter
help: Filter for Boundary workers that have network access to your storage. Boolean expression.
help: Filters to the worker(s) that can handle requests for this storage bucket.
disable_credential_rotation:
label: Disable credential rotation
help: Disabling credential rotation could allow entities outside of Boundary to know the client secret in use.
@@ -782,11 +801,9 @@ target:
title: Add Injected Application Credential Sources
description: Select injected Application credential sources to assign to this target.
workers:
title: Workers
description: You can customize how your workers route traffic to this target by setting up a filter that matches specific worker tags. If your target is in a private network, we recommend setting up an egress filter to specify a worker inside the network so that Boundary can access the host. Ingress and egress filters are allowed to select the same worker.
messages:
none:
title: No worker filter added
description: You haven't added an {type} worker filter yet.
diagram:
client: Client
@@ -812,13 +829,10 @@ target:
title: Edit Ingress Worker Filter
description: Specify workers Boundary should use to start the route to this target.
worker-filter:
title: Worker filter
description: The worker filter format for this target.
accordion-label:
egress-workers: Egress workers
ingress-workers: Ingress workers
actions:
edit-worker-filter: Edit worker filter
credential-store:
title: Credential Store
title_plural: Credential Stores
@@ -828,6 +842,13 @@ credential-store:
title: Welcome to Credential Stores
titles:
new: New Credential Store
worker-filter:
description: A filter used to control which PKI workers can handle Vault requests, allowing the use of private Vault instances with Boundary.
messages:
none:
description: You haven't added a worker filter yet.
edit:
description: Specify workers that have access to the credential store.
actions:
delete: Delete Credential Store
types:
@@ -865,9 +886,6 @@ credential-store:
help: A PEM-encoded private key matching the client certificate from Client Certificate.
client_certificate_key_hmac:
label: Client Certificate Key HMAC
worker_filter:
label: Worker Filter
help: A filter used to control which PKI workers can handle Vault requests, allowing the use of private Vault instances with Boundary.
credential-library:
title: Credential Library
title_plural: Credential Libraries
@@ -1001,7 +1019,6 @@ managed-group:
help: The list of groups that this Managed Group belongs to.
worker:
title: Worker
title_plural: Workers
titles:
new: New PKI Worker
description: Workers are the server components that perform the session handling.
+1 -5
View File
@@ -10,11 +10,7 @@ module.exports = {
extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'],
overrides: [
{
files: [
'addon/components/rose/anonymous/index.hbs',
'addon/components/rose/table/row/cell/index.hbs',
'addon/components/rose/anonymous/index.hbs',
],
files: ['addon/components/rose/anonymous/index.hbs'],
rules: {
'no-yield-only': false,
},
@@ -21,7 +21,6 @@
}}
{{yield
(hash
radioGroup=(component 'rose/form/radio/group' disabled=this.disabled)
checkbox=(component 'rose/form/checkbox' disabled=this.disabled)
checkboxGroup=(component
'rose/form/checkbox/group' disabled=this.disabled
@@ -1,17 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
{{yield
(hash
radio=(component
'rose/form/radio/radio'
name=@name
selectedValue=@selectedValue
disabled=@disabled
changed=@changed
inline=@inline
)
)
}}
@@ -1,43 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<div class='{{this.className}} {{if @error " error"}} {{if @inline "inline"}}'>
<RadioButton
...attributes
@radioClass='rose-form-radio-field'
@radioId={{this.id}}
type='radio'
disabled={{@disabled}}
@name={{@name}}
@value={{@value}}
@groupValue={{@selectedValue}}
@disabled={{@disabled}}
@changed={{@changed}}
/>
<label class='rose-form-radio-label' for={{this.id}}>
{{#if @icon}}
<Rose::Icon @name={{@icon}} @size='medium' />
{{/if}}
{{@label}}
<span class='radio-card-overlay'></span>
{{#if @helperText}}
<Rose::Form::HelperText
@id='helper-text-{{this.id}}'
@error={{@error}}
@link={{@link}}
@linkText={{@linkText}}
>
{{@helperText}}
</Rose::Form::HelperText>
{{/if}}
</label>
{{yield}}
</div>
@@ -1,13 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { generateComponentID } from '../../../../../utilities/component-auto-id';
export default class RoseFormRadioRadioComponent extends Component {
// =attributes
className = 'rose-form-radio';
id = generateComponentID();
}
@@ -1,32 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<LinkTo
class='rose-link-button
{{if @style (concat "rose-button-" @style)}}
{{if @iconOnly "has-icon-only"}}
{{if @iconLeft "has-icon-left"}}
{{if @iconRight "has-icon-right"}}'
@route={{@route}}
...attributes
>
<span class='rose-button-wrapper'>
{{#if @iconLeft}}
<Rose::Icon @name={{@iconLeft}} @size='medium' />
{{/if}}
<span class='rose-button-body'>
{{yield}}
</span>
{{#if @iconOnly}}
<Rose::Icon @name={{@iconOnly}} @size='medium' />
{{/if}}
{{#if @iconRight}}
<Rose::Icon @name={{@iconRight}} @size='medium' />
{{/if}}
</span>
</LinkTo>
@@ -1,33 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<article ...attributes class='rose-message'>
{{#if @title}}
<header class='rose-message-header'>
{{#if @icon}}
<Rose::Icon @name={{@icon}} />
{{/if}}
<h2 class='rose-message-title'>{{@title}}</h2>
{{#if @subtitle}}
<h3 class='rose-message-subtitle'>{{@subtitle}}</h3>
{{/if}}
</header>
{{/if}}
<section class='rose-message-body'>
{{yield
(hash
description=(component
'rose/anonymous' tagName='p' class='rose-message-description'
)
link=(component 'link-to')
)
}}
</section>
</article>
@@ -1,8 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
export default class RoseMessageComponent extends Component {}
@@ -5,7 +5,7 @@
<li class='rose-metadata-list-item {{(concat "hds-foreground-" @color)}}'>
{{#if @icon}}
<FlightIcon @name={{@icon}} />
<Hds::Icon @name={{@icon}} />
{{/if}}
{{yield}}
</li>
@@ -1,41 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<section
...attributes
class='rose-notification is-{{@style}}{{if @dismiss " is-dismissible"}}'
role='alert'
aria-live={{if @dismiss 'assertive'}}
aria-atomic='true'
>
<header class='rose-notification-header'>
<Rose::Icon
class='rose-notification-icon'
@name={{this.icon}}
@size='medium'
/>
{{@heading}}
</header>
<div class='rose-notification-body'>
{{yield}}
</div>
{{#if @dismiss}}
<button
type='button'
class='rose-notification-dismiss'
title={{@dismissText}}
{{on 'click' @dismiss}}
>
<Rose::Icon @name='flight-icons/svg/x-16' @size='medium' />
<span class='rose-notification-dismiss-text'>
{{@dismissText}}
</span>
</button>
{{/if}}
</section>
@@ -1,32 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { computed } from '@ember/object';
export default class RoseNotificationComponent extends Component {
/**
* Returns an icon name based on the notification style.
* @type {string}
*/
@computed('style')
get icon() {
let icon = 'flight-icons/svg/info-16';
switch (this.style) {
case 'error':
icon = 'flight-icons/svg/x-square-16';
break;
case 'success':
icon = 'flight-icons/svg/check-circle-16';
break;
case 'warning':
icon = 'flight-icons/svg/alert-triangle-16';
break;
}
return icon;
}
}
@@ -1,6 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<hr class='rose-separator' />
@@ -1,27 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<table
...attributes
class='rose-table {{if @style (concat "rose-table-" @style)}}'
>
{{#if @caption}}
<caption>{{@caption}}</caption>
{{/if}}
{{yield
(hash
header=(component
'rose/table/section' tagName='thead' classNames='rose-table-header'
)
body=(component
'rose/table/section' tagName='tbody' classNames='rose-table-body'
)
footer=(component
'rose/table/section' tagName='tfoot' classNames='rose-table-footer'
)
)
}}
</table>
@@ -1,6 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
{{yield}}
@@ -1,33 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
import layout from './index';
import { computed } from '@ember/object';
export default Component.extend({
layout,
tagName: 'td',
classNameBindings: ['cellTypeStyle', 'cellShrink', 'cellAlignmentStyle'],
cellTypeStyle: computed(function () {
return this.tagName.match('th')
? `rose-table-header-cell`
: 'rose-table-cell';
}),
cellShrink: computed(function () {
if (this.shrink) {
return 'rose-table-cell-shrink';
}
}),
cellAlignmentStyle: computed(function () {
if (this.alignRight) {
return 'align-right';
}
if (this.alignCenter) {
return 'align-center';
}
}),
});
@@ -1,18 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
<tr
...attributes
class='rose-table-row
{{if @hidden (concat "rose-table-row-visually-hidden")}}
{{if @style (concat "rose-table-row-" @style)}}'
>
{{yield
(hash
cell=(component 'rose/table/row/cell')
headerCell=(component 'rose/table/row/cell' tagName='th')
)
}}
</tr>
@@ -1,16 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
import layout from './index';
/**
* A table row component that uses tr table element and
* configures a table cell element.
*/
export default Component.extend({
layout,
tagName: '',
});
@@ -1,6 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
{{yield (hash row=(component 'rose/table/row'))}}
@@ -1,16 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
import layout from './index';
/**
* A table section component that configures a row component.
* Section can be: thead, tbody, or tfoot table elements.
*/
export default Component.extend({
layout,
tagName: '',
});
@@ -0,0 +1,18 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export default function mapTypeToColor(flashMsgType) {
// Maps the flash message type parameter to the correct hds color.
switch (flashMsgType) {
case 'error':
return 'critical';
case 'success':
return 'success';
case 'warning':
return 'warning';
default:
return 'neutral';
}
}
@@ -3,12 +3,12 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { v1 } from 'ember-uuid';
import { v4 as uuidv4 } from 'uuid';
/**
* A function that generates a component ID, based on UUID.
* @return {string}
*/
export function generateComponentID() {
return `component-${v1()}`;
return `component-${uuidv4()}`;
}
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/form/radio/card';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/form/radio/group';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/form/radio/radio';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/link-button';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/message';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/notification';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/table/row';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/table/row/cell';
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/table/section';
@@ -3,4 +3,4 @@
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/table';
export { default } from 'rose/helpers/map-type-to-color';
@@ -42,10 +42,5 @@ $xxxxs: sizing.rems(xxxxs); // 1
$body-padding: $l;
padding: $body-padding;
> .rose-separator {
margin-left: -$body-padding;
margin-right: -$body-padding;
}
}
}
@@ -3,24 +3,21 @@
* SPDX-License-Identifier: BUSL-1.1
*/
// This list is sorted in ascending order to improve readability
@import 'button';
@import 'card';
@import 'cards';
@import 'code-editor';
@import 'dialog';
@import 'dropdown';
@import 'form';
@import 'frame';
@import 'header';
@import 'layout';
@import 'icon';
@import 'layout';
@import 'list';
@import 'notification';
@import 'card';
@import 'cards';
@import 'page-header';
@import 'separator';
@import 'nav';
@import 'table';
@import 'message';
@import 'link-button';
@import 'toolbar';
@import 'code-editor';
@import 'metadata-list';
@import 'nav';
@import 'page-header';
@import 'toolbar';
@@ -1,9 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
// Link button uses rose-button styling
.rose-link-button {
text-decoration: none;
}
@@ -1,74 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@use '../utilities/type';
@use '../variables/sizing';
@use '../variables/media';
.rose-message {
--message-color: var(--ui-gray-starker-1);
display: flex;
flex-direction: column;
color: var(--message-color);
width: sizing.rems(l) * 15; // 16 rems/320 px
margin-bottom: sizing.rems(l);
@media (width <= (media.width(small))) {
width: auto;
}
// Icons always need tweaking
.rose-icon {
--size: #{sizing.rems(m)};
> svg {
vertical-align: text-bottom;
}
}
.rose-message-header {
display: grid;
grid-template-areas:
'icon title'
'icon subtitle';
grid-auto-columns: auto 1fr;
margin-bottom: sizing.rems(m);
.rose-icon {
// targeting 40px
--size: #{sizing.rems(xl) + sizing.rems(xxs)};
grid-area: icon;
margin-right: sizing.rems(s);
}
.rose-message-title {
@include type.type(l, semibold);
grid-area: title;
margin: 0;
}
.rose-message-subtitle {
@include type.type(s, normal);
grid-area: subtitle;
margin: 0;
}
}
.rose-message-body {
display: grid;
grid-template-areas:
'description description'
'left-link right-link';
.rose-message-description {
grid-area: description;
margin-bottom: sizing.rems(l);
}
}
}
@@ -1,106 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@use '../variables/sizing';
@use '../utilities/type';
.rose-notification {
$s: sizing.rems(s); // 12
$xs: sizing.rems(xs); // 8
$xxs: sizing.rems(xxs); // 4
$xxxs: sizing.rems(xxxs); // 2
$xxxxs: sizing.rems(xxxxs); // 1
position: relative;
padding: sizing.rems(m);
margin-bottom: sizing.rems(m);
border: sizing.rems(xxxxs) solid;
border-radius: $xxxs;
box-shadow:
0 $xs $s rgba(var(--stark-components), var(--opacity-3)),
0 $xxs $xxs rgba(var(--stark-components), var(--opacity-4));
.rose-notification-icon {
position: relative;
top: $xxxxs;
color: var(--icon-color);
}
.rose-notification-header {
color: var(--header-color);
@include type.type(m, semibold);
}
.rose-notification-body {
@include type.type(s);
// hack alert, because the icons are still hard to work with
margin-left: sizing.rems(m) + $xxs;
overflow-wrap: break-word;
}
.rose-notification-dismiss {
position: absolute;
color: currentcolor;
background-color: transparent;
border-radius: $xxxs;
border: 0;
padding: 0;
cursor: pointer;
top: sizing.rems(s);
right: sizing.rems(s);
.rose-icon {
display: block;
padding: $xxs;
}
.rose-notification-dismiss-text {
position: absolute;
top: -1000px;
left: -1000px;
width: 1px;
height: 1px;
opacity: 0;
}
&:hover {
background-color: rgba(var(--stark-components), var(--opacity-3));
}
&:active {
background-color: rgba(var(--stark-components), var(--opacity-2));
}
}
--icon-color: var(--info);
--header-color: var(--info-starker-1);
--border-color: var(--info-subtler-1);
--background-color: var(--info-subtler-3);
border-color: var(--border-color);
background-color: var(--background-color);
&.is-error {
--icon-color: var(--failure);
--header-color: var(--failure);
--border-color: var(--failure-subtler-1);
--background-color: var(--failure-subtler-2);
}
&.is-success {
--icon-color: var(--success);
--header-color: var(--success-starker-1);
--border-color: var(--success-subtler-1);
--background-color: var(--success-subtler-2);
}
&.is-warning {
--icon-color: var(--warning);
--header-color: var(--warning-starker-1);
--border-color: var(--warning-subtler-1);
--background-color: var(--warning-subtler-2);
}
}
@@ -1,13 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@use '../variables/sizing';
.rose-separator {
background-color: var(--ui-gray-subtler-3);
border: 0;
height: sizing.rems(xxxxs);
margin: sizing.rems(l) 0;
}
@@ -9,5 +9,4 @@
@import 'input';
@import 'checkbox';
@import 'select';
@import 'radio';
@import 'fieldset';
@@ -1,184 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@use '../../variables/sizing';
@use '../../utilities/type';
@use '../../variables/typography';
$radio-svg: (
'default':
"<svg viewBox='0 0 24 24' fill='%23aaaaaa' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' /></svg>",
'hover':
"<svg viewBox='0 0 24 24' fill='%236f7682' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' /></svg>",
'focus':
"<svg viewBox='0 0 24 24' fill='%231563ff' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' /></svg>",
'checked':
"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' fill='%231563ff'/></svg>",
'disabled':
"<svg viewBox='0 0 24 24' fill='%23bac1cc' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' /></svg>",
'disabled-checked':
"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' fill='%23bac1cc'/></svg>",
'error':
"<svg viewBox='0 0 24 24' fill='%23c73445' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' /></svg>",
'error-checked':
"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z' fill='%23c73445'/></svg>",
);
$data-svg-prefix: 'data:image/svg+xml;utf-8,';
.rose-form-radio {
&.inline {
display: inline-block;
.rose-form-radio-label {
margin-right: sizing.rems(s);
}
}
@include type.type(m);
position: relative;
margin-bottom: sizing.rems(xs);
color: var(--ui-gray);
.rose-form-radio-label {
display: flex;
align-items: center;
.rose-form-helper-text {
font-weight: normal;
margin-left: sizing.rems(xxs);
margin-bottom: 0;
}
@include type.type(s, semibold);
color: currentcolor;
padding-left: sizing.rems(l);
padding-top: sizing.rems(xxxs);
.rose-icon {
margin-right: sizing.rems(xxs);
margin-left: sizing.rems(xxs);
}
&::before {
position: absolute;
top: sizing.rems(xs);
left: 0;
display: block;
width: sizing.rems(m);
height: sizing.rems(m);
pointer-events: none;
content: ' ';
}
&::after {
position: absolute;
top: -#{sizing.rems(xxxxs)};
left: -#{sizing.rems(xxxs)};
display: block;
width: sizing.rems(l);
height: sizing.rems(l);
transform: scale(2);
pointer-events: none;
content: ' ';
background: no-repeat 50%/50% 50%;
background-image: url($data-svg-prefix + map-get($radio-svg, 'default'));
}
}
.rose-form-radio-field {
position: absolute;
left: 0;
z-index: -1;
width: sizing.rems(m);
height: sizing.rems(m);
opacity: 0;
&:not(:disabled) {
&:hover {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'hover'
));
}
}
}
&:focus {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'focus'
));
}
}
}
&:checked {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'checked'
));
}
}
}
}
&:disabled {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'disabled'
));
}
}
&:checked {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'disabled-checked'
));
}
}
}
}
}
&.error {
color: var(--failure);
.rose-form-radio-field {
&:not(:disabled, :focus) {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'error'
));
}
}
&:checked {
~ .rose-form-radio-label {
&::after {
background-image: url($data-svg-prefix + map-get(
$radio-svg,
'error-checked'
));
}
}
}
}
}
}
}
@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@import 'table';
@@ -1,120 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
@use '../../variables/sizing';
@use '../../utilities/type';
@use 'sass:math';
.rose-table {
@include type.type(s, normal);
width: 100%;
.rose-table-row {
border-bottom: sizing.rems(xxxxs) solid var(--ui-gray-subtler-3);
text-align: left;
&:last-child {
border-bottom: 0;
}
}
// This styles are add to stop the input to apply extra margin (default on forms)
.rose-form-input,
.rose-form-input-field,
.hds-form-select,
[class^='hds-form-field'],
[class^='hds-form-field']:not(:first-child) {
margin: 0;
width: 100%;
}
// Fix margin issue with textarea elements inside tables
textarea {
display: block;
}
.rose-table-cell,
.rose-table-header-cell {
vertical-align: text-top;
&.align-right {
text-align: right;
}
&.align-center {
text-align: center;
}
}
.rose-table-header-cell {
@include type.type(s, semibold);
}
.rose-table-header .rose-table-header-cell {
color: var(--ui-gray);
}
.rose-table-header,
.rose-table-footer {
.rose-table-cell,
.rose-table-header-cell {
padding: sizing.rems(s);
}
}
.rose-table-body {
.rose-table-cell,
.rose-table-header-cell {
padding: sizing.rems(m) sizing.rems(s);
}
}
.rose-table-cell-shrink {
width: 1%;
white-space: nowrap;
}
[class*='rose-button'] {
margin-bottom: 0;
}
&.rose-table-condensed {
.rose-table-cell,
.rose-table-header-cell {
@include type.type(s);
$line-height: math.div(36, 14);
line-height: $line-height;
padding: sizing.rems(xxs) + sizing.rems(xxxxs) sizing.rems(s);
&:first-child {
padding-left: 0;
}
}
.rose-table-header {
border-bottom: sizing.rems(xxxxs) solid var(--ui-gray-subtler-3);
.rose-table-header-cell {
$line-height: math.div(16, 14);
line-height: $line-height;
padding: sizing.rems(xs) sizing.rems(s);
&:first-child {
padding-left: 0;
}
}
}
}
.rose-table-row-visually-hidden {
position: absolute;
top: -9999rem;
left: -9999rem;
}
}
@@ -76,6 +76,8 @@
--ui-gray-starker-3: #{color(ui-gray, 800)};
--ui-gray-starker-4: #{color(ui-gray, 900)};
--ui-header-subtler: #{color(ui-cool-gray, 700)};
color-scheme: light;
}
// Dark Mode
+4 -7
View File
@@ -27,13 +27,13 @@ module.exports = {
/**
* Due to a limitation in how ember treats nested addons (see https://github.com/ember-cli/ember-cli/issues/4475)
* this is neeeded to reach down into @hashicorp/ember-flight-icons' contentFor hook to run the logic
* this is neeeded to reach down into @hashicorp/design-system-components' contentFor hook to run the logic
* that injects the sprite into the DOM
*/
contentFor(type, config) {
return this.findOwnAddonByName('@hashicorp/design-system-components')
.findOwnAddonByName('@hashicorp/ember-flight-icons')
.contentFor(type, config);
return this.findOwnAddonByName(
'@hashicorp/design-system-components',
).contentFor(type, config);
},
/**
@@ -45,8 +45,6 @@ module.exports = {
'../../node_modules/@hashicorp/design-system-tokens/dist/products/css';
const hdsPath =
'../../node_modules/@hashicorp/design-system-components/dist/styles';
const iconsPath =
'../../node_modules/@hashicorp/ember-flight-icons/dist/styles';
// Setup default sassOptions on the running application
app.options.sassOptions = app.options.sassOptions || {};
@@ -56,7 +54,6 @@ module.exports = {
// Include the addon styles
app.options.sassOptions.includePaths.push(tokensPath);
app.options.sassOptions.includePaths.push(hdsPath);
app.options.sassOptions.includePaths.push(iconsPath);
},
/**
+12 -16
View File
@@ -30,20 +30,19 @@
},
"dependencies": {
"@embroider/test-setup": "^2.1.1",
"@hashicorp/design-system-components": "^4.4.1",
"@hashicorp/design-system-components": "^4.13.0",
"codemirror": "5.65.7",
"ember-auto-import": "^2.6.3",
"ember-auto-import": "^2.8.1",
"ember-cli-babel": "^8.2.0",
"ember-cli-clipboard": "^1.0.0",
"ember-cli-clipboard": "^1.2.1",
"ember-cli-htmlbars": "^6.2.0",
"ember-cli-sass": "^11.0.1",
"ember-composable-helpers": "^5.0.0",
"ember-focus-trap": "^1.0.1",
"ember-named-blocks-polyfill": "^0.2.5",
"ember-radio-button": "github:yapplabs/ember-radio-button.git#b2ab65f",
"ember-uuid": "^2.1.0",
"jsonlint": "^1.6.3",
"sass": "^1.69.5"
"sass": "^1.69.5",
"uuid": "^11.0.3"
},
"devDependencies": {
"@babel/core": "^7.25.2",
@@ -51,16 +50,16 @@
"@babel/plugin-proposal-decorators": "^7.21.0",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.0.1",
"@ember/test-helpers": "^3.3.1",
"@ember/test-helpers": "^4.0.4",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"babel-loader": "^8.2.5",
"babel-loader": "^9.2.1",
"babel-plugin-ember-modules-api-polyfill": "^2.12.0",
"babel-plugin-htmlbars-inline-precompile": "3",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"concurrently": "^9.1.0",
"doctoc": "^2.2.0",
"ember-cli": "^4.12.2",
"ember-cli": "^5.12.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
@@ -88,19 +87,16 @@
"loader.js": "^4.7.0",
"postcss": "^8.4.31",
"prettier": "^3.0.0",
"qunit": "^2.19.4",
"qunit-dom": "^2.0.0",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
"react": "^16",
"react-dom": "^16",
"react-syntax-highlighter": "^15.5.0",
"react-syntax-highlighter": "^15.6.1",
"stylelint": "^15.10.1",
"stylelint-config-prettier-scss": "^0.0.1",
"stylelint-config-standard-scss": "^11.0.0",
"webpack": "^5.94.0"
},
"peerDependencies": {
"ember-source": "^4.0.0"
},
"engines": {
"node": "18.* || 20.*"
},
@@ -72,19 +72,12 @@ module('Integration | Component | rose/form', function (hooks) {
</Hds::Form::Select::Field>
<form.checkbox @label="Checkbox" @name="checkbox-field" />
<form.radioGroup @name="radio-group-field" @selectedValue="green" as |radioGroup|>
<radioGroup.radio
@label="Red"
@value="red"
/>
</form.radioGroup>
</Rose::Form>
`);
assert.ok(find('[name="input-field"]'));
assert.ok(find('[name="textarea-field"]'));
assert.ok(find('[name="select-field"]'));
assert.ok(find('[name="checkbox-field"]'));
assert.ok(find('[name="radio-group-field"]'));
});
test('it supports an editability toggle when @showEditToggle is true', async function (assert) {
@@ -1,126 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click, findAll } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/form/radio/group', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`
<Rose::Form::Radio::Group @name="bird" as |radioGroup|>
<radioGroup.radio
@name="bird"
@label="Pegion"
@value="pegion"
/>
<radioGroup.radio
@name="bird"
@label="flamingo"
@value="flamingo"
/>
</Rose::Form::Radio::Group>
`);
assert.strictEqual(findAll('input').length, 2);
assert.strictEqual(findAll('input')[0].name, 'bird');
assert.strictEqual(findAll('input')[1].name, 'bird');
});
test('it renders with @selectedValue value selected', async function (assert) {
await render(hbs`
<Rose::Form::Radio::Group @name="bird" @selectedValue="flamingo" as |radioGroup|>
<radioGroup.radio
@name="bird"
@label="Pegion"
@value="pegion"
/>
<radioGroup.radio
@name="bird"
@label="flamingo"
@value="flamingo"
/>
</Rose::Form::Radio::Group>
`);
assert.false(findAll('[type="radio"]')[0].checked);
assert.true(findAll('[type="radio"]')[1].checked);
await click(findAll('[type="radio"]')[0]);
assert.true(findAll('[type="radio"]')[0].checked);
assert.false(findAll('[type="radio"]')[1].checked);
});
test('it reflects active radiofield value in @selectedValue and triggers a @changed function', async function (assert) {
assert.expect(5);
this.changed = (value) => {
this.selectedValue = value;
assert.ok(value);
};
await render(hbs`
<Rose::Form::Radio::Group
@name="bird"
@selectedValue={{this.selectedValue}}
@changed={{this.changed}}
as |radioGroup|>
<radioGroup.radio
@name="bird"
@label="Pegion"
@value="pegion"
/>
<radioGroup.radio
@name="bird"
@label="flamingo"
@value="flamingo"
/>
</Rose::Form::Radio::Group>
`);
assert.notOk(this.selectedValue);
await click(findAll('[type="radio"]')[0]);
assert.strictEqual(this.selectedValue, 'pegion');
await click(findAll('[type="radio"]')[1]);
assert.strictEqual(this.selectedValue, 'flamingo');
});
test('it uses @selectedValue value to set active radiofield', async function (assert) {
this.set('selectedValue', 'cat');
await render(hbs`
<Rose::Form::Radio::Group @name="bird" @selectedValue={{this.selectedValue}} as |radioGroup|>
<radioGroup.radio
@name="bird"
@label="Pegion"
@value="pegion"
/>
<radioGroup.radio
@name="bird"
@label="flamingo"
@value="flamingo"
/>
</Rose::Form::Radio::Group>
`);
assert.false(findAll('[type="radio"]')[0].checked);
assert.false(findAll('[type="radio"]')[1].checked);
this.set('selectedValue', 'flamingo');
assert.false(findAll('[type="radio"]')[0].checked);
assert.true(findAll('[type="radio"]')[1].checked);
this.set('selectedValue', 'pegion');
assert.true(findAll('[type="radio"]')[0].checked);
assert.false(findAll('[type="radio"]')[1].checked);
this.set('selectedValue', 'dog');
assert.false(findAll('[type="radio"]')[0].checked);
assert.false(findAll('[type="radio"]')[1].checked);
});
});
@@ -1,71 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/form/radio/radio', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio />`);
assert.ok(await find('input'));
assert.strictEqual(await find('input').type, 'radio');
});
test('it renders with label', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio @label="Label"/>`);
assert.strictEqual(await find('label').textContent.trim(), 'Label');
});
test('it supports an icon', async function (assert) {
await render(
hbs`<Rose::Form::Radio::Radio @icon="flight-icons/svg/user-circle-16" />`,
);
assert.ok(find('svg'));
});
test('it is checked when @value and @selectedValue values match', async function (assert) {
await render(
hbs`<Rose::Form::Radio::Radio @value="tree" @selectedValue="tree"/>`,
);
assert.true(await find('input').checked);
});
test('it is not checked when @value and @selectedValue values are different', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio @value="tree"/>`);
assert.false(await find('input').checked);
await render(hbs`<Rose::Form::Radio::Radio @selectedValue="tree"/>`);
assert.false(await find('input').checked);
await render(
hbs`<Rose::Form::Radio::Radio @value="root" @selectedValue="tree"/>`,
);
assert.false(await find('input').checked);
});
test('it is not disabled by default', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio />`);
assert.false(await find('input').disabled);
});
test('it is not in error state by default', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio />`);
assert.notOk(await find('.error'));
});
test('it is disabled when @disabled={{true}}', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio @disabled={{true}}/>`);
assert.true(await find('input').disabled);
});
test('it is in error state when @error={{true}}', async function (assert) {
await render(hbs`<Rose::Form::Radio::Radio @error={{true}} />`);
assert.ok(await find('.error'));
});
});
@@ -1,64 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/link-button', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::LinkButton @route="index" />`);
assert.ok(find('a'));
assert.notOk(find('.rose-button'));
assert.ok(find('.rose-link-button'));
});
test('it renders with attributes', async function (assert) {
await render(hbs`<Rose::LinkButton @route="index" id="link-button"/>`);
assert.ok(find('#link-button'));
});
test('it renders with content', async function (assert) {
await render(
hbs`<Rose::LinkButton @route="index">Link button content</Rose::LinkButton>`,
);
assert.ok(
find('.rose-link-button').textContent.trim(),
'Link button content',
);
});
test('it renders with rose button styles', async function (assert) {
await render(hbs`<Rose::LinkButton @route="index" @style="sample"/>`);
assert.ok(find(`.rose-button-sample`));
});
test('it supports left icon with @iconLeft', async function (assert) {
await render(
hbs`<Rose::LinkButton @route="index" @iconLeft="flight-icons/svg/chevron-left-16" />`,
);
assert.ok(find('.rose-icon'));
assert.ok(find('.has-icon-left'));
});
test('it supports left icon with @iconRight', async function (assert) {
await render(
hbs`<Rose::LinkButton @route="index" @iconRight="flight-icons/svg/entry-point-16" />`,
);
assert.ok(find('.rose-icon'));
assert.ok(find('.has-icon-right'));
});
test('it supports only icon with @iconOnly', async function (assert) {
await render(
hbs`<Rose::LinkButton @route="index" @iconOnly="flight-icons/svg/help-16" />`,
);
assert.ok(find('.rose-icon'));
assert.ok(find('.has-icon-only'));
});
});
Loaded 100 of 683 files, more files were not shown because too many files have changed in this diff. Show more