Compare commits

..
Author SHA1 Message Date
CyberCoveandClaude Sonnet 5.5 8e63f6bd0f ci: build admin test bundle once and run partitions against it
ci / check (push) Successful in 15m57s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 13:57:21 -07:00
CyberCoveandClaude Sonnet 5.5 01e50cb8ce ci: run admin test partitions sequentially (parallel browsers stalled on the runner)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 13:19:19 -07:00
CyberCoveandClaude Sonnet 5.5 1a97b8b428 ci: install chromium from the distro archive instead of downloading Chrome
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 12:53:16 -07:00
CyberCoveandClaude Sonnet 5.5 3f2be6fab0 Security: patch-level dependency overrides, electron 42.11.12; add Gitea CI
- Lockfile-only/overrides bumps within the same major for vulnerable
  transitive build/test tooling (see pnpm-overrides.md)
- electron 42.0.1 -> 42.11.12 (same major) for runtime advisories
- .gitea/workflows/ci.yml: single job (build admin, tests, audit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 12:42:41 -07:00
CyberCoveandClaude Sonnet 5.5 89089dbb9a Rebrand Boundary UI to Threshold; neutralize HashiCorp update/phone-home endpoints
- Product name, app id, installer names, translations, titles -> Threshold
- Desktop: look for threshold CLI first, then boundary; migrate legacy
  Boundary user-data dir; updater/release lookups disabled unless configured
- Remove HashiCorp/IBM logos and art (plain placeholder mark), GitHub
  release workflows and HashiCorp release metadata
- Add NOTICE and README 'About this fork'

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 12:24:16 -07:00
richardtrinh1 25d78b2fc5 chore(deps): upgrade fast-uri to 3.1.8 (#3352) 2026-09-29 10:36:51 -05:00
Zhihe Li 4565676d4b chore: 🤖 Remove merge trigger for ENT (#3351) 2026-09-21 16:07:07 -04:00
Zhihe Li ed9371fab7 chore: 🤖 Update CLI version to correct one (#3336) 2026-07-10 12:17:57 -04:00
Zhihe Li 68a01cfa3a chore: 🤖 Update CLI version (#3335) 2026-07-10 10:17:51 -04:00
Priya Patel b7129e38a8 fix: 🐛 credential library update (#3328)
* fix: 🐛 update credential library serializer

* test: 💍 adds acceptance test

* test: 💍 fix test

* fix: 🐛 fix test

* test: 💍 adds a11y ignore

* chore: 🤖 pr feedback
2026-07-08 14:38:48 -07:00
Lisbet Alvarez 26e27fd24b fix: 🐛 code mirror lint tooltip format issues (#3325)
* fix: 🐛 code mirror lint tooltip format issues

* refactor: 💡 add fallback for parent config

* refactor: 💡 use getOwner to retrieve actual app root element
2026-06-19 12:52:48 -07:00
Zhihe Li d0612c0a46 feat: 🎸 No-op linting errors should be warnings (#3326) 2026-06-19 15:15:58 -04:00
Zhihe Li 347107c109 feat: 🎸 Add discard modal for edit grants page (#3324) 2026-06-18 13:35:49 -04:00
Lisbet Alvarez 0dac4ccf47 Use codemirror tooltip extension to set parent to fix positioning bug (#3323)
* fix: 🐛 absolute positioning of cm tooltip in firefox

* docs: ✏️ add comment

* fix: 🐛 tooltip hidden in editor fullscreen mode & dark mode

* fix: 🐛 tooltip color in dark mode

* docs: ✏️ add comment
2026-06-18 09:59:38 -07:00
Zhihe Li b8ea558f14 ci: 🎡 Update when e2e tests run (#3320) 2026-06-16 14:25:06 -04:00
Priya Patel 4fc061ec48 chore: 🤖 update desktop client changelog (#3316)
* chore: 🤖 update desktop client changelog and version

* fix: 🐛 update PR number

* docs: ✏️ updated log

* fix: 🐛 revert cli version change
2026-06-16 10:06:41 -07:00
Zhihe Li 3313cd5316 feat: 🎸 Add new grants and update grant templates (#3319)
* feat: 🎸 Add new grants and update grant templates

* feat: 🎸 Use wildcard for remaining grants as well
2026-06-15 18:52:50 -04:00
Zhihe Li af36d75252 feat: 🎸 Update deps for newest shell-quote version (#3318) 2026-06-12 17:25:19 -04:00
72a69fbe8d chore(deps): bump shell-quote in /ui/desktop/electron-app (#3313)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.8.2 to 1.8.4.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.2...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2026-06-12 13:35:04 -04:00
Michael Li e77ed3ae53 test(e2e): Address flake when loading project page (#3315) 2026-06-12 12:06:36 -04:00
Lisbet Alvarez 677a3c925b fix: 🐛 color of search cancel button icon (#3314) 2026-06-10 16:26:55 -07:00
Priya Patel 4cbe08276a chore: 🤖 updates libraries for webkit to resolve e2e test fais (#3317) 2026-06-10 10:14:40 -07:00
Lisbet Alvarez 20699d62dc chore: 🤖 Add override to pin markdown-it to resolve cve (#3311) 2026-06-09 15:14:23 -07:00
Zhihe Li 048339982e feat: 🎸 Use new path for grants schema (#3309) 2026-06-09 15:35:10 -04:00
Priya Patel 2709c2e081 chore: 🤖 updates chaining using pipe operator (#3312) 2026-06-09 08:19:02 -07:00
Susmitha GirumalaandLisbet Alvarez 5e3addcc41 chore: 🤖 Upgrade lint-staged to resolve yaml secvuln (#3310)
✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/254

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
2026-06-08 15:47:51 -07:00
Zhihe Li 576fef9f8a feat: 🎸 Update scope dropdown in login to show current scope (#3308)
* feat: 🎸 Update scope dropdown in login to show current scope

* test: 💍 Update e2e tests
2026-06-08 17:08:23 -04:00
dillanb-hashi b9517e0233 test: 💍 Allow webkit and firefox to tests to run on enterprise (#3305)
* test: 💍 Allow webkit and firefox to tests to run on enterprise
✅ Closes: ICU-18952
2026-06-05 08:45:28 -07:00
Lisbet Alvarez 4d7ce40469 fix: 🐛 newline being removed for existing import & fix transform errors for a11y ignores codemod (#3183)
* fix: 🐛 newline being removed for existing import & fix errors

* fix: 🐛 avoid testName being undefined
2026-06-04 17:00:56 -07:00
Dharini fa51973e6d chore: make sub claim mapping readonly after creation (#3306) 2026-06-04 15:40:24 -07:00
Lisbet Alvarez f80d7ebeb3 chore: 🤖 add override for tmp in electron-app to resolve cve (#3307) 2026-06-04 15:24:25 -07:00
Dharini 9aea1a8fd8 test: 💍 a11y (#3302) 2026-06-03 16:36:29 -07:00
Dharini 35377e8466 chore: 🤖 remove listwrapper usage (#3294) 2026-06-03 16:36:29 -07:00
Dharini 0e580e3c9f chore: migrate listwrapper Target and worker fields to keyvalue component (#3284) 2026-06-03 16:36:29 -07:00
Dharini e38d3afa58 Chore migrate credential library storage bucket (#3282) 2026-06-03 16:36:29 -07:00
Dharini 0e223ded8f chore: 🤖 migrate host catalog and host set list wrapper fields to key value (#3276) 2026-06-03 16:36:29 -07:00
Dharini 4e2b779d96 migrate ldap list wrapper fields (#3162) 2026-06-03 16:36:29 -07:00
Dharini 71ec7b465a chore: 🤖 migrate oidc fields to keyvalue component (#3160) 2026-06-03 16:36:29 -07:00
Dharini 8b870556a6 chore: 🤖 use hds keyvalue component (#3027) 2026-06-03 16:36:29 -07:00
Lisbet Alvarez ecefa39b98 fix: 🐛 actions not displaying for templates in grants editor (#3296)
* fix: 🐛 actions not displaying for templates in grants editor

* fix: 🐛 invalid ids check

* fix: 🐛 update check for ids type

* test: 💍 fix broken strings

* test: 💍 fix test case

* test: 💍 remove log
2026-06-03 12:46:34 -07:00
Susmitha Girumala 4a206c82dc chore: 🤖 Update openapi-generator-cli to resolve basic-ftp cve (#3303)
✅ Closes: https://github.com/hashicorp/boundary-ui-enterprise/security/code-scanning/287
2026-06-03 12:22:17 -07:00
Michael Li 2d0ed53202 test(e2e): Fix flakiness in host catalog navigation (#3291) 2026-06-02 15:51:42 -04:00
dependabot[bot]andPriya Patel 5000d98e23 chore(deps): bump hashicorp/setup-terraform from 4.0.0 to 4.0.1 (#3299)
Bumps [hashicorp/setup-terraform](https://github.com/hashicorp/setup-terraform) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/hashicorp/setup-terraform/releases)
- [Changelog](https://github.com/hashicorp/setup-terraform/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/setup-terraform/compare/5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85...dfe3c3f87815947d99a8997f908cb6525fc44e9e)

---
updated-dependencies:
- dependency-name: hashicorp/setup-terraform
  dependency-version: 4.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Patel <priya.patel@hashicorp.com>
2026-06-01 17:29:20 -07:00
dependabot[bot] 4afd7c2d0b chore(deps): bump browser-actions/setup-chrome from 2.1.1 to 2.1.2 (#3300)
Bumps [browser-actions/setup-chrome](https://github.com/browser-actions/setup-chrome) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/browser-actions/setup-chrome/releases)
- [Changelog](https://github.com/browser-actions/setup-chrome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/browser-actions/setup-chrome/compare/4f8e94349a351df0f048634f25fec36c3c91eded...2e1d749697dd1612b833dba4a722266286fbefcd)

---
updated-dependencies:
- dependency-name: browser-actions/setup-chrome
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-01 16:55:28 -07:00
dependabot[bot]andPriya Patel 207b1e8a7c chore(deps): bump actions/labeler from 6.0.1 to 6.1.0 (#3298)
Bumps [actions/labeler](https://github.com/actions/labeler) from 6.0.1 to 6.1.0.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/634933edcd8ababfe52f92936142cc22ac488b1b...f27b608878404679385c85cfa523b85ccb86e213)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Patel <priya.patel@hashicorp.com>
2026-06-01 16:41:29 -07:00
dependabot[bot]andPriya Patel 8b90db1ff5 chore(deps): bump slackapi/slack-github-action from 3.0.2 to 3.0.3 (#3297)
Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/slackapi/slack-github-action/releases)
- [Changelog](https://github.com/slackapi/slack-github-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/slackapi/slack-github-action/compare/03ea5433c137af7c0495bc0cad1af10403fc800c...45a88b9581bfab2566dc881e2cd66d334e621e2c)

---
updated-dependencies:
- dependency-name: slackapi/slack-github-action
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Patel <priya.patel@hashicorp.com>
2026-06-01 16:06:40 -07:00
Zhihe Li 7fc10855d9 feat: 🎸 Add override to fix node 24 builds (#3301) 2026-06-01 18:39:45 -04:00
Lisbet Alvarez 7c3be7c9a0 chore: 🤖 upgrade @babel/preset-env to resolve cve (#3292) 2026-06-01 11:36:59 -07:00
Lisbet Alvarez 7cddf7cb91 Refactor utils to share components (#3279)
* refactor: 💡 initial refactor for utils to share components

* refactor: 💡 actions logic

* Add missing copyright headers

* refactor: 💡 simplify actions getter logic

* refactor: 💡 convert output fields logic into a separate getter

* test: 💍 fix unit test

* fix: 🐛 bug with unknown fields lint error

* refactor: 💡 pr feedback - simplify template check

* refactor: 💡 remove unnecessary unit test file
2026-05-29 16:52:00 -07:00
Zhihe Li 878bd241f3 feat: 🎸 Update playwright to newest version (#3290)
* feat: 🎸 Update playwright to newest version

* fix: 🐛 Fix cache dependency key
2026-05-29 19:23:04 -04:00
Susmitha Girumala 4aec0d3acc chore: 🤖 Upgrade fast-uri for cve (#3289)
✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/333,
https://github.com/hashicorp/boundary-ui/security/code-scanning/334,
https://github.com/hashicorp/boundary-ui/security/code-scanning/335,
https://github.com/hashicorp/boundary-ui/security/code-scanning/336
2026-05-29 15:27:18 -04:00
Susmitha GirumalaandZhihe Li e43625f76c chore: 🤖 Upgrade tmp module for secvuln resolution (#3287)
* chore: 🤖 Upgrade tmp module for secvul cve-2026-44705

Resolves
https://github.com/hashicorp/boundary-ui/security/code-scanning/342

* chore: 🤖 Upgrade tmp to resolve desktop client secvuln

✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/342

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2026-05-29 14:26:59 -04:00
Priya Patel 5b51edd17f fix: 🐛 updates node and pnpm setup order (#3286) 2026-05-29 10:36:17 -07:00
Dharini 6eb21d3e13 Display session playback error (#3280) 2026-05-29 09:54:55 -07:00
Susmitha Girumala 9b43819865 chore: 🤖 Upgrade xmldom for secvul resolution (#3288)
✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/313,
https://github.com/hashicorp/boundary-ui/security/code-scanning/311,
https://github.com/hashicorp/boundary-ui/security/code-scanning/310,
https://github.com/hashicorp/boundary-ui/security/code-scanning/309
2026-05-29 12:07:55 -04:00
Zhihe Li 562b371db3 feat: 🎸 Update grant template names (#3283) 2026-05-29 10:18:42 -04:00
Zhihe Li 89d492042e feat: 🎸 Fix contrast issue with count badge in dropdown (#3285)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-19106
2026-05-28 09:59:48 -04:00
Lisbet Alvarez f9197d5a99 chore: 🤖 Upgrade electron to v42.0.1 (latest) (#3278) 2026-05-21 16:59:13 -07:00
Zhihe Li ebd7f64fe3 fix: 🐛 Add missing code block description (#3277)
* fix: 🐛 Add missing code block description

* feat: 🎸 Add autocomplete on empty lines
2026-05-20 18:29:40 -04:00
Zhihe Li 7dadc53fe1 Remove renderer url (#3194)
* feat: 🎸 Refactor how we store cluster URL
2026-05-20 16:36:16 -04:00
Zhihe Li 3d257afd49 refactor: 💡 Update transaction style and add busy timeout PRAGMA (#3271)
* refactor: 💡 Update transaction style and add busy PRAGMA

* refactor: 💡 Add return to insert statement for consistency

* refactor: 💡 Also add check for db

* test: 💍 Fix flaky test
2026-05-18 19:05:29 -04:00
Lisbet Alvarez 95918cb997 fix: 🐛 scrollbar color in dark mode + layout bugs (#3226)
* fix: 🐛 header scrollbar overlap in desktop

* refactor: 💡 undo desktop scrollbar changes

* refactor: 💡 fix whitespace issues when viewport is small

* fix: 🐛 scrollbar color issue in dark mode

* fix: 🐛 move style inside of media query
2026-05-15 15:01:19 -07:00
Lisbet Alvarez 077200d792 test: 💍 fix a11y violations and ignore existing issues (#3275)
* test: 💍 fix a11y violations and ignore existing issues

* test: 💍 remove ignores for fixed violation

* test: 💍 more color contrast ignores for new tests
2026-05-15 13:05:19 -07:00
Zhihe Li f34a1df298 refactor: 💡 Update e2e test for finding code editor (#3273)
* refactor: 💡 Update e2e test for finding code editor

* test: 💍 Added more acceptance tests
2026-05-15 12:40:37 -04:00
Zhihe Li 7fa59bfd5d feat: 🎸 Add support for output fields (#3268)
* feat: 🎸 Add support for output fields

* refactor: 💡 PR feedback
2026-05-14 15:21:30 -04:00
e0b80b1176 refactor: 💡 update pinned IDs logic for actions table (#3269)
* chore: 🤖 Add initial page layout for code editor + sidebar (#3180)

* chore: 🤖 Add initial page layout for code editor + sidebar

* test: 💍 add missing a11y ignores for exisitng color issue

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

* Replace `Rose::CodeEditor` with `Hds::CodeEditor` (#2679)

* chore: 🤖 replace Rose::CodeEditor with Hds::CodeEditor

- Remove CodeEditor 5 dependency

* chore: 🤖 Replace CodeMirror5 dep

remove CodeMirror5 and replace existing editors with HDS::CodeEditor

* chore: 🤖 deleting old codeeditor styles

* feat: 🎸 changing code editor

changing code editor

✅ Closes: 0

* removed unused export

* pr cleanup

* updating tests for new code editor

* responding to PR feedback

* added whitespace

* fixing linting violations

* chore: 🤖 self-review PR

self-review PR in the course of switching to new HDS code editor

* fix: 🐛 fix linting bug

* fix: 🐛 unneeded dependency

remove jslint dependency as this is handled in the new code editor from
HDS

* fix: 🐛 Fixing import bug

Fix import bug caused by importing scss modules that no longer exist

* Fix and refactor code editor acceptance test

* Fix and refactor code editor acceptance test

* chore: 🤖 remove codemirror dependency

* refactor: 💡 fix merge conflict issues

* test: 💍 update e2e test selectors

* test: 💍 fix target spec click event

* refactor: 💡 clean up test selectors for code editor

* refactor: 💡 clean up storage bucket test selectors

* chore: 🤖 remove code mirror modifiers

---------

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

* feat: 🎸 Create string formats component (#3191)

* feat: 🎸 Create string formats component

* Add missing copyright headers

* fix: 🐛 change to readonly

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

* feat: 🎸 add grant schema to mirage (#3196)

* feat: 🎸 add grant schema to mirage

* refactor: 💡 update grant schema mirage data

* refactor: 💡 fix route name in mirage

* feat: 🎸 add export options flyout (#3198)

* feat: 🎸 add export options flyout

* fix: 🐛 update id to be ids

* test: 💍 add a11y ignores

* test: 💍 fix tests for grant string format

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

* feat: 🎸 Add autocompletion to code editor (#3207)

* feat: 🎸 Add CSP nonce to code editors (#3230)

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* feat: 🎸 Add linting to code editor (#3211)

* feat: 🎸 Add linting to code editor

* refactor: 💡 add missing case for wildcard type & ids field

* refactor: 💡 simplify condition + add more tests

* refactor: 💡 cleanup ids validation logic and add more lint erro

* refactor: 💡 simplify logic and add actions linting cases

* feat: 🎸 Add autocompletion to code editor (#3207)

* refactor: 💡 apply PR feedback

* refactor: 💡 PR feedback

* refactor: 💡 add noop action

* fix: 🐛 noop should not be suggested with resource type format

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

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* Icu 18506 load resource IDs for autocomplete (#3247)

* feat: 🎸 Add id auto completion
* style: 💄 Fix code editor styling for dark mode
* feat: 🎸 Add optimization for caching sqlite data

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

* refactor: 💡 Update string formats to use select + add options (#3256)

* refactor: 💡 Update string formats to use select + add options

* refactor: 💡 PR feedback

* test: 💍 fix tests

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

* refactor: 💡 role grants tab to use code block (#3261)

* refactor: 💡 grants tab to use code block

* refactor: 💡 update getter comment

* test: 💍 fix tests

* refactor: 💡 remove language arg for codeblock

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

* Grant actions card (#3246)

* chore: 🤖 Add initial page layout for code editor + sidebar (#3180)

* chore: 🤖 Add initial page layout for code editor + sidebar

* test: 💍 add missing a11y ignores for exisitng color issue

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

* Replace `Rose::CodeEditor` with `Hds::CodeEditor` (#2679)

* chore: 🤖 replace Rose::CodeEditor with Hds::CodeEditor

- Remove CodeEditor 5 dependency

* chore: 🤖 Replace CodeMirror5 dep

remove CodeMirror5 and replace existing editors with HDS::CodeEditor

* chore: 🤖 deleting old codeeditor styles

* feat: 🎸 changing code editor

changing code editor

✅ Closes: 0

* removed unused export

* pr cleanup

* updating tests for new code editor

* responding to PR feedback

* added whitespace

* fixing linting violations

* chore: 🤖 self-review PR

self-review PR in the course of switching to new HDS code editor

* fix: 🐛 fix linting bug

* fix: 🐛 unneeded dependency

remove jslint dependency as this is handled in the new code editor from
HDS

* fix: 🐛 Fixing import bug

Fix import bug caused by importing scss modules that no longer exist

* Fix and refactor code editor acceptance test

* Fix and refactor code editor acceptance test

* chore: 🤖 remove codemirror dependency

* refactor: 💡 fix merge conflict issues

* test: 💍 update e2e test selectors

* test: 💍 fix target spec click event

* refactor: 💡 clean up test selectors for code editor

* refactor: 💡 clean up storage bucket test selectors

* chore: 🤖 remove code mirror modifiers

---------

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

* feat: 🎸 Create string formats component (#3191)

* feat: 🎸 Create string formats component

* Add missing copyright headers

* fix: 🐛 change to readonly

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

* feat: 🎸 add grant schema to mirage (#3196)

* feat: 🎸 add grant schema to mirage

* refactor: 💡 update grant schema mirage data

* refactor: 💡 fix route name in mirage

* feat: 🎸 add export options flyout (#3198)

* feat: 🎸 add export options flyout

* fix: 🐛 update id to be ids

* test: 💍 add a11y ignores

* test: 💍 fix tests for grant string format

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

* feat: 🎸 Add autocompletion to code editor (#3207)

* feat: 🎸 create grant actions component

* feat: 🎸 add description to actions

* feat: 🎸 update default logic to not show all actions

* feat: 🎸 show invalid id and type message

* test: 💍 create const for test selectors

* feat: 🎸 trigger action suggestion when cursor moves

* refactor: 💡 add createGrantLineHelpers func

* refactor: 💡 small refactors

* feat: 🎸 Add CSP nonce to code editors (#3230)

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* refactor: 💡 small refactor

* test: 💍 add more tests

* refactor: 💡 fix merge conflicts issues

* refactor: 💡 updates from PR feedback

* refactor: 💡 updates for completions and linting

* test: 💍 fix test issue with translations

* test: 💍 fix test issue that was already fixed once

* refactor: 💡 address PR feedback

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Co-authored-by: Zack Moore <zack.moore@hashicorp.com>
Co-authored-by: calcaide <ccorvo@hashicorp.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
Co-authored-by: Bharath Gajjala <120367134+bgajjala8@users.noreply.github.com>

* refactor: 💡 update pinned IDs logic for actions table

* refactor: 💡 use if/else in place of complex ternary

* refactor: 💡 user intl.formatList

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Co-authored-by: Zack Moore <zack.moore@hashicorp.com>
Co-authored-by: calcaide <ccorvo@hashicorp.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
Co-authored-by: Bharath Gajjala <120367134+bgajjala8@users.noreply.github.com>
2026-05-14 15:21:30 -04:00
Zhihe Li 0479c3c8fc test: 💍 Fix broken tests after new code editor (#3213) 2026-05-14 15:21:30 -04:00
08b8b03fed Grant actions card (#3246)
* chore: 🤖 Add initial page layout for code editor + sidebar (#3180)

* chore: 🤖 Add initial page layout for code editor + sidebar

* test: 💍 add missing a11y ignores for exisitng color issue

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

* Replace `Rose::CodeEditor` with `Hds::CodeEditor` (#2679)

* chore: 🤖 replace Rose::CodeEditor with Hds::CodeEditor

- Remove CodeEditor 5 dependency

* chore: 🤖 Replace CodeMirror5 dep

remove CodeMirror5 and replace existing editors with HDS::CodeEditor

* chore: 🤖 deleting old codeeditor styles

* feat: 🎸 changing code editor

changing code editor

✅ Closes: 0

* removed unused export

* pr cleanup

* updating tests for new code editor

* responding to PR feedback

* added whitespace

* fixing linting violations

* chore: 🤖 self-review PR

self-review PR in the course of switching to new HDS code editor

* fix: 🐛 fix linting bug

* fix: 🐛 unneeded dependency

remove jslint dependency as this is handled in the new code editor from
HDS

* fix: 🐛 Fixing import bug

Fix import bug caused by importing scss modules that no longer exist

* Fix and refactor code editor acceptance test

* Fix and refactor code editor acceptance test

* chore: 🤖 remove codemirror dependency

* refactor: 💡 fix merge conflict issues

* test: 💍 update e2e test selectors

* test: 💍 fix target spec click event

* refactor: 💡 clean up test selectors for code editor

* refactor: 💡 clean up storage bucket test selectors

* chore: 🤖 remove code mirror modifiers

---------

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

* feat: 🎸 Create string formats component (#3191)

* feat: 🎸 Create string formats component

* Add missing copyright headers

* fix: 🐛 change to readonly

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

* feat: 🎸 add grant schema to mirage (#3196)

* feat: 🎸 add grant schema to mirage

* refactor: 💡 update grant schema mirage data

* refactor: 💡 fix route name in mirage

* feat: 🎸 add export options flyout (#3198)

* feat: 🎸 add export options flyout

* fix: 🐛 update id to be ids

* test: 💍 add a11y ignores

* test: 💍 fix tests for grant string format

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

* feat: 🎸 Add autocompletion to code editor (#3207)

* feat: 🎸 create grant actions component

* feat: 🎸 add description to actions

* feat: 🎸 update default logic to not show all actions

* feat: 🎸 show invalid id and type message

* test: 💍 create const for test selectors

* feat: 🎸 trigger action suggestion when cursor moves

* refactor: 💡 add createGrantLineHelpers func

* refactor: 💡 small refactors

* feat: 🎸 Add CSP nonce to code editors (#3230)

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* refactor: 💡 small refactor

* test: 💍 add more tests

* refactor: 💡 fix merge conflicts issues

* refactor: 💡 updates from PR feedback

* refactor: 💡 updates for completions and linting

* test: 💍 fix test issue with translations

* test: 💍 fix test issue that was already fixed once

* refactor: 💡 address PR feedback

---------

Co-authored-by: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Co-authored-by: Zack Moore <zack.moore@hashicorp.com>
Co-authored-by: calcaide <ccorvo@hashicorp.com>
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
Co-authored-by: Bharath Gajjala <120367134+bgajjala8@users.noreply.github.com>
2026-05-14 15:21:29 -04:00
Lisbet Alvarez 1a8c895ea9 refactor: 💡 role grants tab to use code block (#3261)
* refactor: 💡 grants tab to use code block

* refactor: 💡 update getter comment

* test: 💍 fix tests

* refactor: 💡 remove language arg for codeblock

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18757
2026-05-14 15:21:29 -04:00
Lisbet Alvarez 990146fa34 refactor: 💡 Update string formats to use select + add options (#3256)
* refactor: 💡 Update string formats to use select + add options

* refactor: 💡 PR feedback

* test: 💍 fix tests

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18983
2026-05-14 15:21:29 -04:00
Zhihe Li e5657a8d4c Icu 18506 load resource IDs for autocomplete (#3247)
* feat: 🎸 Add id auto completion
* style: 💄 Fix code editor styling for dark mode
* feat: 🎸 Add optimization for caching sqlite data

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18506
2026-05-14 15:21:29 -04:00
Lisbet AlvarezandZhihe Li 1d126dc074 feat: 🎸 Add linting to code editor (#3211)
* feat: 🎸 Add linting to code editor

* refactor: 💡 add missing case for wildcard type & ids field

* refactor: 💡 simplify condition + add more tests

* refactor: 💡 cleanup ids validation logic and add more lint erro

* refactor: 💡 simplify logic and add actions linting cases

* feat: 🎸 Add autocompletion to code editor (#3207)

* refactor: 💡 apply PR feedback

* refactor: 💡 PR feedback

* refactor: 💡 add noop action

* fix: 🐛 noop should not be suggested with resource type format

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

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2026-05-14 15:21:29 -04:00
Bharath GajjalaandZhihe Li dc224268f5 feat: 🎸 Add CSP nonce to code editors (#3230)
Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2026-05-14 15:21:28 -04:00
Zhihe Li 4c15d72321 feat: 🎸 Add autocompletion to code editor (#3207) 2026-05-14 15:21:28 -04:00
Lisbet Alvarez add2a9d6c4 feat: 🎸 add export options flyout (#3198)
* feat: 🎸 add export options flyout

* fix: 🐛 update id to be ids

* test: 💍 add a11y ignores

* test: 💍 fix tests for grant string format

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18524
2026-05-14 15:21:28 -04:00
Cameron Perera 759c1f5555 feat: 🎸 add grant schema to mirage (#3196)
* feat: 🎸 add grant schema to mirage

* refactor: 💡 update grant schema mirage data

* refactor: 💡 fix route name in mirage
2026-05-14 15:21:28 -04:00
Lisbet Alvarez 37de7e3480 feat: 🎸 Create string formats component (#3191)
* feat: 🎸 Create string formats component

* Add missing copyright headers

* fix: 🐛 change to readonly

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18458
2026-05-14 15:21:28 -04:00
d469f305de Replace Rose::CodeEditor with Hds::CodeEditor (#2679)
* chore: 🤖 replace Rose::CodeEditor with Hds::CodeEditor

- Remove CodeEditor 5 dependency

* chore: 🤖 Replace CodeMirror5 dep

remove CodeMirror5 and replace existing editors with HDS::CodeEditor

* chore: 🤖 deleting old codeeditor styles

* feat: 🎸 changing code editor

changing code editor

✅ Closes: 0

* removed unused export

* pr cleanup

* updating tests for new code editor

* responding to PR feedback

* added whitespace

* fixing linting violations

* chore: 🤖 self-review PR

self-review PR in the course of switching to new HDS code editor

* fix: 🐛 fix linting bug

* fix: 🐛 unneeded dependency

remove jslint dependency as this is handled in the new code editor from
HDS

* fix: 🐛 Fixing import bug

Fix import bug caused by importing scss modules that no longer exist

* Fix and refactor code editor acceptance test

* Fix and refactor code editor acceptance test

* chore: 🤖 remove codemirror dependency

* refactor: 💡 fix merge conflict issues

* test: 💍 update e2e test selectors

* test: 💍 fix target spec click event

* refactor: 💡 clean up test selectors for code editor

* refactor: 💡 clean up storage bucket test selectors

* chore: 🤖 remove code mirror modifiers

---------

Co-authored-by: calcaide <ccorvo@hashicorp.com>
Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
2026-05-14 15:21:27 -04:00
Lisbet Alvarez fd1eba617b chore: 🤖 Add initial page layout for code editor + sidebar (#3180)
* chore: 🤖 Add initial page layout for code editor + sidebar

* test: 💍 add missing a11y ignores for exisitng color issue

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18460
2026-05-14 15:21:27 -04:00
Zhihe Li 5048fd478a feat: 🎸 Handle rate limiting (#3257)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12035
2026-05-14 15:21:07 -04:00
Susmitha Girumala de907284ff chore: 🤖 Upgrade path-to-regexp to latest (#3260)
* chore: 🤖 Upgrade path-to-regexp to latest

Fix multiple path-to-regexp sec vulns up upgrading to latest possible
versions. This fixes:
- https://github.com/hashicorp/boundary-ui/security/code-scanning/272
- https://github.com/hashicorp/boundary-ui/security/code-scanning/269
- https://github.com/hashicorp/boundary-ui/security/code-scanning/266

* chore: 🤖 Resolve broken pnpm lockfile after merge with install
2026-05-14 11:32:21 -04:00
Zhihe Li 6e9fe1c675 docs: ✏️ Add documentation for pnpm overrides (#3263) 2026-05-13 12:54:34 -04:00
Cameron Perera 6b29a875f3 test: 💍 update json credential flaky tests (#3267)
* test: 💍 update json credential flaky tests

* test: 💍 add commonSelector for code mirror editor
2026-05-13 11:26:41 -05:00
Michael Li 1fd638618b doc: No more enos variables (#3262) 2026-05-13 09:51:13 -04:00
dependabot[bot] 6704a251e0 chore(deps): bump pnpm/action-setup from 5.0.0 to 6.0.8 (#3249)
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5.0.0 to 6.0.8.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/fc06bc1257f339d1d5d8b3a19a8cae5388b55320...0e279bb959325dab635dd2c09392533439d90093)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: 6.0.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 16:47:41 -04:00
Zhihe Li 46ed965360 chore: 🤖 Update node version (#3265) 2026-05-12 16:02:21 -04:00
Susmitha Girumala 283e128139 chore: 🤖 Fix axios cve-2026-42038 (#3259)
* chore: 🤖 Fix axios cve-2026-42038 by adding override to latest

✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/327

* chore: 🤖 Undo axios override in favour of updating parent mod

Update openapi-generator-cli instead of dependent axios
2026-05-11 15:05:08 -04:00
Zhihe Li 047db02a65 fix: 🐛 Add extra check for SQLite errors (#3258) 2026-05-08 10:30:05 -04:00
Zhihe Li bd837a6383 fix: 🐛 Fix race condition from sqlite initialization (#3253) 2026-05-05 10:25:40 -04:00
dependabot[bot] 2a6fac66fc chore(deps-dev): bump postcss from 8.5.3 to 8.5.10 (#3255) 2026-05-04 16:17:13 -07:00
dependabot[bot] 4bdcd4ce69 chore(deps): bump actions/setup-node from 6.3.0 to 6.4.0 (#3252) 2026-05-04 15:21:37 -07:00
dependabot[bot] 9f76650dfb chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#3251) 2026-05-04 14:51:54 -07:00
dependabot[bot] c33d1718a2 chore(deps): bump slackapi/slack-github-action from 3.0.1 to 3.0.2 (#3250) 2026-05-04 14:07:44 -07:00
Priya Patel 923dedc9bb fix: 🐛 hides preferred clients section for linux (#3241) 2026-04-24 10:31:42 -07:00
dependabot[bot] 2d9b9d9659 chore(deps): bump uuid from 11.1.0 to 14.0.0 (#3245)
Bumps [uuid](https://github.com/uuidjs/uuid) from 11.1.0 to 14.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v11.1.0...v14.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 09:41:13 -04:00
Michael Li 6d7e24fdc5 chore(e2e): Log in via CLI during global setup to update cache (#3235)
* chore(e2e): log in via CLI

* CR

* CR: Rename ci option
2026-04-23 11:57:24 -04:00
Dharini 4f9086642b fix: 🐛 refresh action in session recording (#3229) 2026-04-22 10:54:50 -07:00
Susmitha Girumala 337963bff8 chore: 🤖 Upgrade socket.io-parser to fix sec vuln (#3244)
Fixes CVE 2026 33151

✅ Closes: https://github.com/hashicorp/boundary-ui/security/dependabot/269
2026-04-22 12:25:35 -04:00
Cameron Perera b87ed9f3db chore: 🤖 bump xmldom in electron-app (#3243) 2026-04-21 15:40:03 -05:00
dependabot[bot]andcameronperera 95b78299f9 chore(deps): bump brace-expansion from 1.1.12 to 1.1.13 in /ui/desktop/electron-app (#3210)
* chore(deps): bump brace-expansion in /ui/desktop/electron-app

Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.13.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.13)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: 🤖 bump brace-expansion package

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
2026-04-21 13:39:20 -05:00
Cameron Perera cf0df900e0 chore: 🤖 bump lodash to 4.18.1 (#3242) 2026-04-21 11:36:01 -05:00
dependabot[bot]andcameronperera 214304dc8c chore(deps): bump lodash from 4.17.23 to 4.18.1 in /ui/desktop/electron-app (#3236)
* chore(deps): bump lodash in /ui/desktop/electron-app

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: 🤖 bump lodash version in electron-app

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
2026-04-21 10:55:03 -05:00
Susmitha Girumala ed8075d200 chore: 🤖 Update flatted dep to latest (#3240)
Update flatted to latest >=3.4.0 version to fix DoS vuln CVE-2026-32141
2026-04-17 10:00:59 -04:00
Susmitha Girumala 07089e97e2 chore: 🤖 Update immutable to fix CVE 2026 29063 (#3237)
* chore: 🤖 Update immutable to fix CVE 2026 29063

Tag immutable to latest 5.1.5 version to fix prototype pollution vuln

✅ Closes: https://github.com/hashicorp/boundary-ui/security/code-scanning/239

* chore: 🤖 Undo immutable override in favor of updating only deps
2026-04-15 11:59:27 -04:00
Dharini 14b3c8c6d0 fix: 🐛 @xmldom/xmldom version update (#3238) 2026-04-14 14:21:57 -07:00
Zhihe Li 6580350dcf feat: 🎸 Update AWS host catalog fields to be required (#3214) 2026-04-13 16:33:41 -04:00
Michael Li 51be681758 chore: Always run end-to-end tests (#3234)
* chore: Always run e2e tests

* chore: Parallelize browsers
2026-04-13 15:08:21 -04:00
Susmitha Girumala dab50e352d chore: 🤖 Update underscore to fix vulnerability (#3231)
* chore: 🤖 Update underscore to fix vulnerability

Code scanning identified a potential DoS attach for underscore versions
not using latest 1.13.8 release

✅ Closes: https://github.com/jashkenas/underscore/issues/3011

* chore: 🤖 Remove nomnom module underscore override
2026-04-09 16:14:45 -04:00
dependabot[bot] f3af41dea4 chore(deps): bump hashicorp/action-setup-enos from 1.50 to 1.52 (#3221) 2026-04-08 09:51:01 -07:00
dependabot[bot] fb3f88a77d chore(deps): bump slackapi/slack-github-action from 2.1.1 to 3.0.1 (#3223) 2026-04-08 09:08:54 -07:00
dependabot[bot] 7345c79b20 chore(deps): bump actions/setup-go from 6.0.0 to 6.4.0 (#3220) 2026-04-07 16:18:41 -07:00
dependabot[bot] 9ed79be3ef chore(deps): bump actions/setup-node from 6.2.0 to 6.3.0 (#3222) 2026-04-07 15:37:22 -07:00
dependabot[bot] 7614f17efb chore(deps): bump pnpm/action-setup from 4.2.0 to 5.0.0 (#3219) 2026-04-07 11:59:18 -07:00
301 changed files with 13989 additions and 9509 deletions

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
name: ci
on:
push:
branches: [main]
tags-ignore: ["**"]
workflow_dispatch:
schedule:
- cron: "41 5 * * *" # nightly: dependency audit + full test run
# One job on purpose: the shared runner pool is small, so everything runs
# sequentially on a single runner instead of a matrix.
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 90
env:
CI: "true"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: install pnpm
run: |
npm install -g pnpm@10.33.3
pnpm --version
- name: install dependencies
# Lifecycle scripts are skipped: the desktop postinstall compiles
# electron-app native modules, which the admin build and ember tests
# do not need.
run: pnpm install --frozen-lockfile --ignore-scripts
- name: install headless chrome
# Debian/Ubuntu chromium from the distro archive (fast, mirrored),
# exposed under the name testem's "Chrome" launcher looks for.
shell: bash
run: |
set -euo pipefail
if ! command -v google-chrome >/dev/null 2>&1; then
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
export DEBIAN_FRONTEND=noninteractive
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq --no-install-recommends chromium \
|| $SUDO apt-get install -y -qq --no-install-recommends chromium-browser
CHROME_BIN=$(command -v chromium || command -v chromium-browser)
mkdir -p "$HOME/bin"
ln -sf "$CHROME_BIN" "$HOME/bin/google-chrome"
echo "$HOME/bin" >> "$GITHUB_PATH"
fi
PATH="$HOME/bin:$PATH" google-chrome --version
- name: build admin ui
run: pnpm --filter admin build
- name: test addons
run: |
for a in core auth rose; do
echo "::group::addons/$a"
(cd addons/$a && npx ember test --test-port 0)
echo "::endgroup::"
done
echo "::group::addons/api"
(cd addons/api && npx ember exam --test-port 0 --split=4 --parallel=1)
echo "::endgroup::"
- name: test desktop ui
run: cd ui/desktop && npx ember test --test-port 0
- name: test admin ui
# The test build is made once and the partitions then run against the
# static output, one browser at a time. Rebuilding in-process (and four
# concurrent browsers) stalled on the small shared runners.
run: |
cd ui/admin
npx ember build --environment=test --output-path="$RUNNER_TEMP/admin-test-dist"
for p in 1 2 3 4; do
echo "::group::admin partition $p/4"
timeout 25m npx ember exam --test-port 0 --path "$RUNNER_TEMP/admin-test-dist" --split=4 --partition=$p
echo "::endgroup::"
done
- name: dependency audit
# Fails on critical advisories only; remaining high/moderate advisories
# are build/test tooling and are tracked in pnpm-overrides.md.
run: |
pnpm audit --audit-level=critical
(cd ui/desktop/electron-app && pnpm audit --audit-level=critical)
-2
View File
@@ -1,2 +0,0 @@
# Anyone attached to this rule will always been assigned to a pull request
* @hashicorp/boundary-ui
-67
View File
@@ -1,67 +0,0 @@
---
name: Bug report
about: Let us know about a bug!
title: ''
labels: ''
assignees: ''
---
<!-- Please reserve GitHub issues for bug reports and feature requests.
For questions, the best place to get answers is on our [discussion forum](https://discuss.hashicorp.com/c/boundary), as they will get more visibility from experienced users than the issue tracker.
Please note: We take Boundary's security and our users' trust very seriously. If you believe you have found a security issue in Boundary, please responsibly disclose this by contacting us at security@hashicorp.com. Our PGP key is available on [our security page](https://www.hashicorp.com/security/).
Logging is available on Boundary Desktop Client version >= 2.1.0
Logs are found at:
Linux: ~/.config/Boundary/logs/desktop-client.log
MacOS: ~/Library/Logs/Boundary/desktop-client.log
Windows: $env:USERPROFILE\AppData\Roaming\Boundary\logs\desktop-client.log
If requests are taking longer than expected, please change the logging level to DEBUG. This will log the cache daemon request times as well.
-->
## :bug: Bug Report
### **Description**
_A clear and concise description of the bug._
### **Steps to Reproduce**
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See the error
## **Expected behavior**
_A clear description of what you expected to happen._
## **Actual behavior**
_A clear description of what actually happened._
## **Screenshots (if applicable)**
_Add screenshots or screen recordings to help explain the issue._
## **Environment**
- **OS and Version:** (e.g., Windows 10 (22H2), macOS (14.7.2))
- **Architecture:** (e.g., arm64, x86_64)
- **Browser/Version:** (if applicable)
- **Desktop Client Version:** (if applicable)
- **Controller/worker versions:** (if applicable)
## **Logs & Console Output (if applicable)**
_Paste any relevant error logs or browser console output here._
## **Additional Context**
_Any other details that might be relevant._
-7
View File
@@ -1,7 +0,0 @@
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: BUSL-1.1
contact_links:
- name: Ask a question
url: https://discuss.hashicorp.com/c/boundary
about: For increased visibility, please post questions on the discussion forum.
-23
View File
@@ -1,23 +0,0 @@
---
name: Feature request
about: Suggest something!
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Explain any additional use-cases**
If there are any use-cases that would help us understand the use/need/value please share them as they can help us decide on acceptance and prioritization.
**Additional context**
Add any other context or screenshots about the feature request here.
-6
View File
@@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
-30
View File
@@ -1,30 +0,0 @@
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: BUSL-1.1
addons:
- changed-files:
- any-glob-to-any-file: "addons/**/*"
api-addon:
- changed-files:
- any-glob-to-any-file: "addons/api/**/*"
auth-addon:
- changed-files:
- any-glob-to-any-file: "addons/auth/**/*"
styles-addon:
- changed-files:
- any-glob-to-any-file: "addons/rose/**/*"
ui:
- changed-files:
- any-glob-to-any-file: "ui/**/*"
admin:
- changed-files:
- any-glob-to-any-file: "ui/admin/**/*"
desktop:
- changed-files:
- any-glob-to-any-file: "ui/desktop/**/*"
-33
View File
@@ -1,33 +0,0 @@
# Description
<!-- Add a brief description of changes here -->
<!-- Uncomment line below to manually add link to JIRA ticket -->
<!-- :tickets: [Jira ticket](https://hashicorp.atlassian.net/browse/JIRA_TICKET_NUMBER) -->
## Screenshots (if appropriate)
## How to Test
<!-- Add steps to test this change. Include any other necessary relevant links -->
## Checklist
<!-- strikethrough the checklist item that is not relevant to your change -->
<!-- If you are merging a long lived branch, major feature, or UI altering changes,
please add the a11y-tests label. Other cases, like a dependency change or
translation update likely does not need an a11y audit -->
- [ ] I have added before and after screenshots for UI changes
- [ ] I have added JSON response output for API changes
- [ ] I have added steps to reproduce and test for bug fixes in the description
- [ ] I have commented on my code, particularly in hard-to-understand areas
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] I have added `a11y-tests` label to run a11y audit tests if needed
## PCI review checklist
<!-- heimdall_github_prtemplate:grc-pci_dss-2024-01-05 -->
- [ ] I have documented a clear reason for, and description of, the change I am making.
- [ ] If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.
- [ ] If applicable, I've documented the impact of any changes to security controls.
Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.
-95
View File
@@ -1,95 +0,0 @@
#!/usr/bin/env bash
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: BUSL-1.1
set -euo pipefail
# Configure Git
git config --global user.email "hc-github-team-secure-boundary@hashicorp.com"
git config --global user.name "github-team-secure-boundary"
# Fetch branches from ENT
git fetch origin
BRANCH=${1}
echo "BRANCH to merge is $BRANCH"
git checkout "${BRANCH}"
# Checkout OSS repo
git remote add oss-remote https://github.com/hashicorp/boundary-ui.git
git fetch oss-remote
# Merge oss/${BRANCH} into ent/${BRANCH}
git_merge_branch="${2}"
git checkout -b "${git_merge_branch}"
latest_oss_commit="$(git rev-parse --short oss-remote/${BRANCH})"
echo "Trying to merge the latest oss commit - ${latest_oss_commit} from oss/${BRANCH} into ent/${BRANCH}"
if ! errors=$(git merge -m "Merge Boundary UI OSS branch '${BRANCH}' at commit ${latest_oss_commit}" "${latest_oss_commit}"); then
# Here we can try to auto resolve conflicts
# This logic will vary from project to project. The below is just an example.
conflicts=$(git diff --name-only --diff-filter=U)
printf "Found conflicts:\n${conflicts}\n"
# Resolve package.json conflicts by taking oss
if echo "$conflicts" | grep -q package.json; then
echo "Checking out oss package.json..."
git checkout --theirs package.json
git add package.json
fi
# Resolve pnpm-lock.yaml conflicts by re-running pnpm install:
# https://pnpm.io/git#merge-conflicts
if echo "$conflicts" | grep -q pnpm-lock.yaml; then
echo "Re-running pnpm install to fix conflicts by regenerating pnpm-lock.yaml..."
pnpm install
git add pnpm-lock.yaml
fi
# If we can commit all the changes without conflict, we have resolved everything automatically
if ! git commit --no-edit; then
echo "Auto Merge failed, creating a draft PR"
git merge --abort
git clean -xfd
git fetch oss-remote
git checkout "${OSS_BRANCH}"
git checkout -b "${OSS_LOCAL_BRANCH}"
git push -f --set-upstream origin "${OSS_LOCAL_BRANCH}"
gh pr create \
--draft \
--fill-first \
--label "oss-merge" \
-B "${BASE_BRANCH}" \
-H "${OSS_LOCAL_BRANCH}" \
-a "${ACTOR}" \
-t "[${BASE_BRANCH/oss\/release\//}] OSS to ENT merge of (${SHA::7})" \
-b "${BASE_BRANCH/oss\//} upstream merge of (${SHA::7})
Merge failed, Please fix the conflicts using github UI or using the following commands:
\`\`\`bash
git checkout ${BASE_BRANCH}
git pull
git checkout ${OSS_LOCAL_BRANCH}
git merge ${BASE_BRANCH}
# fix conflicts and commit
git push origin ${OSS_LOCAL_BRANCH}
\`\`\`"
exit 0
fi
fi
# Check that there are actually changes to merge by comparing the
# merge branch to the current checked out branch
if (git diff ${BRANCH} --exit-code > /dev/null); then
echo "there are no changes to merge"
exit 0
fi
git push origin "${git_merge_branch}"
gh pr create \
--fill-first \
--label "oss-merge" \
-B "${BASE_BRANCH}" \
-H "${MERGE_BRANCH}" \
-a ${ACTOR} \
-t "[${BASE_BRANCH/oss\/release\//}] OSS to ENT merge of (${SHA::7})" \
-b "${BASE_BRANCH/oss\//} upstream merge of (${SHA::7})"
gh pr merge --auto --merge
# This PR will be need to be approved manually
-56
View File
@@ -1,56 +0,0 @@
name: A11y Tests
on:
pull_request:
types: [labeled, opened, synchronize, reopened]
workflow_dispatch:
jobs:
a11y-test:
name: Run A11y Tests
if: contains(github.event.pull_request.labels.*.name, 'a11y-tests')
runs-on: ${{ fromJSON(vars.RUNNER) }}
timeout-minutes: 40
steps:
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
with:
install-dependencies: true
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install
- name: Run a11y tests
env:
# this will generate the test reports but causes tests to pass even when there a11y failures
ENABLE_A11Y_MIDDLEWARE_REPORTER: true
run: pnpm test-a11y
- name: Consolidate ember a11y reports
run: |
mkdir ember-a11y-reports
jq -s 'map(.[])' ./ui/admin/ember-a11y-report/*.json > ember-a11y-reports/admin.json
jq -s 'map(.[])' ./ui/desktop/ember-a11y-report/*.json > ember-a11y-reports/desktop.json
- name: Output ember a11y test report summary (admin)
run: 'cat ember-a11y-reports/admin.json | jq "map({moduleName,testName,routes,urls,violations: .violations | map({id, nodes: .nodes | map({failureSummary, targets: .[].[]?.relatedNodes?.[]?.target? })}) })"'
- name: Output ember a11y test report summary (desktop)
run: 'cat ember-a11y-reports/desktop.json | jq "map({moduleName,testName,routes,urls,violations: .violations | map({id, nodes: .nodes | map({failureSummary, targets: .[].[]?.relatedNodes?.[]?.target? })}) })"'
- name: Upload ember a11y reports
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ember-a11y-reports
path: ./ember-a11y-reports
- name: Report a11y test results status
if: always()
# fail CI when the combined array length of admin and desktop a11y reports is not 0
run: |
EMBER_A11Y_FAILURE_COUNT=$(jq -s 'map(.[]) | length' ember-a11y-reports/admin.json ember-a11y-reports/desktop.json)
echo "$EMBER_A11Y_FAILURE_COUNT ember-a11y test failures"
if [ "$EMBER_A11Y_FAILURE_COUNT" != '0' ]; then
exit 1
fi
-94
View File
@@ -1,94 +0,0 @@
name: Build Admin UI
on:
push:
branches:
- main
- "release/**"
workflow_dispatch:
jobs:
dependencies:
name: Install dependencies
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
build-admin-ui-oss:
name: Build Admin UI OSS
needs: [dependencies]
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- name: Build
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm run build:ui:admin:oss
- name: Upload artifact admin-ui
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: admin-ui
path: ./ui/admin/dist/
- name: Upload artifact OSS admin UI
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: admin-ui-oss
path: ./ui/admin/dist/
build-admin-ui-enterprise:
name: Build Admin UI Enterprise
needs: [dependencies]
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- name: Build
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm run build:ui:admin:enterprise
- name: Upload artifact enterprise admin UI
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: admin-ui-ent
path: ./ui/admin/dist/
build-admin-ui-hcp:
name: Build Admin UI HCP
needs: [dependencies]
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- name: Build
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm run build:ui:admin:hcp
- name: Upload artifact HCP admin UI
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: admin-ui-hcp
path: ./ui/admin/dist/
-62
View File
@@ -1,62 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [main]
pull_request:
# The branches below must be a subset of the branches above
branches: [main]
schedule:
- cron: '0 19 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ${{ fromJSON(vars.RUNNER) }}
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@70df9def86d22bf0ea4e7f8b956e7b92e7c1ea22 # codeql-bundle-v2.20.7
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@70df9def86d22bf0ea4e7f8b956e7b92e7c1ea22 # codeql-bundle-v2.20.7
-19
View File
@@ -1,19 +0,0 @@
name: Add Copyright Headers
on: pull_request
jobs:
copywrite:
runs-on: ${{ fromJSON(vars.RUNNER) }}
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Ref and repository aren't normally needed, this is a workaround for a bug in auto commit
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 # v1.1.3
- name: Add missing Copyright Headers
run: copywrite headers
- uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
with:
commit_message: Add missing copyright headers
-11
View File
@@ -1,11 +0,0 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
-74
View File
@@ -1,74 +0,0 @@
name: Validate Monorepo
on: pull_request
jobs:
dependencies:
name: Install dependencies
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
compliance:
name: License Compliance
needs: [dependencies]
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm run compliance:licenses
lint:
name: Lint
needs: [dependencies, compliance]
runs-on: ${{ fromJSON(vars.RUNNER) }}
timeout-minutes: 5
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm lint
test:
name: Test
needs: [lint]
runs-on: ${{ fromJSON(vars.RUNNER) }}
timeout-minutes: 15
steps:
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
with:
install-dependencies: true
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --offline
- run: pnpm test
@@ -1,25 +0,0 @@
name: Trigger OSS to Enterprise Merge
on:
pull_request_target:
types:
- closed
branches:
- main
jobs:
trigger-oss-merge:
# Run this only on merge events in OSS repo
if: ${{ github.event.pull_request.merged && github.repository == 'hashicorp/boundary-ui' }}
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- name: Trigger Merge
env:
GIT_REF: ${{ github.ref_name }}
GIT_SHA: ${{ github.sha }}
GH_PAT: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
GIT_ACTOR: ${{ github.actor }}
run: |
curl -H "Authorization: token $GH_PAT" \
-H 'Accept: application/json' \
-d "{\"event_type\": \"oss-merge\", \"client_payload\": {\"git-ref\": \"${GIT_REF}\", \"git-sha\": \"${GIT_SHA}\", \"git-actor\": \"${GIT_ACTOR}\" }}" \
"https://api.github.com/repos/hashicorp/boundary-ui-enterprise/dispatches"
-24
View File
@@ -1,24 +0,0 @@
name: OSS → ENT Merge
on:
repository_dispatch:
types: [ oss-merge ]
jobs:
oss-merge:
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
- run: |
git fetch --prune --unshallow
./.github/scripts/merge.sh "${BASE_BRANCH}" "${MERGE_BRANCH}"
env:
BASE_BRANCH: ${{ github.event.client_payload.git-ref }}
OSS_BRANCH: oss-remote/${{ github.event.client_payload.git-ref }}
OSS_LOCAL_BRANCH: oss/${{ github.event.client_payload.git-ref }}-${{ github.event.client_payload.git-sha }}
MERGE_BRANCH: oss/merge-${{ github.event.client_payload.git-ref }}-${{github.event.client_payload.git-sha}}
GITHUB_TOKEN: ${{secrets.ELEVATED_GITHUB_TOKEN }}
MERGE_APPROVE_TOKEN: ${{secrets.ELEVATED_GITHUB_TOKEN }}
ACTOR: ${{ github.event.client_payload.git-actor }}
SHA: ${{github.event.client_payload.git-sha}}
-252
View File
@@ -1,252 +0,0 @@
name: Test E2E Admin UI
on:
pull_request:
types: [labeled, opened, synchronize, reopened]
paths:
- 'ui/admin/**'
- 'e2e-tests/*'
- 'e2e-tests/api-client/**'
- 'e2e-tests/helpers/**'
- 'e2e-tests/admin/**'
- '.github/workflows/test-e2e-admin-ui.yaml'
workflow_dispatch:
inputs:
boundary-enterprise-branch:
description: 'Branch of boundary-enterprise to test against (default to main)'
required: true
default: 'main'
type: string
boundary-branch:
description: 'Branch of boundary to test against (default to main)'
required: true
default: 'main'
type: string
jobs:
test:
name: test
# don't need to run e2e tests on ce -> ent merges since tests were already
# run in the ce branch, and there are currently no code diffferences between
# the two repos
if: github.actor != 'hc-github-team-secure-boundary'
runs-on: ${{ fromJSON(vars.RUNNER_UBUNTU_22) }}
strategy:
matrix:
include:
- boundary_edition: community
github_repo: hashicorp/boundary
folder_name: boundary
boundary_edition_short: oss
enos_scenario: e2e_ui_docker
test_command: admin:ce:docker
boundary_branch: ${{ github.event.inputs.boundary-branch || 'main' }}
- boundary_edition: enterprise
github_repo: hashicorp/boundary-enterprise
folder_name: boundary-enterprise
boundary_edition_short: ent
enos_scenario: e2e_ui_docker_ent
test_command: admin:ent:docker
boundary_branch: ${{ github.event.inputs.boundary-enterprise-branch || 'main' }}
fail-fast: false
env:
ENOS_VAR_e2e_debug_no_run: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- name: Set up Node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: "pnpm"
cache-dependency-path: |
pnpm-lock.yaml
ui/desktop/electron-app/pnpm-lock.yaml
- name: Set up Terraform
uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85
- name: Set up Enos
uses: hashicorp/action-setup-enos@17b90fcf9591275b468a94aefb9dc6a93017de8a
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Vault CLI
run: |
wget https://releases.hashicorp.com/vault/1.17.6/vault_1.17.6_linux_amd64.zip -O /tmp/vault.zip
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
unzip -o -q /tmp/vault.zip -d /usr/local/bin
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
unzip -o -q /tmp/vault.zip -d "$HOME/bin"
fi
- name: GPG setup
run: |
# Create a GPG key
export KEY_PW=boundary
gpg --generate-key --batch <<eoGpgConf
%echo Started!
Key-Type: RSA
Key-Length: default
Subkey-Type: RSA
Name-Real: boundary
Name-Comment: default
Name-Email: default
Expire-Date: 0
Passphrase: $KEY_PW
%commit
%echo Done.
eoGpgConf
# Enable gpg-preset-passphrase so that key passwords can be saved
echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf
gpg-connect-agent reloadagent /bye &>/dev/null
# Get information about the created keys
export lines=$(gpg --list-secret-keys --with-colons --with-keygrip)
export KEY_ID=""
while read -r line
do
# Save the first key id to be used later
if [[ $line =~ "fpr"* ]]; then
if [[ $KEY_ID == "" ]]; then
KEY_ID="$(echo "$line" | sed -r 's/fpr|://g')"
fi
fi
# Cache the passphrases for the keys so passwords do not need to be entered
if [[ $line =~ "grp"* ]]; then
export KEYGRIP_ID="$(echo "$line" | sed -r 's/grp|://g')"
/usr/lib/gnupg/gpg-preset-passphrase --preset -P $KEY_PW $KEYGRIP_ID
fi
done <<< $lines
# Trust the key
touch /tmp/test.txt
gpg -a --encrypt -r $KEY_ID --trust-model always --batch --yes /tmp/test.txt
echo "trusted-key $KEY_ID" >> ~/.gnupg/gpg.conf
# Initialize the password store
sudo apt-get update
sudo apt-get install -y pass
pass init $KEY_ID &>/dev/null
- name: Checkout ${{ matrix.folder_name }} repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: ${{ matrix.github_repo }}
path: support/src/${{ matrix.folder_name }}
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
ref: ${{ matrix.boundary_branch }}
- name: GH fix for localhost resolution
run: |
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
cat /etc/hosts && echo "-----------"
sudo sed -i 's/::1 *localhost ip6-localhost ip6-loopback/::1 ip6 -localhost ip6-loopback/g' /etc/hosts
cat /etc/hosts
ssh -V
fi
- name: Set git config
run: |
git config --global url."https://oauth2:${{ secrets.ELEVATED_GITHUB_TOKEN }}@github.com".insteadOf "https://github.com"
- name: Install node dependencies
run: |
pnpm install --frozen-lockfile
cd e2e-tests
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
pnpm playwright install --with-deps
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
# playwright dependencies can't be installed on a self-hosted runner
# due to permission issues
pnpm playwright install
fi
- name: Determine Go version
id: get-go-version
run: |
echo "Building with Go $(cat ./support/src/${{matrix.folder_name}}/.go-version)"
echo "go-version=$(cat ./support/src/${{matrix.folder_name}}/.go-version)" >> "$GITHUB_OUTPUT"
- name: Set up Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: ${{ steps.get-go-version.outputs.go-version }}
cache: false
- name: Set up test infra
id: infra
run: |
ssh-keygen -N '' -t ed25519 -f ~/.ssh/github_enos
mkdir -p ./enos/terraform-plugin-cache
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
echo ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} > ./support/src/${{matrix.folder_name}}/internal/ui/VERSION
export ENOS_VAR_ui_build_override="oss"
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
echo ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} > ./support/src/${{matrix.folder_name}}/internal/ui/VERSION_ent
export ENOS_VAR_ui_build_override="enterprise"
fi
export ENOS_VAR_boundary_edition=${{ matrix.boundary_edition_short }} && \
export ENOS_VAR_enos_user=$GITHUB_ACTOR && \
export ENOS_VAR_aws_ssh_private_key_path=~/.ssh/github_enos && \
export ENOS_VAR_boundary_license="${{ secrets.BOUNDARY_ENT_LICENSE }}" && \
enos scenario launch --timeout 60m0s --chdir ./support/src/${{matrix.folder_name}}/enos ${{matrix.enos_scenario}} builder:local
- name: Move boundary binary
run: |
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
mv ./support/src/${{matrix.folder_name}}/bin/boundary /usr/local/bin/
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
mv ./support/src/${{matrix.folder_name}}/bin/boundary $HOME/bin
fi
boundary version
- name: Run Tests
run: |
source <(bash ./support/src/${{matrix.folder_name}}/enos/scripts/test_e2e_env.sh)
cd e2e-tests
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
pnpm run ${{ matrix.test_command }} --reporter=html
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
# only run tests on chromium because playwright dependencies can't
# be installed to support other browsers
pnpm run ${{ matrix.test_command }} --project=chromium --reporter=html
fi
- name: Upload Playwright report
if: ${{ failure() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: playwright-report-${{ matrix.boundary_edition }}
path: e2e-tests/playwright-report
- name: Clean up test infra
if: ${{ always() }}
continue-on-error: true
run: |
export ENOS_VAR_boundary_edition=${{ matrix.boundary_edition_short }} && \
export ENOS_VAR_enos_user=$GITHUB_ACTOR && \
export ENOS_VAR_aws_ssh_private_key_path=~/.ssh/github_enos && \
enos scenario destroy --timeout 60m0s --chdir ./support/src/${{matrix.folder_name}}/enos ${{matrix.enos_scenario}} builder:local
- name: Send Slack message on failure
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
if: ${{ failure() && github.repository == 'hashicorp/boundary-ui' }}
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_CHANNEL_ID }}
text: ":x: admin ui e2e tests failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Branch:* ${{ github.repository }}:${{ github.head_ref }}"
-190
View File
@@ -1,190 +0,0 @@
name: Test E2E Desktop Client
on:
pull_request:
types: [labeled, opened, synchronize, reopened]
paths:
- 'ui/desktop/**'
- 'e2e-tests/*'
- 'e2e-tests/api-client/**'
- 'e2e-tests/helpers/**'
- 'e2e-tests/desktop/**'
- '.github/workflows/test-e2e-desktop.yaml'
workflow_dispatch:
inputs:
boundary-enterprise-branch:
description: 'Branch of boundary-enterprise to test against (default to main)'
required: true
default: 'main'
type: string
boundary-ui-enterprise-branch:
description: 'Branch of boundary-ui-enterprise to test against (default to main)'
required: true
default: 'main'
type: string
jobs:
test:
name: test
# don't need to run e2e tests on ce -> ent merges since tests were already
# run in the ce branch, and there are currently no code diffferences between
# the two repos
if: github.actor != 'hc-github-team-secure-boundary'
runs-on: ${{ fromJSON(vars.RUNNER_UBUNTU_22) }}
env:
ENOS_VAR_boundary_edition: "ent"
ENOS_VAR_e2e_debug_no_run: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 #v4.2.0
with:
run_install: false
- name: Set up Node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 20
cache: "pnpm"
cache-dependency-path: |
pnpm-lock.yaml
ui/desktop/electron-app/pnpm-lock.yaml
- name: Set up Terraform
uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85
- name: Set up Enos
uses: hashicorp/action-setup-enos@17b90fcf9591275b468a94aefb9dc6a93017de8a
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Vault CLI
run: |
wget https://releases.hashicorp.com/vault/1.17.6/vault_1.17.6_linux_amd64.zip -O /tmp/vault.zip
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
unzip -o -q /tmp/vault.zip -d /usr/local/bin
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
unzip -o -q /tmp/vault.zip -d "$HOME/bin"
fi
# TODO: We should consider modifying to also build CE binary. For now
# building ENT binary using most recent UI commit is sufficient.
- name: Checkout boundary-enterprise
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: hashicorp/boundary-enterprise
ref: ${{ github.event.inputs.boundary-enterprise-branch || 'main' }}
path: support/src/boundary-enterprise
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
- name: Checkout boundary-ui-enterprise
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: hashicorp/boundary-ui-enterprise
ref: ${{ github.event.inputs.boundary-ui-enterprise-branch || 'main' }}
path: support/src/boundary-ui-enterprise
token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
- name: Use boundary-ui-enterprise commit SHA for building binary
run: |
cd support/src/boundary-ui-enterprise
git rev-parse HEAD > ../boundary-enterprise/internal/ui/VERSION_ent
- name: GH fix for localhost resolution
run: |
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
cat /etc/hosts && echo "-----------"
sudo sed -i 's/::1 *localhost ip6-localhost ip6-loopback/::1 ip6 -localhost ip6-loopback/g' /etc/hosts
cat /etc/hosts
ssh -V
fi
- name: Set git config
run: |
git config --global url."https://oauth2:${{ secrets.ELEVATED_GITHUB_TOKEN }}@github.com".insteadOf "https://github.com"
- name: Determine Go version
id: get-go-version
run: |
echo "Building with Go $(cat ./support/src/boundary-enterprise/.go-version)"
echo "go-version=$(cat ./support/src/boundary-enterprise/.go-version)" >> "$GITHUB_OUTPUT"
- name: Set up Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: ${{ steps.get-go-version.outputs.go-version }}
cache: false
- name: Set up test infra
id: infra
run: |
ssh-keygen -N '' -t ed25519 -f ~/.ssh/github_enos
mkdir -p ./enos/terraform-plugin-cache
export ENOS_VAR_ui_build_override="ent" && \
export ENOS_VAR_boundary_edition="ent" && \
export ENOS_VAR_enos_user=$GITHUB_ACTOR && \
export ENOS_VAR_aws_ssh_private_key_path=~/.ssh/github_enos && \
export ENOS_VAR_boundary_license="${{ secrets.BOUNDARY_ENT_LICENSE }}" && \
enos scenario launch --timeout 60m0s --chdir ./support/src/boundary-enterprise/enos e2e_ui_docker_ent builder:local
- name: Move boundary binary
run: |
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
mv ./support/src/boundary-enterprise/bin/boundary /usr/local/bin/
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
mv ./support/src/boundary-enterprise/bin/boundary $HOME/bin
fi
boundary version
- name: Install dependencies
run: |
pnpm install --frozen-lockfile
cd e2e-tests
if [[ "${{ github.repository }}" == "hashicorp/boundary-ui" ]]; then
pnpm playwright install --with-deps
elif [[ "${{ github.repository }}" == "hashicorp/boundary-ui-enterprise" ]]; then
# playwright dependencies can't be installed on a self-hosted runner
# due to permission issues
pnpm playwright install
sudo apt update -qq
sudo apt install xvfb -y
fi
# Runner is headless so we need to enable a display to run the client
- name: Run Tests
run: |
source <(bash ./support/src/boundary-enterprise/enos/scripts/test_e2e_env.sh)
cd e2e-tests
export DISPLAY=:99
Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
pnpm run desktop
- name: Upload Playwright report
if: ${{ failure() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: playwright-report
path: e2e-tests/playwright-report
- name: Clean up test infra
if: ${{ always() }}
continue-on-error: true
run: |
export ENOS_VAR_enos_user=$GITHUB_ACTOR && \
export ENOS_VAR_aws_ssh_private_key_path=~/.ssh/github_enos && \
enos scenario destroy --timeout 60m0s --chdir ./support/src/boundary-enterprise/enos e2e_ui_docker_ent builder:local
- name: Send Slack message on failure
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
if: ${{ failure() && github.repository == 'hashicorp/boundary-ui' }}
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_CHANNEL_ID }}
text: ":x: desktop client e2e tests failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Branch:* ${{ github.repository }}:${{ github.head_ref }}"
-22
View File
@@ -1,22 +0,0 @@
name: "Trigger Boundary Desktop release"
on:
push:
tags:
- '*'
jobs:
trigger-gha-release:
runs-on: ${{ fromJSON(vars.RUNNER) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Release description
run: |
echo "Triggering release with a TAG ${GITHUB_REF#refs/tags/}"
- name: Trigger Release workflow in boundary-desktop-releases
run: |
export GITHUB_TOKEN="${{ secrets.ELEVATED_GITHUB_TOKEN }}"
gh workflow run release.yml \
--repo hashicorp/boundary-desktop-releases \
--ref main \
-f TAG="${GITHUB_REF#refs/tags/}"
-6
View File
@@ -1,6 +0,0 @@
# Copyright IBM Corp. 2021, 2026
# SPDX-License-Identifier: BUSL-1.1
url_license = "https://github.com/hashicorp/boundary-ui/blob/main/LICENSE"
url_project_website = "https://learn.hashicorp.com/tutorials/boundary/getting-started-desktop-app"
url_source_repository = "https://github.com/hashicorp/boundary-ui"
+28
View File
@@ -0,0 +1,28 @@
Threshold UI
Copyright (c) 2026 CyberCove LLC
This repository is a community fork of "Boundary UI"
(https://github.com/hashicorp/boundary-ui), the admin web UI and desktop
client for HashiCorp Boundary.
Upstream work: Copyright (c) HashiCorp, Inc. 2020-2023;
Copyright (c) IBM Corp. 2024-2026
Upstream license: Business Source License 1.1 (see LICENSE)
The upstream LICENSE and per-file copyright / SPDX headers are retained
unchanged. Modifications made in this fork are provided under the same
license terms (BUSL-1.1), including its Additional Use Grant and Change
Date as stated in LICENSE.
This project is an independent community effort. It is NOT affiliated with,
endorsed by, or sponsored by HashiCorp, Inc. or IBM Corp. "HashiCorp",
"Boundary", and related marks and logos are trademarks of their respective
owners; they are used here only to describe the upstream origin of this work
and to name the upstream CLI/API this software interoperates with. The
upstream logos and brand art have been removed and replaced with plain
placeholder marks.
Third-party components keep their own licenses (see pnpm-lock.yaml and the
`pnpm compliance:licenses` script). The HashiCorp Design System packages
(@hashicorp/design-system-components, -tokens, @hashicorp/flight-icons) are
consumed unmodified from the public npm registry under their own licenses.
+36 -5
View File
@@ -1,10 +1,41 @@
# Boundary UI Monorepo
![](boundary.png)
![Validate Monorepo](https://github.com/hashicorp/boundary-ui/workflows/Validate%20Monorepo/badge.svg)
# Threshold UI Monorepo
This monorepo contains multiple projects, including UIs and addons, used by
Boundary.
Threshold (the admin web UI and the desktop client).
## About this fork
This repository is a community fork of
[hashicorp/boundary-ui](https://github.com/hashicorp/boundary-ui), rebranded
for the Threshold project (a community fork of Boundary CE). It is licensed
under the upstream Business Source License 1.1 (see [LICENSE](LICENSE) and
[NOTICE](NOTICE)) and is **not affiliated with, endorsed by, or sponsored by
HashiCorp or IBM**.
What changed relative to upstream:
- User-visible product name is Threshold (window titles, menus, about dialog,
installer/package names, app id `net.anhonesthost.cybercove.threshold`).
API paths, field names, protocol strings and environment variable names are
unchanged for compatibility with the server. The desktop client's persisted
settings are copied from the former "Boundary" user-data directory on first
launch.
- The desktop client looks for a `threshold` CLI first, then `boundary`.
- HashiCorp/IBM logos and brand art were removed and replaced with plain
placeholder marks.
- Nothing contacts HashiCorp services by default. The in-app updater, the
"unsupported controller" release-metadata lookup, and the CLI bundling
download are disabled until you configure your own hosts:
`THRESHOLD_UPDATE_FEED_URL` (desktop build, updater feed),
`THRESHOLD_RELEASES_METADATA_URL` / `THRESHOLD_RELEASES_PAGE_URL`
(desktop build, release links) and `THRESHOLD_CLI_RELEASES_URL` /
`THRESHOLD_CLI_ARCHIVE_PREFIX` (used with `SETUP_CLI=true` to bundle a CLI).
- Documentation links still point at the upstream Boundary documentation,
which is user-initiated only.
- Upstream HashiCorp-internal GitHub workflows and release metadata were
removed; CI lives in `.gitea/workflows/ci.yml`.
Upstream history, CHANGELOGs and copyright headers are preserved.
This top-level repository provides limited common tasks, such as installation
and commit assistance. However, most tasks must be executed from within a
+36 -4
View File
@@ -131,11 +131,34 @@ export default class ApplicationAdapter extends RESTAdapter.extend(
// which indicates that there are more items in the list
// or the batch size has not been reached
do {
result = await super.query(store, schema, queryObject);
//add the result items to a data array
try {
result = await super.query(store, schema, queryObject);
} catch (e) {
// If we hit a rate limit mid-pagination, return whatever
// data has been accumulated so far rather than throwing and leaving
// the user stuck with nothing to show.
// Without a list_token this is the very first request with nothing to
// show, so rethrow and let normal error handling apply.
if (e?.errors?.[0]?.isRateLimited && queryObject.list_token) {
result ??= {};
result.items = data;
result.response_type = 'complete';
result.meta = {
...result.meta,
rateLimitWarning: { retryAfter: e.errors[0].retryAfter },
};
return prenormalizeArrayResponse(result);
}
// For all other errors (or a 429 on the very first request with
// no data yet), rethrow so normal error handling applies.
throw e;
}
// Add the result items to a data array
if (result && result.items) {
data.push(...result.items);
//pass in the list token for subsequent calls to fetch the remaining list items
// Pass in the list token for subsequent calls to fetch the remaining list items
queryObject.list_token = result.list_token;
}
} while (
@@ -237,6 +260,7 @@ export default class ApplicationAdapter extends RESTAdapter.extend(
* - `403 isForbidden`: the session is authenticated but does not have
* permission to perform the requested action
* - `404 isNotFound`: the requested resource could not be found
* - `429 isRateLimited`: the client has exceeded the rate limit
* - `500 isServer`: an internal server error occurred
* - `isUnknown`: an error occurred, but we don't know which or
* we don't distinguish it yet
@@ -248,7 +272,7 @@ export default class ApplicationAdapter extends RESTAdapter.extend(
* @param {Object} payload
* @return {Array} errors payload
*/
normalizeErrorResponse(/*status, headers, payload*/) {
normalizeErrorResponse(status, headers /*, payload*/) {
const errors = super.normalizeErrorResponse(...arguments);
if (isArray(errors)) {
errors.forEach((error) => {
@@ -262,6 +286,14 @@ export default class ApplicationAdapter extends RESTAdapter.extend(
case 404:
error.isNotFound = true;
break;
case 429:
error.isRateLimited = true;
// Attach rate-limit metadata from response headers so downstream
// error handlers can inform the user when they may retry.
error.retryAfter = Number(
headers['Retry-After'] ?? headers['retry-after'],
);
break;
case 500:
error.isServer = true;
break;
@@ -96,19 +96,19 @@ export default class GeneratedSessionRecordingModel extends BaseModel {
})
duration;
@attr('string', {
@attr({
description:
'The current state of the session recording. One of "started", "available" and "unknown".',
'The detailed state of the session recording. This contains storage_state, syncing_error_details, and verification_error_details.',
readOnly: true,
})
state;
recording_state;
@attr('string', {
description:
'Any error seen during the closing of the session recording. Currently only set if state is "unknown".',
'The availability of the session recording. One of "available" or "not available".',
readOnly: true,
})
error_details;
availability;
@attr('date', {
description: 'The time until a session recording is required to be stored.',
@@ -122,4 +122,19 @@ export default class GeneratedSessionRecordingModel extends BaseModel {
readOnly: true,
})
delete_after;
// Deprecated: These two fields should be removed once support for RDP is added.
@attr('string', {
description:
'The current state of the session recording. One of "started", "available" and "unknown".',
readOnly: true,
})
state;
@attr('string', {
description:
'Any error seen during the closing of the session recording. Currently only set if state is "unknown".',
readOnly: true,
})
error_details;
}
+43 -9
View File
@@ -59,17 +59,39 @@ export default class SqliteHandler {
let payload,
listToken,
writeToDbPromise,
totalInsert = 0;
totalInsert = 0,
rateLimitWarning;
if (!peekDb) {
const tokenKey = `${type}-${hashCode(remainingQuery)}`;
let tokenKey = `${type}-${hashCode(remainingQuery)}`;
// The global token key represents a recursive query from global scope,
// which pulls back every resource of this type.
const globalTokenKey = `${type}-${hashCode({ scope_id: 'global', recursive: true })}`;
if (storeToken) {
const [tokenObj] = await this.sqlite.fetchResource({
...generateSQLExpressions('token', {
filters: { id: [{ equals: tokenKey }] },
}),
});
listToken = tokenObj?.token;
// Always prefer the global token when it exists for
// any more fine-grained query. The global query already fetched every
// item for this resource type, so we should just re-use it.
if (tokenKey !== globalTokenKey) {
const [globalTokenObj] = await this.sqlite.fetchResource({
...generateSQLExpressions('token', {
filters: { id: [{ equals: globalTokenKey }] },
}),
});
if (globalTokenObj?.token) {
tokenKey = globalTokenKey;
listToken = globalTokenObj.token;
}
}
// Only look up the specific token if no global token was found.
if (!listToken) {
const [tokenObj] = await this.sqlite.fetchResource({
...generateSQLExpressions('token', {
filters: { id: [{ equals: tokenKey }] },
}),
});
listToken = tokenObj?.token;
}
} else {
// This is a temporary fix of clearing the DB (specifically for auth-methods)
// since we are not storing the token we do not get back a list of removed_ids
@@ -86,6 +108,11 @@ export default class SqliteHandler {
});
totalInsert += payload.items?.length ?? 0;
// Propagate any rate-limit warning the adapter attached
if (payload.meta?.rateLimitWarning) {
rateLimitWarning = payload.meta.rateLimitWarning;
}
// await the previous writeToDbPromise before writing to db again
if (writeToDbPromise) {
await writeToDbPromise;
@@ -163,7 +190,14 @@ export default class SqliteHandler {
// This isn't conventional but is better than returning an ArrayProxy
// or EmberArray since the ember store query method asserts it has to be an array
// so we can't just return an object.
records.meta = { totalItems: count[0].total };
records.meta = {
totalItems: count[0].total,
};
if (rateLimitWarning) {
records.meta.rateLimitWarning = rateLimitWarning;
}
return records;
}
default:
@@ -48,6 +48,18 @@ export default class AuthMethodSerializer extends ApplicationSerializer {
} else {
delete serialized.attributes.state;
}
// Don't send account_claim_maps on update if there's a saved 'sub' mapping
// The API doesn't allow updating when 'sub' claim mappings exist
if (!snapshot.record.isNew && snapshot.record.account_claim_maps) {
const hasSubClaimMap = snapshot.record.account_claim_maps.some(
(item) => item?.value === 'sub',
);
if (hasSubClaimMap) {
delete serialized.attributes.account_claim_maps;
}
}
return serialized;
}
@@ -68,21 +68,25 @@ export default class CredentialLibrarySerializer extends ApplicationSerializer {
*/
handleCredentialMappingOverrides(serialized, isNew) {
const { credential_type, credential_mapping_overrides } = serialized;
if (Object.keys(credential_mapping_overrides).length === 0) {
serialized.credential_mapping_overrides = null;
}
// API expects to send null to fields if it is undefined or deleted
if (credential_mapping_overrides && !isNew) {
serialized.credential_mapping_overrides = options.mapping_overrides[
credential_type
].reduce((obj, key) => {
if (credential_mapping_overrides[key]) {
obj[key] = credential_mapping_overrides[key];
} else {
obj[key] = null;
}
return obj;
}, {});
if (!credential_mapping_overrides || isNew || !credential_type) {
return;
}
// API expects to send null to fields if it is undefined or deleted
serialized.credential_mapping_overrides = options.mapping_overrides[
credential_type
].reduce((obj, key) => {
if (credential_mapping_overrides[key]) {
obj[key] = credential_mapping_overrides[key];
} else {
obj[key] = null;
}
return obj;
}, {});
}
}
+58 -16
View File
@@ -46,6 +46,15 @@ import {
// See https://sqlite.org/rescode.html#corrupt
const SQLITE_CORRUPT = 11;
/**
* Throws if the database has not been initialized or has been closed.
*/
const requireDb = () => {
if (!db) {
throw new Error('Database is not available');
}
};
const methods = {
initializeSQLite: async () => {
sqlite3 = await sqlite3InitModule({
@@ -57,6 +66,9 @@ import {
});
function setupDb() {
// Allow SQLite to wait up to 5 s when it encounters a busy lock
db.exec('PRAGMA busy_timeout = 5000;');
const [row] = db.exec('PRAGMA user_version;', { rowMode: 'object' });
// Check if we're on a newer schema version and clear the database if so
if (SCHEMA_VERSION > row.user_version) {
@@ -97,30 +109,40 @@ import {
if (poolUtil.getCapacity() > 20) {
await poolUtil.removeVfs();
}
} else {
} else if (
err?.resultCode === SQLITE_CORRUPT ||
err instanceof WebAssembly.RuntimeError
) {
// We might have gotten ourselves into a bad state which corrupted the DB so clear it out and retry.
console.error('SQLite initialization error:', err);
// We might have gotten ourselves into a bad state which corrupted the DB so clear it out and retry
if (err?.resultCode === SQLITE_CORRUPT) {
try {
if (db) {
db.close();
db = null;
}
poolUtil.unlink(`/${dbName}`);
} else {
throw err;
} catch (cleanupErr) {
// If cleanup fails, wipe the entire VFS and reinstall to start fresh
console.error('SQLite cleanup error:', cleanupErr);
await poolUtil.removeVfs();
}
} else {
throw err;
}
}
}
},
analyzeDatabase: () => {
requireDb();
db.exec('PRAGMA optimize');
},
clearDatabase: () => {
requireDb();
db.exec(CLEAR_DB);
db.exec(CREATE_TABLES(SCHEMA_VERSION));
},
deleteDatabase: () => {
if (!db) {
throw new Error('No database was initialized');
}
requireDb();
const name = db.dbFilename();
db.close();
@@ -128,9 +150,11 @@ import {
poolUtil.unlink(`/${name}`);
},
downloadDatabase: () => {
requireDb();
return sqlite3.capi.sqlite3_js_db_export(db);
},
fetchResource: ({ sql, parameters = [] }) => {
requireDb();
try {
return db.exec({
sql,
@@ -147,6 +171,7 @@ import {
if (items.length === 0) {
return [];
}
requireDb();
const numberOfParameters = items.reduce((acc, item) => {
return acc + item.length;
@@ -155,11 +180,15 @@ import {
// We want to use as many VALUES as part of the insert statement as it's quicker
// than doing multiple inserts. If we have too many rows to insert, we'll manually
// chunk them up to avoid hitting the maximum number of host parameters
// and execute these separate insert statements in a transaction
// and execute these separate insert statements in a transaction.
if (numberOfParameters > MAX_HOST_PARAMETERS) {
const chunkSize = Math.floor(MAX_HOST_PARAMETERS / items[0].length);
return db.transaction(() => {
// Note: We use explicit BEGIN/COMMIT instead of db.transaction() because
// db.transaction() uses SAVEPOINT internally, which fails with SQLITE_BUSY
// when any statement is active on the connection.
db.exec('BEGIN');
try {
const results = [];
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
@@ -170,20 +199,29 @@ import {
});
results.push(result);
}
db.exec('COMMIT');
return results;
});
} catch (err) {
db.exec('ROLLBACK');
throw err;
}
}
db.exec({
return db.exec({
sql: INSERT_STATEMENTS(resource, items, modelMapping),
bind: items.flat(),
rowMode: 'object',
});
},
deleteResource: ({ resource, ids }) => {
// Check if we have too many parameters for the deletion and chunk if so
requireDb();
// Check if we have too many parameters for the deletion and chunk if so.
if (ids?.length > MAX_HOST_PARAMETERS) {
return db.transaction(() => {
// Note: We use explicit BEGIN/COMMIT instead of db.transaction() because
// db.transaction() uses SAVEPOINT internally, which fails with SQLITE_BUSY
// when any statement is active on the connection.
db.exec('BEGIN');
try {
const results = [];
for (let i = 0; i < ids.length; i += MAX_HOST_PARAMETERS) {
const chunk = ids.slice(i, i + MAX_HOST_PARAMETERS);
@@ -194,8 +232,12 @@ import {
});
results.push(result);
}
db.exec('COMMIT');
return results;
});
} catch (err) {
db.exec('ROLLBACK');
throw err;
}
}
return db.exec({
@@ -169,6 +169,12 @@ const onBecomeProvider = async ({
target,
status,
}) => {
// Create a gate that prevents request handling until the provider is fully initialized.
// This prevents a race condition where a client registers and sends requests before
// the provider has finished initialization.
const { promise: providerReady, resolve: providerReadyResolve } =
Promise.withResolvers();
// Set an event listener for other tabs to register with the provider tab
commonChannel.addEventListener('message', async (event) => {
const { clientId, type } = event.data;
@@ -194,6 +200,9 @@ const onBecomeProvider = async ({
}
const { method, args, id } = data;
// Wait for the provider to finish initialization before handling requests
await providerReady;
let result, error;
try {
result = await target[method](...args);
@@ -221,6 +230,7 @@ const onBecomeProvider = async ({
});
await onProviderChange?.(status.isServiceProvider);
providerReadyResolve();
commonChannel.postMessage({ type: 'providerChange', serviceName });
// If we became the new provider and there were requests in flight, request it directly again.
+5
View File
@@ -11,6 +11,7 @@ import initializeMockDesktopContextBridgeAPI from './scenarios/ipc/desktop-conte
import makeBooleanFilter from './helpers/bexpr-filter';
import { faker } from '@faker-js/faker';
import { asciicasts } from './data/asciicasts';
import { grantsSchemaData } from './data/grants-schema';
import { TYPE_WORKER_PKI } from 'api/models/worker';
import environmentConfig from 'ember-get-config';
@@ -210,6 +211,10 @@ function routes() {
return metadata;
});
this.get('/internal/grants-schema.json', () => {
return grantsSchemaData;
});
// make this `/api`, for example, if your API is namespaced
this.namespace = environmentConfig.api?.namespace;
// delay for each request, automatically set to 0 during testing
+648
View File
@@ -0,0 +1,648 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
export const grantsSchemaData = {
resource_types: [
{
type: 'account',
collection_actions: ['create', 'list'],
id_actions: [
'read',
'update',
'delete',
'set-password',
'change-password',
],
scopes: ['global', 'org'],
id_prefixes: ['apw', 'acctldap', 'acctpw', 'acctoidc'],
parent_type: 'auth-method',
output_fields: [
'id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'auth_method_id',
'attributes',
'password_account_attributes',
'oidc_account_attributes',
'ldap_account_attributes',
'managed_group_ids',
'authorized_actions',
],
},
{
type: 'alias',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['global'],
id_prefixes: ['alt'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'value',
'destination_id',
'type',
'attributes',
'target_alias_attributes',
'authorized_actions',
],
},
{
type: 'auth-method',
collection_actions: ['create', 'list'],
id_actions: ['authenticate', 'read', 'update', 'delete', 'change-state'],
scopes: ['global', 'org'],
id_prefixes: ['amoidc', 'amldap', 'ampw'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'password_auth_method_attributes',
'oidc_auth_methods_attributes',
'ldap_auth_methods_attributes',
'is_primary',
'authorized_actions',
'authorized_collection_actions',
],
},
{
type: 'auth-token',
collection_actions: ['list'],
id_actions: ['read', 'read:self', 'delete', 'delete:self'],
scopes: ['global', 'org'],
id_prefixes: ['at'],
output_fields: [
'id',
'scope_id',
'scope',
'token',
'user_id',
'auth_method_id',
'account_id',
'created_time',
'updated_time',
'approximate_last_used_time',
'expiration_time',
'authorized_actions',
],
},
{
type: 'billing',
collection_actions: ['monthly-active-users'],
scopes: ['global'],
},
{
type: 'credential',
collection_actions: ['create', 'list'],
id_actions: ['delete', 'read', 'update'],
scopes: ['project'],
id_prefixes: [
'credp',
'cdvlt',
'credup',
'cred',
'credjson',
'credupd',
'credspk',
],
parent_type: 'credential-store',
output_fields: [
'id',
'credential_store_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'username_password_attributes',
'ssh_private_key_attributes',
'json_attributes',
'username_password_domain_attributes',
'password_attributes',
'authorized_actions',
],
},
{
type: 'credential-library',
collection_actions: ['create', 'list'],
id_actions: ['delete', 'read', 'update'],
scopes: ['project'],
id_prefixes: ['clvlt', 'clvllt', 'clvsclt'],
parent_type: 'credential-store',
output_fields: [
'id',
'credential_store_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'vault_credential_library_attributes',
'vault_ssh_certificate_credential_library_attributes',
'vault_generic_credential_library_attributes',
'vault_ldap_credential_library_attributes',
'authorized_actions',
'credential_type',
'credential_mapping_overrides',
],
},
{
type: 'credential-store',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['project'],
id_prefixes: ['csvlt', 'csst', 'cs'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'vault_credential_store_attributes',
'authorized_actions',
'authorized_collection_actions',
],
},
{
type: 'group',
collection_actions: ['create', 'list'],
id_actions: [
'update',
'delete',
'add-members',
'set-members',
'remove-members',
'read',
],
scopes: ['global', 'org', 'project'],
id_prefixes: ['g'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'member_ids',
'members',
'authorized_actions',
],
},
{
type: 'host',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['project'],
id_prefixes: ['hst', 'hplg', 'h'],
parent_type: 'host-catalog',
output_fields: [
'id',
'host_catalog_id',
'scope',
'plugin',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'host_set_ids',
'attributes',
'static_host_attributes',
'ip_addresses',
'dns_names',
'external_id',
'external_name',
'authorized_actions',
],
},
{
type: 'host-catalog',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['project'],
id_prefixes: ['hc', 'hcplg', 'hcst'],
output_fields: [
'id',
'scope_id',
'scope',
'plugin_id',
'plugin',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'secrets',
'secrets_hmac',
'worker_filter',
'authorized_actions',
'authorized_collection_actions',
],
},
{
type: 'host-set',
collection_actions: ['create', 'list'],
id_actions: [
'read',
'update',
'delete',
'add-hosts',
'set-hosts',
'remove-hosts',
],
scopes: ['project'],
id_prefixes: ['hsst', 'hsplg', 'hs'],
parent_type: 'host-catalog',
output_fields: [
'id',
'host_catalog_id',
'scope',
'plugin',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'host_ids',
'preferred_endpoints',
'sync_interval_seconds',
'attributes',
'authorized_actions',
],
},
{
type: 'managed-group',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['global', 'org'],
id_prefixes: ['mgoidc', 'mgldap'],
parent_type: 'auth-method',
output_fields: [
'id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'auth_method_id',
'attributes',
'oidc_managed_group_attributes',
'ldap_managed_group_attributes',
'member_ids',
'authorized_actions',
],
},
{
type: 'policy',
collection_actions: ['create', 'list'],
id_actions: ['read', 'update', 'delete'],
scopes: ['global', 'org'],
id_prefixes: ['pst'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'type',
'version',
'attributes',
'storage_policy_attributes',
'authorized_actions',
],
},
{
type: 'role',
collection_actions: ['create', 'list'],
id_actions: [
'remove-principals',
'add-grants',
'set-grants',
'remove-grants',
'delete',
'add-grant-scopes',
'set-grant-scopes',
'remove-grant-scopes',
'read',
'update',
'add-principals',
'set-principals',
],
scopes: ['global', 'org', 'project'],
id_prefixes: ['r'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'grant_scope_ids',
'principal_ids',
'principals',
'grant_strings',
'grants',
'authorized_actions',
],
},
{
type: 'scope',
collection_actions: [
'create',
'list',
'list-keys',
'rotate-keys',
'list-key-version-destruction-jobs',
'destroy-key-version',
],
id_actions: [
'read',
'update',
'delete',
'attach-storage-policy',
'detach-storage-policy',
'set-alias-suffix',
'remove-alias-suffix',
],
scopes: ['global', 'org', 'project'],
id_prefixes: ['global', 'p', 'o'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'primary_auth_method_id',
'authorized_actions',
'authorized_collection_actions',
'storage_policy_id',
],
},
{
type: 'session',
collection_actions: ['list'],
id_actions: ['cancel:self', 'read', 'read:self', 'cancel'],
scopes: ['project'],
id_prefixes: ['s'],
output_fields: [
'id',
'target_id',
'scope',
'created_time',
'updated_time',
'version',
'type',
'expiration_time',
'auth_token_id',
'user_id',
'host_set_id',
'host_id',
'scope_id',
'endpoint',
'states',
'status',
'certificate',
'termination_reason',
'authorized_actions',
'connections',
],
},
{
type: 'session-recording',
collection_actions: ['list', 'list-exports'],
id_actions: [
'reapply-storage-policy',
'export',
'export:cancel',
'read',
'download',
'delete',
],
scopes: ['global', 'org'],
id_prefixes: ['sr'],
output_fields: [
'id',
'scope',
'session_id',
'storage_bucket_id',
'bytes_up',
'bytes_down',
'created_time',
'updated_time',
'start_time',
'end_time',
'duration',
'type',
'state',
'error_details',
'mime_types',
'endpoint',
'connection_recordings',
'create_time_values',
'authorized_actions',
'retain_until',
'delete_after',
'availability',
'recording_state',
],
},
{
type: 'storage-bucket',
collection_actions: ['list', 'create'],
id_actions: ['delete', 'read', 'update'],
scopes: ['global', 'org'],
id_prefixes: ['sb'],
output_fields: [
'id',
'scope_id',
'scope',
'plugin_id',
'plugin',
'name',
'description',
'bucket_name',
'bucket_prefix',
'created_time',
'updated_time',
'version',
'type',
'attributes',
'secrets',
'secrets_hmac',
'worker_filter',
'storage_bucket_credential_id',
'authorized_actions',
],
},
{
type: 'target',
collection_actions: ['create', 'list'],
id_actions: [
'delete',
'add-host-sources',
'set-host-sources',
'remove-host-sources',
'add-credential-sources',
'read',
'update',
'set-credential-sources',
'remove-credential-sources',
'authorize-session',
],
scopes: ['project'],
id_prefixes: ['trdp', 'tssh', 'ttcp'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'type',
'host_source_ids',
'host_sources',
'session_max_seconds',
'session_connection_limit',
'worker_filter',
'egress_worker_filter',
'ingress_worker_filter',
'brokered_credential_source_ids',
'brokered_credential_sources',
'injected_application_credential_source_ids',
'injected_application_credential_sources',
'attributes',
'tcp_target_attributes',
'ssh_target_attributes',
'rdp_target_attributes',
'authorized_actions',
'address',
'aliases',
'with_aliases',
],
},
{
type: 'user',
collection_actions: ['create', 'list'],
id_actions: [
'delete',
'add-accounts',
'set-accounts',
'remove-accounts',
'list-resolvable-aliases',
'read',
'update',
],
scopes: ['global', 'org'],
id_prefixes: ['u'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'account_ids',
'accounts',
'authorized_actions',
'login_name',
'full_name',
'email',
'primary_account_id',
],
},
{
type: 'worker',
collection_actions: [
'create:worker-led',
'list',
'read-certificate-authority',
'reinitialize-certificate-authority',
'create:controller-led',
],
id_actions: [
'remove-worker-tags',
'read',
'update',
'delete',
'add-worker-tags',
'set-worker-tags',
],
scopes: ['global'],
id_prefixes: ['w'],
output_fields: [
'id',
'scope_id',
'scope',
'name',
'description',
'created_time',
'updated_time',
'version',
'address',
'canonical_tags',
'config_tags',
'last_status_time',
'worker_generated_auth_token',
'controller_generated_activation_token',
'active_connection_count',
'type',
'api_tags',
'release_version',
'directly_connected_downstream_workers',
'authorized_actions',
'local_storage_state',
'remote_storage_state',
],
},
],
};
@@ -45,6 +45,12 @@ export default factory.extend({
username: faker.internet.userName(),
key_bits: faker.number.int(999),
path: faker.system.directoryPath(),
critical_options: {
[faker.word.noun()]: faker.word.words(2),
},
extensions: {
[faker.word.noun()]: faker.word.words(2),
},
};
case TYPE_CREDENTIAL_LIBRARY_VAULT_LDAP:
return {
+1 -1
View File
@@ -26,7 +26,7 @@ export default factory.extend({
'set-grant-scopes',
],
grant_strings: () => [
'ids=*;action=*',
'ids=*;actions=*',
'ids=*;type=host-catalog;actions=create,read',
],
@@ -10,8 +10,8 @@ import { faker } from '@faker-js/faker';
import { trait } from 'miragejs';
import {
TYPES_SESSION_RECORDING as types,
STATE_SESSION_RECORDING_STARTED,
STATE_SESSION_RECORDING_AVAILABLE,
STATE_SESSION_RECORDING_STARTED,
STATE_SESSION_RECORDING_UNKNOWN,
} from 'api/models/session-recording';
@@ -27,6 +27,29 @@ export default factory.extend({
// Cycle through available types
type: (i) => types[i % types.length],
state: (i) => states[i % states.length],
recording_state(i) {
// Only return error details if state is 'unknown' (failed)
if (this.state !== STATE_SESSION_RECORDING_UNKNOWN) {
return {};
}
const errorScenarios = [
{
syncing_error_details:
'sync errors in container "sr_jovU2XLzBP.bsr": file "sr_jovU2XLzBP.bsr/session-recording.meta": storage.(syncingFile).sync: storage.parseStoragePluginError: plugin error, external system issue: error #3001: rpc error: \ncode = Internal desc = failed to put object into minio: Get "http://minio:9000/my-special-storage-bucket/?location=": dial tcp: lookup minio on 10.89.0.1:53: no such host',
verification_error_details:
'integrity check failed for session recording file "sr_jovU2XLzBP.bsr/channels/connection-1/channel-recording-data.bin": expected SHA-256 checksum "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6" but computed \nchecksum was "z6y5x4w3v2u1t0s9r8q7p6o5n4m3l2k1j0i9h8g7f6e5d4c3b2a1" indicating potential data corruption during transfer or storage',
},
{
syncing_error_details:
'sync errors in container "sr_jovU2XLzBP.bsr": file "sr_jovU2XLzBP.bsr/session-recording.meta": storage.(syncingFile).sync: storage.parseStoragePluginError: plugin error, external system issue: error #3001: rpc error: code = Internal desc = failed to put object into minio: Get "http://minio:9000/my-special-storage-bucket/?location=": dial tcp: lookup minio on 10.89.0.1:53: no such host',
verification_error_details: null,
},
];
return errorScenarios[i % 2];
},
start_time() {
return this.state === STATE_SESSION_RECORDING_AVAILABLE
? faker.date.recent({ days: 3, refDate: this.end_time })
+5 -1
View File
@@ -9,7 +9,11 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
module.exports = function (defaults) {
const app = new EmberAddon(defaults, {
// Add options here
babel: {
plugins: [
require.resolve('ember-concurrency/async-arrow-task-transform'),
],
},
});
/*
+5
View File
@@ -18,6 +18,11 @@ module.exports = {
name: require('./package').name,
options: {
babel: {
plugins: [
require.resolve('ember-concurrency/async-arrow-task-transform'),
],
},
// Don't automatically import sqlite, we need to manually control how it gets used
autoImport: {
exclude: ['@sqlite.org/sqlite-wasm'],
+7 -6
View File
@@ -1,9 +1,9 @@
{
"name": "api",
"version": "0.0.0",
"description": "API data layer for Boundary.",
"description": "API data layer for Threshold.",
"license": "BUSL-1.1",
"author": "HashiCorp",
"author": "CyberCove LLC (fork of HashiCorp Boundary UI)",
"keywords": [
"ember-addon"
],
@@ -34,8 +34,9 @@
"broccoli-source": "^3.0.1",
"ember-auto-import": "^2.10.0",
"ember-can": "^8.0.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-babel": "^8.3.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-concurrency": "^4.0.3",
"ember-data": "~5.3.12",
"ember-get-config": "^2.1.1",
"ember-inflector": "^6.0.0",
@@ -43,7 +44,7 @@
"promise-worker-bi": "^5.0.1",
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"tracked-built-ins": "^4.0.0",
"uuid": "^11.0.3"
"uuid": "^14.0.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.25.1",
@@ -55,7 +56,7 @@
"@glimmer/component": "^2.0.0",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^9.1.0",
"concurrently": "^10.0.3",
"doctoc": "^2.2.0",
"ember-cli": "~5.12.0",
"ember-cli-clean-css": "^3.0.0",
@@ -92,7 +93,7 @@
"sinon": "^19.0.2"
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"ember": {
"edition": "octane"
+1 -1
View File
@@ -30,7 +30,7 @@ module.exports = function (environment) {
},
mirage: {
enabled: true
enabled: true,
},
};
@@ -175,11 +175,46 @@ module('Unit | Adapter | application', function (hooks) {
assert.ok(getResponse(401).errors[0].isUnauthenticated);
assert.ok(getResponse(403).errors[0].isForbidden);
assert.ok(getResponse(404).errors[0].isNotFound);
assert.ok(getResponse(429).errors[0].isRateLimited);
assert.ok(getResponse(500).errors[0].isServer);
assert.ok(getResponse(0).errors[0].isUnknown);
assert.ok(getResponse(false).errors[0].isUnknown);
});
test('it attaches retryAfter from Retry-After header on 429 responses', function (assert) {
const adapter = this.owner.lookup('adapter:application');
const headers = { 'Retry-After': '30' };
const response = adapter.handleResponse(429, headers, { status: 429 });
assert.ok(response.errors[0].isRateLimited);
assert.strictEqual(response.errors[0].retryAfter, 30);
});
test('it returns partial data when rate limited mid-pagination', async function (assert) {
const adapter = this.owner.lookup('adapter:application');
const store = this.owner.lookup('service:store');
let requestCount = 0;
this.server.get('/groups', () => {
requestCount++;
if (requestCount === 1) {
return {
items: [{ id: 'g_1', scope_id: 'p_1' }],
response_type: 'delta',
list_token: 'token123',
};
}
// Second request returns a 429
return new Response(429, { 'Retry-After': '10' }, { status: 429 });
});
const result = await adapter.query(
store,
{ modelName: 'group' },
{ scope_id: 'p_1' },
);
assert.strictEqual(result.items.length, 1);
assert.strictEqual(result.meta.rateLimitWarning.retryAfter, 10);
});
test('it returns field-level errors in InvalidError from handleResponse', function (assert) {
const adapter = this.owner.lookup('adapter:application');
const payload = {
@@ -470,6 +470,62 @@ module('Unit | Handler | sqlite-handler', function (hooks) {
assert.strictEqual(results[0].name, 'specific-target');
});
test('it reuses the global token for a more specific query when the global token exists', async function (assert) {
const targets = this.server.createList('target', 3, { scope });
let capturedListToken;
let responseToken = 'specific-list-token';
this.server.get('targets', (_schema, request) => {
capturedListToken = request.queryParams.list_token;
return {
items: targets.map((t) =>
this.server.serializerOrRegistry.serialize(t),
),
list_token: responseToken,
response_type: 'complete',
};
});
// Run the specific query first
await store.query('target', { scope_id: 'some-specific-scope' });
// Switch the response token and run the global query
responseToken = 'global-list-token';
await store.query('target', { scope_id: 'global', recursive: true });
capturedListToken = undefined;
// Re-run the specific query which has a cached token but should prefer the global token since it exists
await store.query('target', { scope_id: 'some-specific-scope' });
assert.strictEqual(capturedListToken, 'global-list-token');
});
test('it uses the specific token when no global token exists', async function (assert) {
const targets = this.server.createList('target', 3, { scope });
let capturedListToken;
this.server.get('targets', (_schema, request) => {
capturedListToken = request.queryParams.list_token;
return {
items: targets.map((t) =>
this.server.serializerOrRegistry.serialize(t),
),
list_token: 'specific-list-token',
response_type: 'complete',
};
});
await store.query('target', { scope_id: 'some-specific-scope' });
capturedListToken = undefined;
// Re-run the specific query which should just use the normal specific token since no global token exists
await store.query('target', { scope_id: 'some-specific-scope' });
assert.strictEqual(capturedListToken, 'specific-list-token');
});
test('it retries query when invalid list token error occurs', async function (assert) {
let targets = this.server.createList('target', 5, { scope });
@@ -506,4 +506,90 @@ module('Unit | Serializer | auth method', function (hooks) {
assert.strictEqual(record.client_certificate_key, '');
assert.strictEqual(record.bind_password, '');
});
test('it includes account_claim_maps when serializing new OIDC records', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('auth-method');
const record = store.createRecord('auth-method', {
type: TYPE_AUTH_METHOD_OIDC,
name: 'Test OIDC',
account_claim_maps: [
{ key: 'email', value: 'email' },
{ key: 'name', value: 'sub' },
],
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.ok(
serializedRecord.attributes.account_claim_maps,
'account_claim_maps is included for new records',
);
assert.deepEqual(serializedRecord.attributes.account_claim_maps, [
'email=email',
'name=sub',
]);
});
test('it excludes account_claim_maps when serializing existing OIDC records with "sub" mapping', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('auth-method');
const record = store.push({
data: {
id: 'oidc123',
type: 'auth-method',
attributes: {
type: TYPE_AUTH_METHOD_OIDC,
name: 'Test OIDC',
account_claim_maps: [
{ key: 'email', value: 'email' },
{ key: 'name', value: 'sub' },
],
},
},
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.notOk(
serializedRecord.attributes.account_claim_maps,
'account_claim_maps is excluded when updating with "sub" mapping',
);
});
test('it includes account_claim_maps when serializing existing OIDC records without "sub" mapping', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('auth-method');
const record = store.push({
data: {
id: 'oidc456',
type: 'auth-method',
attributes: {
type: TYPE_AUTH_METHOD_OIDC,
name: 'Test OIDC',
account_claim_maps: [
{ key: 'email', value: 'email' },
{ key: 'name', value: 'name' },
],
},
},
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.ok(
serializedRecord.attributes.account_claim_maps,
'account_claim_maps is included when updating without "sub" mapping',
);
assert.deepEqual(serializedRecord.attributes.account_claim_maps, [
'email=email',
'name=name',
]);
});
});
@@ -138,6 +138,45 @@ module('Unit | Serializer | credential library', function (hooks) {
});
});
test('it serializes credential library updates without credential_type', function (assert) {
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('credential-library');
store.push({
data: {
id: '1',
type: 'credential-library',
attributes: {
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
name: 'Name',
description: 'Description',
path: '/vault/path',
http_method: 'GET',
version: 1,
credential_type: null,
credential_mapping_overrides: {},
},
},
});
const record = store.peekRecord('credential-library', '1');
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
type: TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
credential_store_id: null,
name: 'Name',
description: 'Description',
credential_type: null,
credential_mapping_overrides: null,
attributes: {
path: '/vault/path',
http_method: 'GET',
http_request_body: null,
},
version: 1,
});
});
test('it does not serialize http_request_body unless http_method is set to POST', function (assert) {
const store = this.owner.lookup('service:store');
const record = store.createRecord('credential-library', {
+5 -5
View File
@@ -1,9 +1,9 @@
{
"name": "auth",
"version": "0.0.0",
"description": "Authentication layer for Boundary.",
"description": "Authentication layer for Threshold.",
"license": "BUSL-1.1",
"author": "HashiCorp",
"author": "CyberCove LLC (fork of HashiCorp Boundary UI)",
"keywords": [
"ember-addon"
],
@@ -28,7 +28,7 @@
"@babel/core": "^7.26.10",
"@ember/test-waiters": "^4.1.0",
"ember-auto-import": "^2.10.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-babel": "^8.3.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-simple-auth": "^6.1.0"
},
@@ -41,7 +41,7 @@
"@glimmer/component": "^2.0.0",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^9.1.0",
"concurrently": "^10.0.3",
"doctoc": "^2.2.0",
"ember-cli": "~5.12.0",
"ember-cli-clean-css": "^3.0.0",
@@ -79,7 +79,7 @@
"ember-source": ">= 4.0.0"
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"ember": {
"edition": "octane"
@@ -12,6 +12,7 @@ import Component from '@glimmer/component';
* - 403 - `error.isForbidden`: the session is authenticated but does not have
* permission to perform the requested action
* - 404 - `error.isNotFound`: the requested resource could not be found
* - 429 - `error.isRateLimited`: the client has exceeded the rate limit
* - 500 - `error.isServer`: an internal server error occurred
*
* For an unknown error state, i.e error state not matching to the above defined list:
@@ -19,7 +20,7 @@ import Component from '@glimmer/component';
* we don't distinguish it yet
*/
const statuses = ['401', '403', '404', '500'];
const statuses = ['401', '403', '404', '429', '500'];
export default class ErrorMessageComponent extends Component {
// =methods
+3 -3
View File
@@ -37,7 +37,7 @@
"auth": "workspace:*",
"ember-auto-import": "^2.10.0",
"ember-browser-services": "^4.0.3",
"ember-cli-babel": "^8.2.0",
"ember-cli-babel": "^8.3.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-cli-sass": "^11.0.1",
"ember-concurrency": "^4.0.3",
@@ -59,7 +59,7 @@
"@glimmer/component": "^2.0.0",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^9.1.0",
"concurrently": "^10.0.3",
"doctoc": "^2.2.0",
"ember-cli": "~5.12.0",
"ember-cli-clean-css": "^3.0.0",
@@ -95,7 +95,7 @@
"webpack": "^5.105.4"
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"ember": {
"edition": "octane"
+4 -10
View File
@@ -1,12 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8C0 4.22876 0 2.34315 1.17157 1.17157C2.34315 0 4.22876 0 8 0H24C27.7712 0 29.6569 0 30.8284 1.17157C32 2.34315 32 4.22876 32 8V24C32 27.7712 32 29.6569 30.8284 30.8284C29.6569 32 27.7712 32 24 32H8C4.22876 32 2.34315 32 1.17157 30.8284C0 29.6569 0 27.7712 0 24V8Z" fill="url(#paint0_linear_4389_56864)"/>
<path d="M10.9261 25.03V23.6294H13.8501V22.7694H12.4249V21.3688H19.0838L15.9878 16.0123L19.0838 10.6557H12.2529V18.0763H8.5918V6.96997H21.2215L23.3347 10.6311L20.2387 16.0123L23.4084 21.4917L21.3444 25.03H10.9261Z" fill="white"/>
<path d="M11.4914 21.3688H10.0908V22.7694H11.4914V21.3688Z" fill="white"/>
<path d="M9.99237 23.6294H8.5918V25.03H9.99237V23.6294Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_4389_56864" x1="16" y1="0" x2="16" y2="32" gradientUnits="userSpaceOnUse">
<stop stop-color="#F66F74"/>
<stop offset="1" stop-color="#F34D52"/>
</linearGradient>
</defs>
<!-- Plain placeholder mark (not an official logo). -->
<rect width="32" height="32" rx="6" fill="#2b3a4a"/>
<rect x="7" y="7" width="18" height="4.5" fill="#fff"/>
<rect x="13.25" y="11.5" width="5.5" height="13.5" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 1009 B

After

Width:  |  Height:  |  Size: 341 B

+7 -68
View File
@@ -1,69 +1,8 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 377 132">
<path
fill="#fff"
d="M125.45,41.34V35.68h-5.18v5.66h-2.64V27.75h2.64v5.7h5.18v-5.7h2.64V41.34Zm12.32,0h-2.09l-.19-.67a5.69,5.69,0,0,1-3,.87c-1.87,0-2.66-1.23-2.66-2.92,0-2,.9-2.75,3-2.75h2.46v-1c0-1.09-.32-1.47-1.95-1.47a14.85,14.85,0,0,0-2.83.3l-.32-1.87a14,14,0,0,1,3.5-.46c3.21,0,4.15,1.08,4.15,3.54Zm-2.55-3.7h-1.89c-.84,0-1.07.22-1.07,1s.23,1,1,1a4.1,4.1,0,0,0,1.93-.51Zm7.91,3.9a12.53,12.53,0,0,1-3.52-.56L140,39.1a11.5,11.5,0,0,0,3,.43c1.13,0,1.3-.24,1.3-1s-.12-.9-1.78-1.29c-2.49-.58-2.79-1.18-2.79-3.08s.9-2.83,3.82-2.83a14.07,14.07,0,0,1,3.06.34l-.25,2a19,19,0,0,0-2.81-.28c-1.11,0-1.3.24-1.3.84,0,.79.06.85,1.44,1.19,2.85.73,3.13,1.09,3.13,3.1S146.21,41.54,143.13,41.54Zm11.7-.2V34.5c0-.53-.23-.79-.81-.79a7.22,7.22,0,0,0-2.67.83v6.8H148.8V27.56l2.55.38v4.34a9.38,9.38,0,0,1,3.73-.94c1.7,0,2.31,1.14,2.31,2.89v7.11Zm4.71-11.19v-2.4h2.55v2.4Zm0,11.19v-9.8h2.55v9.8Zm4.6-9.72c0-2.46,1.48-3.88,5-3.88a16.63,16.63,0,0,1,3.8.44l-.3,2.19a21.49,21.49,0,0,0-3.41-.34c-1.83,0-2.41.6-2.41,2v5.15c0,1.43.58,2,2.41,2a21.57,21.57,0,0,0,3.41-.35l.3,2.2a16.63,16.63,0,0,1-3.8.44c-3.48,0-5-1.43-5-3.88Zm14.41,9.92c-3.5,0-4.45-1.85-4.45-3.86V35.2c0-2,1-3.86,4.45-3.86S183,33.19,183,35.2v2.48C183,39.69,182.05,41.54,178.55,41.54Zm0-8.11c-1.37,0-1.89.58-1.89,1.69v2.64c0,1.1.52,1.69,1.89,1.69s1.88-.59,1.88-1.69V35.12C180.43,34,179.91,33.43,178.55,33.43Zm11.64.16A20.64,20.64,0,0,0,187.48,35v6.32h-2.55v-9.8h2.16l.16,1.09a11.23,11.23,0,0,1,2.69-1.29Zm10.22,4.49c0,2.17-1,3.46-3.38,3.46a14.74,14.74,0,0,1-2.72-.28v4l-2.56.38V31.54h2l.25.82a5.54,5.54,0,0,1,3.22-1c2.06,0,3.15,1.16,3.15,3.4Zm-6.1,1.11a11.85,11.85,0,0,0,2.26.26c.93,0,1.28-.43,1.28-1.31V34.68c0-.81-.31-1.25-1.26-1.25a3.66,3.66,0,0,0-2.28.89Z">
</path>
<path
fill="#fff"
d="M139.64,70.53c3.08-1.33,4.48-3.21,4.48-6.53V59.82c0-5.45-3.09-8.59-10.72-8.59H117.55V91.52h16.94c7.33,0,11-3.56,11-9.25V78C145.51,74,143.33,71.31,139.64,70.53Zm-14.46-13h7.32c2.78,0,4,1,4,3.27v3.57c0,2.36-1.08,3.75-4.06,3.75h-7.25ZM137.89,81.3c0,3-1.46,3.93-4.91,3.93h-7.8v-11h8.47c3,0,4.24,1.33,4.24,4.06Z">
</path>
<path
fill="#fff"
d="M161.54,61.45c-10.11,0-12.82,5.57-12.82,11.62v7.44c0,6,2.71,11.62,12.82,11.62s12.83-5.57,12.83-11.62V73.07C174.37,67,171.64,61.45,161.54,61.45ZM167,80.76c0,3.32-1.51,5.08-5.45,5.08s-5.44-1.76-5.44-5.08V72.83c0-3.33,1.51-5.08,5.44-5.08S167,69.5,167,72.83Z">
</path>
<path
fill="#fff"
d="M196.51,62.06V82.51c-2.66,1.39-5.87,2.48-7.68,2.48s-2.36-.78-2.36-2.36V62.06h-7.38V83.42c0,5.26,1.75,8.71,6.65,8.71A29.44,29.44,0,0,0,197.54,89l.72,2.48h5.64V62.06Z">
</path>
<path
fill="#fff"
d="M228.4,61.45a29.61,29.61,0,0,0-11.8,3.09l-.72-2.48h-5.64V91.52h7.39V71.08c2.66-1.39,5.87-2.48,7.68-2.48s2.36.78,2.36,2.35V91.52h7.38V70.17C235.05,64.9,233.3,61.45,228.4,61.45Z">
</path>
<path
fill="#fff"
d="M257.5,50V62.31a40.19,40.19,0,0,0-7.87-.86c-6.83,0-9.74,3.88-9.74,10.41v10c0,6.71,3.15,10.22,9.08,10.22A15.49,15.49,0,0,0,258.29,89l.72,2.48h5.87V49Zm0,33.16a10.43,10.43,0,0,1-6.59,2.66c-2.73,0-3.63-1.33-3.63-3.75V71.68c0-2.66,1-3.93,3.68-3.93a32.33,32.33,0,0,1,6.54.79Z">
</path>
<path
fill="#fff"
d="M280.8,61.45a38.34,38.34,0,0,0-10.11,1.4l.91,5.62a41.47,41.47,0,0,1,8.17-.9c4.71,0,5.62,1.15,5.62,4.42v3.08h-7.08c-6,0-8.59,2.3-8.59,8.29,0,5.08,2.3,8.77,7.68,8.77a16.18,16.18,0,0,0,8.78-2.6l.54,2h6.06V72.1C292.78,64.72,290.05,61.45,280.8,61.45Zm4.59,23.3a11.57,11.57,0,0,1-5.56,1.51c-2.3,0-3-.91-3-3,0-2.23.67-2.9,3.09-2.9h5.44Z">
</path>
<path
fill="#fff"
d="M313,61.45a33,33,0,0,0-7.74,3.88l-.48-3.27h-6.24V91.52h7.38v-19a57.85,57.85,0,0,1,7.81-4.3Z">
</path>
<path
fill="#fff"
d="M335.67,62.06,328.9,84.44l-6.72-22.38h-7.56l9.32,29.46h2.59l-3.68,11.92h7l3.93-11.92,9.43-29.46Z">
</path>
<polygon
fill="#f04e54"
points="44.94 102.47 44.94 97.64 54.96 97.64 54.96 94.67 50.1 94.67 50.1 89.82 73.03 89.82 62.38 71.38 73.03 52.93 49.53 52.93 49.53 78.51 36.88 78.51 36.88 40.29 80.4 40.29 87.67 52.87 76.98 71.38 87.88 90.26 80.84 102.47 44.94 102.47">
</polygon>
<rect
fill="#f04e54"
x="42.04"
y="89.82"
width="4.85"
height="4.85">
</rect>
<rect
fill="#f04e54"
x="36.88"
y="97.61"
width="4.85"
height="4.85">
</rect>
<svg width="200" height="67" viewBox="0 0 200 67" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Threshold">
<!-- Plain placeholder wordmark (not an official logo). -->
<g class="logomark" fill="#ffffff">
<rect x="2" y="14" width="40" height="9"></rect>
<rect x="16.5" y="23" width="11" height="30"></rect>
</g>
<text class="logotype" x="54" y="43" fill="#ffffff" font-family="Helvetica, Arial, sans-serif" font-size="26" font-weight="600">Threshold</text>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 491 B

+4 -68
View File
@@ -1,71 +1,7 @@
<svg
width="30"
height="30"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 67 67">
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Plain placeholder mark (not an official logo). -->
<g class="logomark">
<polygon
fill="#f04e54"
points="9.89 64.1 9.89 59.28 19.9 59.28 19.9 56.3 15.05 56.3 15.05 51.45 37.97 51.45 27.32 33.01 37.97 14.57 14.47 14.57 14.47 40.15 1.82 40.15 1.82 1.92 45.34 1.92 52.61 14.5 41.92 33.01 52.83 51.9 45.78 64.1 9.89 64.1">
</polygon>
<rect
fill="#f04e54"
x="6.98"
y="51.45"
width="4.85"
height="4.85">
</rect>
<rect
fill="#f04e54"
x="1.82"
y="59.25"
width="4.85"
height="4.85">
</rect>
</g>
<g class="logotype">
<path
fill="#ffffff"
d="M104.58,32.17c3.08-1.33,4.48-3.21,4.48-6.54V21.45c0-5.44-3.09-8.59-10.72-8.59H82.5v40.3H99.44c7.32,0,11-3.57,11-9.26V39.61C110.45,35.67,108.27,33,104.58,32.17Zm-14.46-13h7.32c2.78,0,4,1,4,3.27V26c0,2.36-1.08,3.76-4.05,3.76H90.12Zm12.71,23.78c0,3-1.46,3.94-4.91,3.94h-7.8v-11h8.47c3,0,4.24,1.33,4.24,4Z">
</path>
<path
fill="#ffffff"
d="M126.48,23.09c-10.1,0-12.82,5.56-12.82,11.62v7.44c0,6.05,2.72,11.61,12.82,11.61s12.83-5.56,12.83-11.61V34.71C139.31,28.65,136.58,23.09,126.48,23.09Zm5.45,19.3c0,3.33-1.51,5.08-5.45,5.08S121,45.72,121,42.39V34.47c0-3.34,1.51-5.09,5.44-5.09s5.45,1.75,5.45,5.09Z">
</path>
<path
fill="#ffffff"
d="M161.46,23.69V44.15c-2.67,1.39-5.88,2.48-7.69,2.48s-2.36-.79-2.36-2.37V23.69H144V45.05c0,5.27,1.75,8.71,6.65,8.71a29.59,29.59,0,0,0,11.8-3.08l.73,2.48h5.63V23.69Z">
</path>
<path
fill="#ffffff"
d="M193.34,23.09a29.59,29.59,0,0,0-11.8,3.08l-.72-2.48h-5.63V53.16h7.38V32.71c2.66-1.39,5.87-2.48,7.68-2.48s2.36.79,2.36,2.36V53.16H200V31.8C200,26.54,198.24,23.09,193.34,23.09Z">
</path>
<path
fill="#ffffff"
d="M222.44,11.66V23.94a41,41,0,0,0-7.87-.85c-6.83,0-9.73,3.87-9.73,10.41v10c0,6.72,3.14,10.22,9.07,10.22a15.48,15.48,0,0,0,9.32-3.08l.72,2.48h5.87V10.63Zm0,33.15a10.43,10.43,0,0,1-6.59,2.66c-2.73,0-3.63-1.33-3.63-3.75V33.32c0-2.67,1-3.94,3.69-3.94a33,33,0,0,1,6.53.79Z">
</path>
<path
fill="#ffffff"
d="M245.74,23.09a38.76,38.76,0,0,0-10.11,1.39l.91,5.63a40.74,40.74,0,0,1,8.17-.91c4.71,0,5.62,1.15,5.62,4.42V36.7h-7.07c-6,0-8.6,2.3-8.6,8.29,0,5.09,2.3,8.77,7.69,8.77a16.06,16.06,0,0,0,8.77-2.6l.54,2h6.06V33.74C257.72,26.35,255,23.09,245.74,23.09Zm4.59,23.29a11.57,11.57,0,0,1-5.56,1.51c-2.3,0-3-.9-3-3,0-2.24.67-2.9,3.09-2.9h5.44Z">
</path>
<path
fill="#ffffff"
d="M277.92,23.09A33,33,0,0,0,270.18,27l-.48-3.27h-6.24V53.16h7.39v-19a56,56,0,0,1,7.8-4.29Z">
</path>
<path
fill="#ffffff"
d="M300.61,23.69l-6.77,22.39-6.72-22.39h-7.56l9.32,29.47h2.59l-3.68,11.92h7l3.93-11.92,9.44-29.47Z">
</path>
<rect x="3" y="3" width="24" height="6" fill="currentColor"></rect>
<rect x="12" y="9" width="6" height="18" fill="currentColor"></rect>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 336 B

+3 -23
View File
@@ -1,24 +1,4 @@
<svg
width="30"
height="30"
viewBox="0 0 30 30"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<g class="logomark">
<path
d="M0 7.19579L11.4553 0.58728V5.55192L4.30398 9.68052V25.5537L0 23.069V7.19579Z"
fill="currentColor">
</path>
<path
d="M16.0285 0.58728V13.2291H11.4553V8.51084L7.15165 10.9956V27.1919L11.4553 29.6829V17.0771H16.0285V21.7641L20.3321 19.2788V3.07229L16.0285 0.58728Z"
fill="currentColor">
</path>
<path
d="M27.4838 23.0789L16.0285 29.6875V24.7228L23.1798 20.5939V4.72074L27.4838 7.20576V23.0789Z"
fill="currentColor">
</path>
</g>
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Plain placeholder (no company logo). -->
<rect x="3" y="3" width="24" height="24" rx="4" stroke="currentColor" stroke-width="2"></rect>
</svg>

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 248 B

@@ -52,6 +52,16 @@ module('Integration | Component | error/message', function (hooks) {
.hasText(
'We could not find the requested resource. You can ask your administrator or try again later.',
);
await render(hbs`<Error::Message @status='429' />`);
assert.dom(ERROR_MSG_TITLE).hasText('Rate limit exceeded');
assert.dom(ERROR_MSG_CODE).hasText('ERROR 429');
assert
.dom(ERROR_MSG_BODY)
.hasText(
"You've exceeded the rate limit. Please wait a moment and try again.",
);
});
test('it renders default error for unknown error status', async function (assert) {
+3 -1
View File
@@ -23,13 +23,13 @@ retry: Retry
play: Play
pause: Pause
resume: Resume
export: Export
get-topic-help: Get help for this topic
copy-to-clipboard: Copy to Clipboard
copy-error-detail-to-clipboard: Copy error detail to clipboard
authenticate: Sign In
deauthenticate: Sign Out
choose-org: Choose Scope
choose-different-org: Choose a different scope
add-host-set: Create Host Set
add-host: Create Host
add-principals: Add Principals
@@ -69,3 +69,5 @@ hide-errors: Hide Errors
edit-worker-filter: Edit Worker Filter
add-worker-filter: Add Worker Filter
open: Open
download: Download
show-more: Show more
+15 -14
View File
@@ -24,9 +24,10 @@ titles:
empty-set: No Items Yet
global: Global
actions: Actions
action: Action
documentation: Documentation
abandon-confirm: Discard unsaved changes?
welcome-to-boundary: Welcome to Boundary
welcome-to-boundary: Welcome to Threshold
label: Label
value: Value
no-results: No Results
@@ -83,27 +84,27 @@ onboarding:
target-port: Default Port
target-setup-info: We’ll set up scopes for you
descriptions:
setup-first-target: In Boundary, a “target” defines a server that your users will connect to. We can help you set up your first target for proof-of-concept testing, or you can skip this step if you don’t want to do it right now.
setup-first-target: In Threshold, a “target” defines a server that your users will connect to. We can help you set up your first target for proof-of-concept testing, or you can skip this step if you don’t want to do it right now.
target-address: Host name or IP address.
target-port: The default port on which to connect.
target-setup-info: Targets live in projects, and projects live in orgs. We will create these example scopes for you but they can be edited or deleted later.
success:
titles:
connect: Connect to your target
desktop: Using Boundary Desktop
desktop: Using Threshold Desktop
developers: Developers
controller: Controller URL
creation-success: We have created your scopes and target
descriptions:
download: Download the Boundary Desktop app to browse and connect to targets (available for Windows, Mac, and Linux)
developers: You can also connect to targets using the Boundary CLI or API.
controller: Use this URL to sign in to Boundary
role: You can now connect to the target with the Boundary Desktop app, command-line (CLI) or API. Boundary grants access to resources based on roles, and we have added you to the `demo` role to give you access to this target. Make sure to add this role to any new users if you want them to access it too.
download: Download the Threshold Desktop app to browse and connect to targets (available for Windows, Mac, and Linux)
developers: You can also connect to targets using the Threshold CLI or API.
controller: Use this URL to sign in to Threshold
role: You can now connect to the target with the Threshold Desktop app, command-line (CLI) or API. Threshold grants access to resources based on roles, and we have added you to the `demo` role to give you access to this target. Make sure to add this role to any new users if you want them to access it too.
links:
install: Install the Boundary Desktop app
learn: Learn about Boundary Desktop app
cli: Learn about Boundary command-line (CLI)
api: Learn about Boundary API
install: Install the Threshold Desktop app
learn: Learn about Threshold Desktop app
cli: Learn about Threshold command-line (CLI)
api: Learn about Threshold API
unsupported-controller:
title: Unsupported controller version
description:
@@ -119,7 +120,7 @@ settings:
cli: CLI
cache-daemon: Cache Daemon
client-agent:
title: Boundary Client Agent
title: Threshold Client Agent
description: Required to use Transparent Sessions
client: Client
version: Version
@@ -129,7 +130,7 @@ settings:
self-managed: Self-managed
server:
title: Server
description: Boundary connects you to targets using this server managed by your organization.
description: Threshold connects you to targets using this server managed by your organization.
logs:
title: Logs
logging-level: Logging level
@@ -144,7 +145,7 @@ settings:
client-agent: There may be a problem with the client agent
preferred-clients:
title: Preferred Clients
description: Select the applications for Boundary to use when opening these target types.
description: Select the applications for Threshold to use when opening these target types.
table:
header:
protocol: Protocol
@@ -13,6 +13,11 @@
500:
title: Server error
description: We ran into a problem and could not continue. You can ask your administrator or try again later.
429:
title: Rate limit exceeded
description: You've exceeded the rate limit. Please wait a moment and try again.
description-partial: Currently showing partial results. You can reload for more in {seconds} {seconds, plural, one {second} other {seconds}}.
description-ready: Currently showing partial results. You can reload now.
unknown:
title: Something went wrong
description: We're not sure what happened. Please contact your administrator or try again later.
+7 -6
View File
@@ -55,7 +55,7 @@ session_id:
label: Session ID
worker_release_version:
label: Release Version
help: The version of the Boundary binary the worker is running.
help: The version of the Threshold binary the worker is running.
worker_authorized_actions:
label: Authorized Actions
worker_last_seen:
@@ -70,12 +70,12 @@ grant_scope:
help: Specify the scope to which this role applies.
cluster-url:
label: Cluster URL
description: The Boundary URL to which to connect (e.g. protocol://host:port)
description: The Threshold URL to which to connect (e.g. protocol://host:port)
proxy_url:
label: Proxy URL
issuer:
label: Issuer
help: The issuer URL. Boundary expects only the schema, host, and port and will strip off ".well-known/openid-configuration" if present. This will be used for configuration discovery as well as for validation of the "iss" claim.
help: The issuer URL. Threshold expects only the schema, host, and port and will strip off ".well-known/openid-configuration" if present. This will be used for configuration discovery as well as for validation of the "iss" claim.
client_id:
label: Client ID
help: An OAuth 2.0 Client Identifier valid at the Authorization Server.
@@ -159,7 +159,7 @@ secret_access_key:
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.
help: The client secret value generated by Azure for Threshold to use for authentication.
disable_credential_rotation:
label: Disable credential rotation
help: Azure host catalogs do not yet support credential rotation.
@@ -173,16 +173,17 @@ ip_addresses:
help: Read-only public and private IP addresses given by the plugin host catalog provider
sync-interval:
label: Sync Interval
help: Number of seconds between the time Boundary syncs hosts in this set.
help: Number of seconds between the time Threshold syncs hosts in this set.
tags:
label: Tags
secret-editor:
label: Secret Editor
details: This secret is saved but won't be displayed
edit: Click to replace this secret
created:
label: Created
other_fields:
label: Other Fields
help: This information is provided by the server and cannot be edited in Boundary
help: This information is provided by the server and cannot be edited in Threshold
worker_filter:
label: Worker Filter
+216 -25
View File
@@ -4,7 +4,7 @@
account:
title: Account
title_plural: Accounts
description: An account allows a user to log into Boundary with a particular auth method. Users may have multiple accounts for different auth methods.
description: An account allows a user to log into Threshold with a particular auth method. Users may have multiple accounts for different auth methods.
messages:
welcome:
title: Welcome to Accounts
@@ -167,13 +167,13 @@ auth-method:
help: This group includes inputs for configuring the LDAP server itself, such as the server name, port, and authentication settings.
authenticated-search:
title: Authenticated search
help: Used when Boundary authenticates to the LDAP server.
help: Used when Threshold authenticates to the LDAP server.
anonymous-search:
title: Anonymous search
help: Allows you to bind anonymously to the directory when searching for user entries.
user-entries:
title: User entries
help: Before authenticating an end user, Boundary must first find the end user’s entry in the directory.
help: Before authenticating an end user, Threshold must first find the end user’s entry in the directory.
group-entries:
title: Group entries
help: After authenticating an end user, we need to find out which groups the user is a member of. The configuration for this can vary depending on your LDAP server and your directory schema.
@@ -239,7 +239,7 @@ host-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.
help: The client (application) ID of an Azure service principal that Threshold will use to authenticate and discover hosts from Azure.
worker_filter:
help: Insert a worker filter in order to select the correct worker.
project_id:
@@ -302,7 +302,7 @@ host:
title: Host
title_plural: Hosts
no_name_title: Unnamed Host
description: A host is a resource that may be accessed by a Boundary target.
description: A host is a resource that may be accessed by a Threshold target.
details: Host Details
messages:
welcome:
@@ -323,7 +323,7 @@ session:
title: Session
title_plural: Sessions
active_plural: Active Sessions
description: A session is a connection to a target initiated through Boundary.
description: A session is a connection to a target initiated through Threshold.
remaining: remaining
status:
active: Active
@@ -344,7 +344,7 @@ session:
title: Connected
description: You can now access {targetDisplayName}
stop-sessions-and-signout:
title: Sign out of Boundary?
title: Sign out of Threshold?
description: Signing out will stop all active and pending target sessions. Are you sure you want to sign out?
stop-sessions-before-quit:
title: Close sessions before quitting?
@@ -420,6 +420,13 @@ session-recording:
title: Channels by connection
messages:
no-channels: There are no channels for this connection
flyout:
title: Session playback errors
sync-error:
title: This recording is not available
flyout-section-title: Sync error
verification-error:
flyout-section-title: Verification error
started-no-connections:
title: Session Recording In Progress
description: The Session has started but no connections have been made.
@@ -532,7 +539,7 @@ storage-bucket:
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.
help: Disabling credential rotation could allow entities outside of Threshold to know the client secret in use.
questions:
delete-storage-bucket:
title: Delete associated recordings?
@@ -553,7 +560,7 @@ project:
user:
title: User
title_plural: Users
description: Users are entities authorized to access Boundary. Users may be assigned to roles as principals, thus receiving role grants.
description: Users are entities authorized to access Threshold. Users may be assigned to roles as principals, thus receiving role grants.
messages:
welcome:
title: Welcome to Users
@@ -611,6 +618,7 @@ role:
title: Grant
title_plural: Grants
description: Grants are permissions which allow roles to take actions and access resources.
codeblock-description: Select "{editGrantsTitle}" to add or modify grants. Select "{addGrantTemplatesTitle}" to add in templates.
actions:
create: New Grant
grant-templates:
@@ -626,13 +634,19 @@ role:
templates:
global-admin:
name: Global Admin
description: Full administrative access to all resources in Boundary
description: Full administrative access to all resources in Threshold
org-access:
name: Org Access
description: Administrative access to perform actions on an org scope, but not the resources the org contains
org-admin:
name: Org Admin
description: Administrative access to all resources within an organization
description: Administrative access to perform actions on the resources an org contains
project-access:
name: Project Access
description: Administrative access to perform actions on a project scope, but not the resources the project contains
project-admin:
name: Project Admin
description: Administrative access to all resources within a project
description: Administrative access to perform actions on the resources a project contains
session-manager:
name: Session Manager
description: Can view and cancel sessions
@@ -655,11 +669,188 @@ role:
name: Session Auditor
description: Can view session recordings
target-user:
name: Target User
name: User
description: Can connect to targets but cannot manage anything
read-only-user:
name: Read-Only User
description: Can view but not modify any resources
name: Resource Auditor
description: Can only view but not modify any resources
edit-grants:
title: Edit Grants
description: Modify existing grant strings or add new grant strings to this role.
completion-info:
no-suggestions: No suggestions
wildcard-types: Wildcard - matches all types
wildcard-ids: Wildcard - matches all IDs
wildcard-actions: Wildcard - matches all actions
template-value: Template value
all-fields: All fields
loading-ids: Still loading ID suggestions…
linting-errors:
general:
whitespace-not-allowed: Whitespace is not allowed
leading-semicolon-not-allowed: Leading semicolon is not allowed
trailing-semicolon-not-allowed: Trailing semicolon is not allowed
invalid-character: Invalid character "{character}"
invalid-format: 'Invalid format: expected key=value'
invalid-syntax: Invalid syntax
fields:
duplicate-field: Duplicate field "{field}"
unknown-field: Unknown field "{field}"
empty-value: '"{field}" value cannot be empty'
missing-ids-or-type: Missing "ids" or "type" fields
missing-actions: Missing "actions" field
missing-actions-or-output-fields: Missing "actions" or "output_fields" fields
type:
invalid-type: Invalid resource type "{type}"
not-top-level: Type "{type}" is not a top-level type
not-child-type: Type "{type}" is not a child type of the pinned ID
missing-for-wildcard-ids: Missing "type" field for wildcard ids
wildcard-requires-ids: Missing "ids" field for wildcard type
ids:
empty-list: '"ids" field does not contain at least one id'
wildcard-cannot-combine: Wildcard id "*" cannot be combined with other ids
templates-cannot-combine: Template ids cannot be combined with each other
duplicate-template: Duplicate id "{primary}" and "{secondary}"
duplicate-id: Duplicate id "{id}"
unknown-template: Unknown template "{id}"
invalid-id: Invalid id "{id}"
mismatched-types: Ids do not have the same type. Invalid id "{id}"
must-support-child-types: Pinned IDs must support child types. Invalid id "{id}"
must-match-parent-type: Pinned IDs must be "{parentType}" ids. Invalid id "{id}"
actions:
empty-list: '"actions" field does not contain at least one action'
wildcard-cannot-combine: Wildcard action "*" cannot be combined with other actions
duplicate-action: Duplicate action "{action}"
invalid-action: Invalid action "{action}"
id-only: Only id actions for "{type}" type are allowed. Invalid action "{action}"
collection-only: Only collection actions are allowed. Invalid action "{action}"
noop-requires-list: '"no-op" action should only be used with "list" action'
list-requires-noop: '"list" action should be used with "no-op" action to have an effect'
noop-unnecessary: '"no-op" action is unnecessary when other actions are specified'
editor:
remove: Remove
remove-comma: Remove comma
add-noop: Add "no-op" action
output-fields:
empty-list: '"output_fields" field does not contain at least one field'
wildcard-cannot-combine: Wildcard output field "*" cannot be combined with other fields
duplicate-field: Duplicate output field "{field}"
invalid-field: Invalid output field "{field}"
invalid-field-for-type: Invalid output field "{field}" for type "{type}"
editor:
title: Grant Editor
description: Add each new grant string on a different line. Press Ctrl+Space to get suggestions.
string-formats:
title_plural: String formats
syntax: Syntax
syntax-format: Syntax format
more-info: More info
resource-type:
label: Singular resource type
help: Applies collection actions to a specific resource type in a single grant.
resource:
label: Specific resource(s)
help: Applies ID actions to one or more individual resources in a single grant.
pinned-id:
label: Pinned ID
help: '"Pins" actions to a non-top-level type within a specific ID in a single grant.'
output-fields:
label: Output fields
help: Defines which fields from a request are returned to the user.
account-template:
label: Account template
help: Substitutes the account ID from the token used to perform the request.
user-template:
label: User template
help: Substitutes the user ID from the token used to perform the request.
insert-actions: insert actions
insert-resource-type: insert resource type
insert-ids: insert ids
insert-resource-ids: insert resource ids
insert-output-fields: insert output fields
export:
format: Format
formatted-export-aria-label: Formatted export
options:
terraform: Terraform
native-hcl: Native HCL
actions:
collection-actions: Collection Actions
id-actions: ID Actions
create: Create a new {resourceType}
list: List all {resourceType} resources
read: Read {article} {resourceType}
update: Update {article} {resourceType}
delete: Delete {article} {resourceType}
read:self: Read the {resourceType} associated with the current user
delete:self: Delete {article} {resourceType} associated with the current user
cancel: Cancel {article} {resourceType}
cancel:self: Cancel {article} {resourceType} associated with the current user
generic:
create: Creates resources
list: Lists resources
read: Reads resources
update: Updates resources
delete: Deletes resources
read:self: Read the resource associated with the current user
delete:self: Delete a resource associated with the current user
cancel: Cancels resources
cancel:self: Cancel a resource associated with the current user
add-members: Add members to a group
set-members: Set the full list of members on a group
remove-members: Remove members from a group
add-hosts: Add hosts to a host set
set-hosts: Set the full list of hosts on a host set
remove-hosts: Remove hosts from a host set
add-worker-tags: Add tags to a worker
set-worker-tags: Set the full list of tags on a worker
remove-worker-tags: Remove tags from a worker
reinitialize-certificate-authority: Reinitialize the certificate authority that is used to authorize Threshold workers
create:controller-led: Create a worker via controller-led workflow
create:worker-led: Create a worker via worker-led workflow
read-certificate-authority: Read the certificate authority that is used to authorize Threshold workers
set-password: Set a password on an account, without requiring the current password
change-password: Change the password on an account
authenticate: Authenticate to an auth method
change-state: Change the active and visibility state of an OIDC-type auth method
add-accounts: Add accounts to a user
set-accounts: Set the full list of accounts on a user
remove-accounts: Remove accounts from a user
list-resolvable-aliases: List all aliases that point to resources the user has permission to access
add-principals: Add principals to a role
set-principals: Set the full list of principals on a role
remove-principals: Remove principals from a role
set-grants: Set the full list of grants on a role
add-grants: Add grants to a role
remove-grants: Remove grants from a role
add-grant-scopes: Add grant scopes to a role
set-grant-scopes: Set the full list of grant scopes on a role
remove-grant-scopes: Remove grant scopes from a role
authorize-session: Authorize a session via a target
add-host-sources: Add host sources to a target
set-host-sources: Set the full list of host sources on a target
remove-host-sources: Remove host sources from a target
add-credential-sources: Add credential sources to a target
set-credential-sources: Set the full list of credential sources on a target
remove-credential-sources: Remove credential sources from a target
attach-storage-policy: Attach a storage policy to all session recordings in the scope
detach-storage-policy: Detach a storage policy from all session recordings in the scope
set-alias-suffix: Set the alias suffix on a scope
remove-alias-suffix: Remove the alias suffix from a scope
list-keys: List the keys within a given scope
rotate-keys: Replace a scope's current KEK and DEKs with a new set of keys
list-key-version-destruction-jobs: List all pending key version destruction jobs within a scope
destroy-key-version: Destroy a key version in the scope
download: Download a session recording
reapply-storage-policy: Reapply the storage policy to a session recording
list-exports: List all export jobs for session recordings
export: Export a session recording
export:cancel: Cancel an in-progress session recording export
monthly-active-users: Display the number of monthly active Threshold users to help predict billing
no-actions: No actions available for this resource type.
invalid-id-or-type: Invalid ID and type. No actions available.
no-type-detected: No resource type detected.
unavailable: Grant actions information not available.
scope:
title: Scope
title_plural: Scopes
@@ -745,7 +936,7 @@ target:
title: Welcome to Targets
none:
title: No Targets Available
description: No Targets to display yet. Contact your Boundary admin if you don't see a Target you expect access to.
description: No Targets to display yet. Contact your Threshold admin if you don't see a Target you expect access to.
connection-success:
title: Successfully Connected
connection-details:
@@ -862,10 +1053,10 @@ target:
description: Select injected Application credential sources to assign to this target.
alert:
title: Cannot connect to this target without injected application credentials
description: Injected application credentials are used by Boundary to sign in to remote resources without showing them to the end-user.
description: Injected application credentials are used by Threshold to sign in to remote resources without showing them to the end-user.
action: Add injected application credentials
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.
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 Threshold can access the host. Ingress and egress filters are allowed to select the same worker.
messages:
none:
description: You haven't added an {type} worker filter yet.
@@ -891,7 +1082,7 @@ target:
description: Specify workers that have access to the target (such as within a private network).
edit-ingress-worker-filter:
title: Edit Ingress Worker Filter
description: Specify workers Boundary should use to start the route to this target.
description: Specify workers Threshold should use to start the route to this target.
worker-filter:
description: The worker filter format for this target.
accordion-label:
@@ -907,7 +1098,7 @@ credential-store:
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.
description: A filter used to control which PKI workers can handle Vault requests, allowing the use of private Vault instances with Threshold.
messages:
none:
description: You haven't added a worker filter yet.
@@ -1151,20 +1342,20 @@ worker:
view_more: View more tags
form:
cluster_id:
label: Boundary Cluster ID
label: Threshold Cluster ID
help: The cluster ID is the UUID in the HCP Boundary Controller URL.
ip_address:
label: Worker public address
help: Public IP or address.
initial_upstreams:
label: Initial Upstreams
help: A list of hosts/IP addresses and optionally ports for reaching Boundary cluster. The port will default to :9201 if not specified.
help: A list of hosts/IP addresses and optionally ports for reaching Threshold cluster. The port will default to :9201 if not specified.
config_file_path:
label: Config file path
help: The path on your server where you will store your configuration file.
worker_tags:
label: Worker Tags
help: Boundary can use tags to define key-value pairs which targets can use to determine where they should route connections.
help: Threshold can use tags to define key-value pairs which targets can use to determine where they should route connections.
local_session_recording_storage:
label: Local Session Recording Storage
help: To record sessions using this worker, specify a local storage path and ensure that it is provisioned with sufficient capacity. In progress recordings are stored locally until they complete, and then moved to the remote storage bucket associated with their target.
@@ -1180,8 +1371,8 @@ worker:
create_config: 'Open the file with a text editor, such as Vi. Paste the following configuration into the worker config file:'
save_config: Save the configuration in your <code>pki-worker.hcl</code> file.
2:
title: 2. Install & start Boundary on your server
run_command: 'Run this command on your server to install Boundary:'
title: 2. Install & start Threshold on your server
run_command: 'Run this command on your server to install Threshold:'
copy_registration_request: When the server starts, it will print details related to this worker. Copy the value for
3:
title: 3. Confirm the worker when they connect
@@ -1,11 +0,0 @@
{{!
Copyright IBM Corp. 2021, 2026
SPDX-License-Identifier: BUSL-1.1
}}
<div
data-test-code-editor-field-editor
...attributes
class='rose-codemirror-container'
{{code-mirror value=@value onInput=@onInput options=@options}}
></div>
@@ -1,13 +0,0 @@
{{!
Copyright IBM Corp. 2021, 2026
SPDX-License-Identifier: BUSL-1.1
}}
<div data-test-code-editor>
{{yield
(hash
fieldEditor=(component 'rose/code-editor/field-editor' value=@codeValue)
toolbar=(component 'rose/code-editor/toolbar' copyText=@codeValue)
)
}}
</div>
@@ -1,31 +0,0 @@
{{!
Copyright IBM Corp. 2021, 2026
SPDX-License-Identifier: BUSL-1.1
}}
<div class='rose-code-editor-toolbar' data-test-code-editor-toolbar>
<div data-test-code-editor-toolbar-action class='code-editor-toolbar__action'>
{{yield to='action'}}
</div>
<div class='rose-code-editor-toolbar__right-aligned'>
{{#if (has-block 'menu')}}
<div
data-test-code-editor-toolbar-menu
class='rose-code-editor-toolbar__menu'
>
{{yield to='menu'}}
</div>
<div
class='rose-code-editor-toolbar__menu-divider'
data-test-code-editor-toolbar-menu-divider
></div>
{{/if}}
<Hds::Copy::Button
@text='Copy code'
@textToCopy={{@copyText}}
@onSuccess={{this.copied}}
data-test-code-editor-toolbar-copy-button
/>
</div>
</div>
@@ -1,14 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class RoseCodeEditorToolbarComponent extends Component {
@action
copied() {
this.args.onCopy?.();
}
}
@@ -1,99 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import Modifier from 'ember-modifier';
import codemirror from 'codemirror';
import '../utilities/register-codemirror-hcl';
import 'codemirror/mode/go/go';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/shell/shell';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/display/autorefresh';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/lint/json-lint';
// Here we define default options for the editor.
// These should follow the codemirror configuration types
//
const _PRESET_DEFAULTS = {
theme: 'monokai',
lineNumbers: true,
cursorBlinkRate: 500,
matchBrackets: true,
autoCloseBrackets: true,
styleActiveLine: true,
mode: 'hcl',
};
/**
*
* `CodeMirror` implements a modifier that creates a CodeMirror instance on the provided element.
*
* The supported modes for the editor are currently: hcl, shell, go, javascript
* The supported themes are: monokai
*
* Sample usage:
* ```
* <div
* {{code-mirror value=@value onInput=@onInput options=@options}}
* />
* ```
*
* @class CodeMirror
*
*/
export default class CodeMirrorModifier extends Modifier {
_editor;
didSetup = false;
modify(element, _, named) {
if (!this.didSetup) {
this._setup(element, named);
this.didSetup = true;
}
if (this._editor?.getValue() !== named.value) {
this._editor?.setValue(named.value);
}
}
/**
* Called when editor contents change.
* @method CodeMirror#_onChange
* See documentation for the CodeMirror editor events, specifically 'change' event here https://codemirror.net/doc/manual.html#events.
*/
_onChange(editor, named) {
const { onInput } = named;
const newVal = editor.getValue();
onInput && onInput(newVal);
}
/**
* Initializes the editor with default configuration merged with consumer-provided configuration.
* Also registers a 'change' event on the editor.
* @method CodeMirror#_setup
*/
_setup(element, named) {
if (!element) {
throw new Error('CodeMirror modifier has no element');
}
const editor = codemirror(element, {
..._PRESET_DEFAULTS,
...(named.options || {}),
value: named.value,
});
editor.on('change', (editor) => {
this._onChange(editor, named);
});
this._editor = editor;
}
}
@@ -13,6 +13,7 @@
form {
[class*='hds-form-field--layout'],
[class*='hds-form-group--layout'],
[class='hds-form-key-value-inputs'],
.rose-form-actions.hds-button-set {
margin-bottom: 1.5rem;
}
@@ -121,6 +121,15 @@
}
}
@mixin dark-mode-badge-count-override {
// Override the neutral badge count to fix contrast in dark mode.
/* stylelint-disable selector-class-pattern */
.hds-badge-count--color-neutral.hds-badge-count--type-inverted {
color: var(--token-color-palette-neutral-0);
}
/* stylelint-enable selector-class-pattern */
}
@mixin dark-mode-ember-dropdown-override {
.ember-basic-dropdown-content {
background-color: var(--token-color-palette-neutral-0);
@@ -191,20 +200,95 @@
}
}
// Overrides for CodeMirror 6 autocomplete and lint tooltips/panels.
@mixin dark-mode-codemirror-override {
/* stylelint-disable selector-class-pattern */
// Autocomplete dropdown and lint hover tooltips
.cm-tooltip {
background-color: var(--token-color-palette-neutral-50);
border: 1px solid var(--token-color-palette-neutral-100);
color: var(--token-color-palette-neutral-400);
}
// Arrow pointer for tooltips shown above the cursor
.cm-tooltip-above::before {
border-top-color: var(--token-color-palette-neutral-50);
}
// Arrow pointer for tooltips shown below the cursor
.cm-tooltip-below::before {
border-bottom-color: var(--token-color-palette-neutral-50);
}
// Autocomplete list > selected item highlight
.cm-tooltip-autocomplete > ul > li[aria-selected] {
background-color: var(--token-color-palette-blue-300);
color: var(--token-color-palette-neutral-700);
}
.cm-editor {
// Content/gutter border
.cm-content {
border-left: 1px solid var(--token-color-palette-neutral-100);
}
// Lint diagnostics panel (shown at the bottom of the editor)
.cm-panel.cm-panel-lint {
border-top: 3px solid var(--token-color-palette-neutral-100);
ul {
background-color: var(--token-color-palette-neutral-100);
// Lint panel > selected diagnostic item highlight
&:focus li.cm-diagnostic[aria-selected] {
background-color: var(--token-color-palette-neutral-50);
color: var(--token-color-palette-neutral-400);
}
}
}
}
/* stylelint-enable selector-class-pattern */
}
// Override to improve visibility of search clear ("x") icon.
@mixin dark-mode-search-clear-override {
input[type='search']::-webkit-search-cancel-button {
filter: invert(1);
}
}
@media (prefers-color-scheme: dark) {
// Apply dark style overrides to scrollbar when system is in dark mode and app theme is not set to light
html:has(.ember-application:not(.rose-theme-light)) {
scrollbar-color: var(--token-color-palette-neutral-400)
var(--token-color-palette-neutral-600);
}
.ember-application:not(.rose-theme-light) {
@include dark-mode;
@include dark-mode-modal-override;
@include dark-mode-badge-count-override;
@include dark-mode-ember-dropdown-override;
@include dark-mode-app-header-override;
@include dark-mode-app-side-nav-override;
@include dark-mode-codemirror-override;
@include dark-mode-search-clear-override;
}
}
// Apply dark style overrides to scrollbar when app theme is set to dark regardless of system theme
html:has(.ember-application.rose-theme-dark) {
scrollbar-color: var(--token-color-palette-neutral-400)
var(--token-color-palette-neutral-600);
}
.ember-application.rose-theme-dark {
@include dark-mode;
@include dark-mode-modal-override;
@include dark-mode-badge-count-override;
@include dark-mode-ember-dropdown-override;
@include dark-mode-app-header-override;
@include dark-mode-app-side-nav-override;
@include dark-mode-codemirror-override;
@include dark-mode-search-clear-override;
}
@@ -1,43 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import codemirror from 'codemirror';
import 'codemirror/addon/mode/simple';
// This utility defines 'hcl' mode for a codemirror editor and enables correct syntax highlighting.
// Importing this file in the codemirror modifier will allow 'hcl' to be selected as a mode:
// import 'cloud-ui-core/utils/register-codemirror-hcl'
codemirror.defineSimpleMode('hcl', {
start: [
{ regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string' }, // strings
{ regex: /(\w+)(\s+)(=)/, token: ['keyword', 'null', 'null'] }, // assignment of variables
{
regex: /(build|deploy|release|hook|registry|type|runner|url)( )({)/,
token: ['keyword', 'null', 'null'],
}, // top level keywords
{ regex: /(variable)\b/, token: 'keyword' }, // input variable keyword
{ regex: /true|false|null|undefined/, token: 'atom' }, // bool keywords
{
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: 'number',
}, // numbers
{ regex: /(#|\/\/)(\s*\S*)/, token: 'comment' }, // single line comments
{ regex: /(path)(.)/, token: ['variable-2', 'null'] }, // path variables
{ regex: /(workspace)(.)(\S*)/, token: ['string', 'null', 'string'] }, // workspace variables
{ regex: /\/\*/, token: 'comment', next: 'comment' }, // multi-line comment
{ regex: /[-+/*=<>!]+/, token: 'operator' }, // operators
{ regex: /[{[(]/, indent: true }, // for auto indent
{ regex: /[}\])]/, dedent: true },
],
// The multi-line comment state.
comment: [
{ regex: /.*?\*\//, token: 'comment', next: 'start' },
{ regex: /.*/, token: 'comment' },
],
meta: {
dontIndentStates: ['comment'],
lineComment: '#',
},
});
@@ -1,6 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/code-editor';
@@ -1,6 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/code-editor/field-editor';
@@ -1,6 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/components/rose/code-editor/toolbar';
-6
View File
@@ -1,6 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'rose/modifiers/code-mirror';
@@ -5,7 +5,6 @@
// This list is sorted in ascending order to improve readability
@use 'code-editor';
@use 'form';
@use 'frame';
@use 'layout';
@@ -1,70 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
/* stylelint-disable selector-class-pattern */
@use '../../variables/sizing';
// Codemirror overrides
.rose-codemirror-container {
width: 100%;
}
.CodeMirror {
max-width: 100%;
display: flex;
flex: 1;
font-family: var(--token-typography-font-stack-code);
}
.rose-autosize {
.CodeMirror {
height: auto;
}
}
.CodeMirror-scroll {
width: 100%;
}
.CodeMirror-sizer {
border-right-width: 0 !important;
}
// Toolbar
.rose-code-editor-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--ui-gray-subtler-3);
height: 2.75rem;
&__action {
margin-left: sizing.rems(s);
}
&__right-aligned {
display: flex;
height: 100%;
align-items: center;
}
&__menu {
margin-right: sizing.rems(s);
.pds-dropdownTrigger {
box-shadow: none;
&__layoutWrapper {
padding-left: sizing.rems(s);
padding-right: sizing.rems(s);
}
}
&-divider {
border-right: 1px solid var(--ui-gray-subtler-3);
height: sizing.rems(xl);
}
}
}
@@ -12,7 +12,7 @@
'header header header'
'navigation navigation navigation'
'body body body';
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(3, auto);
grid-template-rows: auto 1fr;
.rose-layout-page-header {
-5
View File
@@ -12,11 +12,6 @@ module.exports = {
included(app) {
this._super.included.apply(this, arguments);
this.import('node_modules/codemirror/lib/codemirror.css');
this.import('node_modules/codemirror/theme/monokai.css');
this.import('node_modules/codemirror/addon/lint/lint.css');
this.import('node_modules/jsonlint/lib/jsonlint.js');
this.includeHDSStyles(app);
this.includeFlightIcons(app);
this.includePublic(app);
+7 -9
View File
@@ -1,9 +1,9 @@
{
"name": "rose",
"version": "0.0.0",
"description": "Styles and components for Boundary.",
"description": "Styles and components for Threshold.",
"license": "BUSL-1.1",
"author": "HashiCorp",
"author": "CyberCove LLC (fork of HashiCorp Boundary UI)",
"keywords": [
"ember-addon"
],
@@ -34,16 +34,14 @@
"@hashicorp/design-system-tokens": "^2.3.0",
"@hashicorp/flight-icons": "^3.10.0",
"@nullvoxpopuli/ember-composable-helpers": "^5.2.10",
"codemirror": "5.65.7",
"ember-auto-import": "^2.10.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-babel": "^8.3.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-cli-sass": "^11.0.1",
"ember-focus-trap": "^1.0.1",
"ember-named-blocks-polyfill": "^0.2.5",
"jsonlint": "^1.6.3",
"sass": "^1.69.5",
"uuid": "^11.0.3"
"uuid": "^14.0.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.25.1",
@@ -54,7 +52,7 @@
"@glimmer/component": "^2.0.0",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^9.1.0",
"concurrently": "^10.0.3",
"doctoc": "^2.2.0",
"ember-cli": "~5.12.0",
"ember-cli-clean-css": "^3.0.0",
@@ -84,7 +82,7 @@
"@eslint/js": "^9.18.0",
"globals": "^15.14.0",
"loader.js": "^4.7.0",
"postcss": "^8.4.31",
"postcss": "^8.5.10",
"prettier": "^3.3.3",
"qunit": "^2.22.0",
"qunit-dom": "^3.2.1",
@@ -98,7 +96,7 @@
"ember-source": ">= 4.0.0"
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"ember": {
"edition": "octane"
@@ -1,32 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/code-editor', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
const codeValue = 'export HCP_CLIENT_ID = {place your client_id here}';
this.set('codeValue', codeValue);
await render(hbs`
<Rose::CodeEditor
@codeValue={{this.codeValue}}
as |c|
>
<c.toolbar />
<c.fieldEditor />
</Rose::CodeEditor>
`);
assert.dom('[data-test-code-editor]').isVisible();
assert.dom('[data-test-code-editor-toolbar]').isVisible();
assert.dom('[data-test-code-editor-field-editor]').isVisible();
assert.dom('[data-test-code-editor-field-editor]').includesText(codeValue);
});
});
@@ -1,74 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { typeIn, render, waitUntil } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module(
'Integration | Component | rose/code-editor/field-editor',
function (hooks) {
setupRenderingTest(hooks);
const editorSelector = '[data-test-code-editor-field-editor]';
test('it renders', async function (assert) {
const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`;
this.set('codeValue', codeValue);
await render(hbs`
<Rose::CodeEditor::FieldEditor @value={{this.codeValue}} />
`);
assert.dom(editorSelector).isVisible();
assert.dom(editorSelector).includesText(codeValue);
const myNewCode = 'NEW CODE';
await typeIn(`${editorSelector} textarea`, myNewCode);
assert.dom(editorSelector).includesText(myNewCode);
});
test('it renders readonly', async function (assert) {
const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`;
this.set('codeValue', codeValue);
this.set('options', {
readOnly: true,
});
await render(hbs`
<Rose::CodeEditor::FieldEditor @value={{this.codeValue}} @options={{this.options}} />
`);
assert.dom(editorSelector).isVisible();
assert.dom(editorSelector).includesText(codeValue);
const myNewCode = 'NEW CODE';
waitUntil(async () => {
try {
// This should fail if the textarea is readonly
await typeIn(`${editorSelector} textarea`, myNewCode);
} catch {
return true;
}
});
assert.dom(editorSelector).doesNotIncludeText(myNewCode);
});
test('it calls onInput on change', async function (assert) {
const codeValue = `export HCP_CLIENT_ID = {place your client_id here}`;
const onInput = () => {
this.set('called', true);
};
this.set('codeValue', codeValue);
this.set('onInput', onInput);
await render(hbs`
<Rose::CodeEditor::FieldEditor @value={{this.codeValue}} @onInput={{this.onInput}} />
`);
assert.dom(editorSelector).isVisible();
assert.dom(editorSelector).includesText(codeValue);
const myNewCode = 'NEW CODE';
await typeIn(`${editorSelector} textarea`, myNewCode);
assert.dom(editorSelector).includesText(myNewCode);
assert.true(this.called);
});
},
);
@@ -1,65 +0,0 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import sinon from 'sinon';
module('Integration | Component | rose/code-editor/toolbar', function (hooks) {
setupRenderingTest(hooks);
const toolbarSelector = '[data-test-code-editor-toolbar]';
const copyButtonSelector = '[data-test-code-editor-toolbar-copy-button]';
const menuDividerSelector = '[data-test-code-editor-toolbar-menu-divider]';
hooks.beforeEach(function () {
const copyText = 'copyText';
this.set('copyText', copyText);
});
test('it renders', async function (assert) {
await render(hbs`
<Rose::CodeEditor::Toolbar @copyText={{this.copyText}} />
`);
assert.dom(toolbarSelector).isVisible();
assert.dom(copyButtonSelector).isVisible();
assert.dom(menuDividerSelector).doesNotExist();
});
test('it displays action', async function (assert) {
await render(hbs`
<Rose::CodeEditor::Toolbar @onCopy={{this.copyText}}>
<:action>
<div data-test-my-action>Hello</div>
</:action>
</Rose::CodeEditor::Toolbar>
`);
assert.dom(toolbarSelector).isVisible();
assert.dom(copyButtonSelector).isVisible();
assert.dom(menuDividerSelector).doesNotExist();
assert.dom('[data-test-my-action]').isVisible();
});
test('it calls onCopy callback', async function (assert) {
// `writeText` could fail if the document is not in focus which is common during test runs
sinon.stub(window.navigator.clipboard, 'writeText').resolves();
const onCopy = () => {
this.set('called', true);
};
this.set('onCopy', onCopy);
await render(hbs`
<Rose::CodeEditor::Toolbar @copyText={{this.copyText}} @onCopy={{this.onCopy}} />
`);
assert.dom(toolbarSelector).isVisible();
assert.dom(copyButtonSelector).isVisible();
assert.dom(menuDividerSelector).doesNotExist();
await click(copyButtonSelector);
assert.true(this.called);
sinon.restore();
});
});
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

+2 -24
View File
@@ -97,31 +97,8 @@ If you would like to do it through the command line:
```bash
doormat login
source <(doormat aws export --account boundary_team_acctest_dev)
# Feel free to use a custom name for your key pair if you don't want to use $USER, just make sure it follows the <name>_enos format so it can be identified. e.g. zed_enos
ssh-keygen -N '' -t ed25519 -f ~/.ssh/"$USER"_enos
aws ec2 import-key-pair \
--region us-east-1 \
--key-name "$USER"_enos \
--public-key-material fileb://~/.ssh/"$USER"_enos.pub
```
### Setup Enos:
Make sure you're in the correct boundary repository for the version of Boundary you plan on using.
The enos scenarios for CE need to be run in `boundary` and the enos scenarios for enterprise need to be run in `boundary-enterprise`.
Enos needs some configuration variables to run the scenario successfully. [See the configuration file](https://github.com/hashicorp/boundary/blob/main/enos/enos.vars.hcl). The file has comments per each variable, but some awareness:
- `boundary_edition`: The edition of Boundary you are using. Options are `oss` or `enterprise`.
- `aws_region`: The AWS region you are using. Very important as mentioned within the EC2 Setup.
- `aws_ssh_keypair_name`: The name of the AWS keypair.
- `aws_ssh_private_key_path`: The path to the private key associated with your keypair.
- `enos_user`: The user name to use for tagged resources in AWS.
- `e2e_debug_no_run`: Make sure this is set to true.
- `boundary_license_path` (enterprise only): Path to an enterprise license
More documentation about [scenario variables](https://github.com/hashicorp/boundary/tree/main/enos#scenarios-variables).
## Run tests:
Make sure you followed all the steps within the [Getting started section](#getting-started).
@@ -157,6 +134,7 @@ Using Terminal 1:
- `cd boundary/enos` or `cd boundary_enterprise/enos`.
- `doormat login`. Login with Doormat.
- `eval "$(doormat aws export --account boundary_team_acctest_dev)"`. Exporting AWS env variables from doormat to your terminal.
- `export ENOS_VAR_boundary_license={LICENSE_KEY}`. If running enterprise, you will need a license key
- `enos scenario launch e2e_ui_aws builder:local protocol:https` or `enos scenario launch e2e_ui_aws_ent builder:local protocol:https` if in enterprise.
- Launches enos scenario, this will take from 5 to 10 minutes. When its done, you will see a Enos Operations finished! within your terminal. Check out more scenarios [here](https://github.com/hashicorp/boundary/tree/main/enos).
- `bash scripts/test_e2e_env.sh`. Prints all the env variables within Enos scenario. Copy the output and paste it within your Terminal 2 (Boundary UI). These env variables are needed within Boundary UI to run the test against the enos scenario.
@@ -324,7 +302,7 @@ If working on an E2E test that is working against a different version of the bou
# in the following example $COMMITISH can be replaced with a branch name, commit, or tag
OPENAPI_SWAGGER_URL_OR_FILE=https://raw.githubusercontent.com/hashicorp/boundary/$COMMITISH/internal/gen/controller.swagger.json pnpm generate:api-client
# using a local absolute or relative path
# using a local absolute or relative path
OPENAPI_SWAGGER_URL_OR_FILE=../../boundary-enterprise/internal/gen/controller.swagger.json pnpm generate:api-client
```
+13 -4
View File
@@ -64,12 +64,21 @@ export class RolesPage extends BaseResourcePage {
*/
async addGrantsToRole(grants) {
await this.page.getByRole('link', { name: 'Grants', exact: true }).click();
await this.page.getByRole('textbox', { name: 'New Grant' }).fill(grants);
await this.page.getByRole('button', { name: 'Add', exact: true }).click();
await this.page
.getByRole('link', { name: 'Edit Grants', exact: true })
.click();
await this.page.locator('.hds-code-editor').getByRole('textbox').click();
await expect(
this.page.locator('.hds-code-editor').getByRole('textbox'),
).toBeFocused();
await this.page
.locator('.hds-code-editor')
.getByRole('textbox')
.fill(grants);
await this.page.getByRole('button', { name: 'Save', exact: true }).click();
await this.dismissSuccessAlert();
await expect(
this.page.getByRole('textbox', { name: 'Grant', exact: true }),
).toHaveValue(grants);
this.page.locator('.grant-codeblock .hds-code-block__code'),
).toHaveText(grants);
}
}
+4
View File
@@ -98,6 +98,10 @@ export class StorageBucketsPage extends BaseResourcePage {
await this.page.getByLabel('Region').fill(region);
await this.page.getByLabel('Access key ID').fill(accessKeyId);
await this.page.getByLabel('Secret access key').fill(secretAccessKey);
await this.page
.getByLabel('Worker filter')
.first()
.scrollIntoViewIfNeeded();
await this.page
.getByLabel('Worker filter')
.getByRole('textbox')
+9 -3
View File
@@ -58,7 +58,7 @@ export class TargetsPage extends BaseResourcePage {
if (alias) {
await this.page
.getByRole('group', { name: 'Aliases' })
.getByLabel('value')
.getByLabel('Aliases')
.last()
.fill(alias);
await this.page.getByRole('button', { name: 'Add' }).click();
@@ -145,7 +145,10 @@ export class TargetsPage extends BaseResourcePage {
.getByText('Edit Ingress Worker Filter'),
).toBeVisible();
await this.page.locator('.CodeMirror').getByRole('textbox').fill(filter);
await this.page
.locator('.hds-code-editor')
.getByRole('textbox')
.fill(filter);
await this.page.getByRole('button', { name: 'Save', exact: true }).click();
await this.dismissSuccessAlert();
@@ -174,7 +177,10 @@ export class TargetsPage extends BaseResourcePage {
.getByText('Edit Egress Worker Filter'),
).toBeVisible();
await this.page.locator('.CodeMirror').getByRole('textbox').fill(filter);
await this.page
.locator('.hds-code-editor')
.getByRole('textbox')
.fill(filter);
await this.page.getByRole('button', { name: 'Save', exact: true }).click();
await this.dismissSuccessAlert();
@@ -174,8 +174,8 @@ test(
// Log in using ldap account
await loginPage.logout(adminLoginName);
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: orgName }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: orgName }).click();
await page.getByRole('link', { name: ldapAuthMethodName }).click();
await loginPage.login(ldapUserName, ldapUserPassword);
await expect(
@@ -193,6 +193,9 @@ test(
// View the LDAP account and verify account attributes
await page.getByRole('link', { name: orgName }).click();
await expect(
page.getByRole('heading', { name: 'Projects' }),
).toBeVisible();
await page
.getByRole('navigation', { name: 'Primary' })
.getByRole('link', { name: 'Auth Methods' })
@@ -129,8 +129,8 @@ test(
// Log in using oidc account
await loginPage.logout(adminLoginName);
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: orgName }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: orgName }).click();
await page.getByRole('link', { name: oidcAuthMethodName }).click();
const pagePromise = context.waitForEvent('page');
await page.getByRole('button', { name: 'Sign In' }).click();
@@ -58,8 +58,8 @@ test(
// Log in using new account
await loginPage.logout(adminLoginName);
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: orgName }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: orgName }).click();
await page.getByRole('link', { name: authMethodName }).click();
await loginPage.login(username, password);
@@ -97,8 +97,8 @@ test(
// Log in using new password
await loginPage.logout(adminLoginName);
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: orgName }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: orgName }).click();
await page.getByRole('link', { name: authMethodName }).click();
await loginPage.login(username, newPassword);
await expect(
@@ -52,8 +52,8 @@ test(
// Log in using new account
await page.reload();
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: org.name }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: org.name }).click();
await page.getByRole('link', { name: authMethod.name }).click();
const loginPage = new LoginPage(page);
await loginPage.login(username, password);
@@ -79,8 +79,8 @@ test(
// Confirm user cannot log in with old password
await loginPage.logout(username);
await page.getByText('Choose a different scope').click();
await page.getByRole('link', { name: org.name }).click();
await page.getByRole('button', { name: /global/i }).click();
await page.getByRole('option', { name: org.name }).click();
await page.getByRole('link', { name: authMethod.name }).click();
await page.getByLabel('Login Name').fill(username);
await page.getByLabel('Password', { exact: true }).fill(password);
@@ -195,7 +195,9 @@ test(
await page.getByLabel('Description').fill('This is an automated test');
await page.getByRole('combobox', { name: 'Type' }).click();
await page.getByRole('option', { name: 'JSON' }).click();
await page.getByText('{}').click();
await page.locator('.hds-code-editor').getByRole('textbox').click();
// Move cursor to middle of '{}' to avoid typing outside of the braces
await page.keyboard.press('ArrowLeft');
const testName = 'name-json';
const testPassword = 'password-json';
const testId = 'id-json';
+6 -6
View File
@@ -52,6 +52,9 @@ test(
.getByRole('navigation', { name: 'Primary' })
.getByRole('link', { name: 'Host Catalogs' })
.click();
await expect(
page.getByRole('heading', { name: 'Host Catalogs' }),
).toBeVisible();
await page.getByRole('link', { name: hostCatalogName }).click();
const hostSetName2 = await hostCatalogsPage.createHostSet();
@@ -233,19 +236,16 @@ test(
).toBeVisible();
await expect(page.getByText('"dev" in "/tags/type"')).toBeVisible();
await page
.locator('.CodeMirror')
.getByRole('textbox')
.click({ force: true });
await page.locator('.hds-code-editor').getByRole('textbox').click();
await expect(
page.locator('.CodeMirror').getByRole('textbox'),
page.locator('.hds-code-editor').getByRole('textbox'),
).toBeFocused();
const selectAllShortcut =
process.platform === 'darwin' ? 'Meta+A' : 'Control+A';
await page.keyboard.press(selectAllShortcut);
await page.keyboard.press('Backspace');
await page
.locator('.CodeMirror')
.locator('.hds-code-editor')
.getByRole('textbox')
.fill('"prod" in "/tags/type"');
await page.getByRole('button', { name: 'Save' }).click();
@@ -133,6 +133,8 @@ export const electronTest = test.extend({
electronPage: async ({ electronApp }, use) => {
const page = await electronApp.firstWindow();
await page.evaluate(() => localStorage.clear());
// Reset the cluster URL stored by electron
await page.evaluate(() => window.desktop.cluster.resetClusterUrl());
await page.context().tracing.start({ screenshots: true, snapshots: true });
await use(page);
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import * as boundaryCli from '../helpers/boundary-cli.js';
import baseGlobalSetup from '../global-setup.js';
export default async function globalSetup() {
await baseGlobalSetup();
// when running locally, there's a chance that the cache could be running an
// older version.
// stop the cache if it's running, and the desktop client will automatically
// start the cache after logging in .
await boundaryCli.checkBoundaryCli();
await boundaryCli.stopCache();
}
+1 -1
View File
@@ -6,7 +6,7 @@ import { defineConfig } from '@playwright/test';
/** @type {import('@playwright/test').PlaywrightTestConfig} */
export default defineConfig({
globalSetup: '../global-setup',
globalSetup: './global-setup',
outputDir: './artifacts',
workers: 1, // Tests need to be run in serial, otherwise there may be conflicts when using the CLI
use: {
+1
View File
@@ -4,6 +4,7 @@
*/
import { chromium, test as baseTest, mergeTests } from '@playwright/test';
import { checkEnv } from './helpers/general.js';
import { boundaryApiClientTest } from './helpers/boundary-api-client.js';
import { LoginPage } from './admin/pages/login.js';
+1
View File
@@ -9,6 +9,7 @@ export * from './boundary-cli/accounts.js';
export * from './boundary-cli/aliases.js';
export * from './boundary-cli/auth-methods.js';
export * from './boundary-cli/authenticate.js';
export * from './boundary-cli/cache.js';
export * from './boundary-cli/connect.js';
export * from './boundary-cli/credential-stores.js';
export * from './boundary-cli/credentials.js';
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import { execSync } from 'node:child_process';
/**
* Stops the boundary cache used by the desktop client
*/
export async function stopCache() {
try {
execSync('boundary cache stop');
} catch (e) {
console.log(`${e.stderr}`);
}
}
+6 -5
View File
@@ -2,7 +2,7 @@
"name": "e2e-tests",
"version": "0.0.0",
"description": "End to end tests for boundary UI",
"author": "HashiCorp",
"author": "CyberCove LLC (fork of HashiCorp Boundary UI)",
"license": "BUSL-1.1",
"private": true,
"type": "module",
@@ -25,15 +25,16 @@
"admin:dev:firefox": "pnpm run admin --project=firefox --headed",
"admin:dev:safari": "pnpm run admin --project=webkit --headed",
"desktop": "pnpm run --filter desktop build && pnpm run desktop:dev",
"desktop:local": "SETUP_CLI=true pnpm run desktop",
"desktop:dev": "pnpm playwright test --config desktop/playwright.config.js --reporter=html"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/eslint-parser": "^7.21.3",
"@eslint/js": "^9.18.0",
"@openapitools/openapi-generator-cli": "^2.30.0",
"@playwright/test": "^1.56.1",
"concurrently": "^9.1.0",
"@openapitools/openapi-generator-cli": "^2.38.0",
"@playwright/test": "^1.60.0",
"concurrently": "^10.0.3",
"dotenv": "^16.4.5",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.1.8",
@@ -44,7 +45,7 @@
"tesseract.js": "^5.1.1"
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"lint-staged": {
"*.js": [
+46 -11
View File
@@ -1,8 +1,8 @@
{
"private": true,
"description": "Monorepo for Boundary UIs and addons.",
"description": "Monorepo for Threshold UIs and addons.",
"license": "BUSL-1.1",
"author": "HashiCorp",
"author": "CyberCove LLC (fork of HashiCorp Boundary UI)",
"scripts": {
"build": "pnpm build:ui:admin",
"build:ui:admin": "pnpm run --filter admin build",
@@ -34,27 +34,62 @@
"prepare": "husky"
},
"devDependencies": {
"concurrently": "^9.1.0",
"concurrently": "^10.0.3",
"doctoc": "^2.2.0",
"esbuild": "^0.25.4",
"git-cz": "^4.9.0",
"husky": "^9.1.6",
"license-checker": "^25.0.1",
"lint-staged": "^15.2.10",
"esbuild": "^0.25.4"
"lint-staged": "^15.5.2"
},
"pnpm": {
"overrides": {
"nomnom>underscore": "^1.12.1",
"micromatch": "^4.0.8",
"css-select>nth-check": "^2.0.1",
"node-gyp": "^10.0.0",
"ansi-html": "^0.0.9",
"fireworm>async": "^2.6.4",
"ember-cli-babel>@babel/runtime": "7.27.6",
"ember-try-config>package-json": "^10.0.1",
"ember-loading>ember-concurrency": "^4.0.0",
"tmp": "^0.2.5",
"tar": "^7.5.10"
"tmp": "^0.2.7",
"tar": "^7.5.22",
"nomnom>underscore": "^1.13.8",
"ember-cli>markdown-it": "^14.3.2",
"diff@>=5.0.0 <6.0.0": "^5.2.2",
"qs@>=6.0.0 <7.0.0": "^6.16.0",
"follow-redirects@>=1.0.0 <2.0.0": "^1.16.1",
"postcss@>=8.0.0 <9.0.0": "^8.5.29",
"ws@>=8.0.0 <9.0.0": "^8.22.0",
"brace-expansion@>=5.0.0 <6.0.0": "^5.0.12",
"brace-expansion@>=2.0.0 <3.0.0": "^2.1.7",
"brace-expansion@>=1.0.0 <2.0.0": "^1.1.21",
"form-data@>=4.0.0 <5.0.0": "^4.0.6",
"js-yaml@>=3.0.0 <4.0.0": "^3.15.2",
"js-yaml@>=4.0.0 <5.0.0": "^4.3.2",
"morgan@>=1.0.0 <2.0.0": "^1.12.1",
"@babel/core@>=7.0.0 <8.0.0": "^7.29.7",
"engine.io@>=6.0.0 <7.0.0": "^6.6.11",
"shell-quote@>=1.0.0 <2.0.0": "^1.12.0",
"body-parser@>=1.0.0 <2.0.0": "^1.20.8",
"linkify-it@>=5.0.0 <6.0.0": "^5.0.2",
"seroval@>=1.0.0 <2.0.0": "^1.6.9",
"socket.io-parser@>=4.0.0 <5.0.0": "^4.2.7",
"ip-address@>=10.0.0 <11.0.0": "^10.7.4",
"axios@>=1.0.0 <2.0.0": "^1.20.0",
"nanoid@>=5.0.0 <6.0.0": "^5.1.16",
"nanoid@>=3.0.0 <4.0.0": "^3.3.20",
"postcss-selector-parser@>=7.0.0 <8.0.0": "^7.1.6",
"browserslist@>=4.0.0 <5.0.0": "^4.29.3",
"immutable@>=5.0.0 <6.0.0": "^5.1.9",
"colord@>=2.0.0 <3.0.0": "^2.10.0",
"baseline-browser-mapping@>=2.0.0 <3.0.0": "^2.11.28",
"source-map-js@>=1.0.0 <2.0.0": "^1.2.2",
"proxy-addr@>=2.0.0 <3.0.0": "^2.0.8",
"compression@>=1.0.0 <2.0.0": "^1.8.2",
"handlebars@>=4.0.0 <5.0.0": "^4.7.10",
"websocket-driver@>=0.7.0 <0.8.0": "^0.7.5",
"@humanfs/node@>=0.16.0 <0.17.0": "^0.16.8",
"@xmldom/xmldom@>=0.8.0 <0.9.0": "^0.8.15"
},
"ignoredBuiltDependencies": [
"@parcel/watcher",
@@ -75,7 +110,7 @@
}
},
"engines": {
"node": "20.* || 22.*"
"node": "22.* || 24.*"
},
"packageManager": "pnpm@10.8.1"
"packageManager": "pnpm@10.33.3"
}
+2182 -1738
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
# package.json Overrides
This file documents the rationale for each dependency override defined in `package.json` under `pnpm.overrides`.
These overrides force specific versions of transitive dependencies, primarily for security and compatibility reasons.
| Package | Version | Pulled in by | What to check before removing |
|---------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `micromatch` | `^4.0.8` | `anymatch` (`^3.1.4`), `common-tags` (`3.1.10`), `sane` (`^3.1.4`), `find-yarn-workspace-root` / `findup-sync` (`^4.0.2`), `ember-template-lint` / `stylelint` (`^4.0.5`) all declare ranges that would resolve below `4.0.8`. | Once all dependents declare a range of `^4.0.8` or higher, the override can be removed. |
| `css-select>nth-check` | `^2.0.1` | `css-select` pins an older `nth-check`. | Check whether a newer `css-select` release drops the pinned `nth-check` `<2.0.0` requirement. Used by `ember-inline-svg`. |
| `ansi-html` | `^0.0.9` | Both `broccoli-middleware` and `broccoli` pin `"ansi-html": "^0.0.7"`. | Check whether newer `broccoli` / `ember-cli` releases update these to drop the sub-`0.0.9` pin. |
| `fireworm>async` | `^2.6.4` | `fireworm` pins an older `async`. | Check whether `fireworm` has been updated or replaced; if `testem` updates `fireworm`, this override can be removed. |
| `ember-cli-babel>@babel/runtime` | `7.27.6` | `ember-cli-babel` restricts the version it resolves. | Review when upgrading `ember-cli-babel`; once its `@babel/runtime` range is broad enough, remove and verify the build. |
| `ember-try-config>package-json` | `^10.0.1` | `ember-try-config` pins an older `package-json`. | Check whether a newer `ember-try-config` release widens its `package-json` range to include v10+. |
| `ember-loading>ember-concurrency` | `^4.0.0` | `ember-loading` declares `"ember-concurrency": "^2.0.3"`. | The override and the patch (`patches/ember-loading.patch`) are a pair — the override forces v4 to be installed, the patch updates the source to use v4. Both must be removed together if `ember-loading` ever ships a version that natively supports v4. |
| `tmp` | `^0.2.7` | 10+ packages pin old versions of `tmp` (`broccoli`, `can-symlink`, `ember-cli-typescript`, `external-editor`, `fixturify-project`, `sane`, `testem`, and others) | Check whether the main Ember toolchain packages (`broccoli`, `ember-cli`, `testem`) have been updated to allow `tmp ^0.2.x`. |
| `tar` | `^7.5.22` | Both `cacache` (`^6.1.11`) and `@electron/rebuild` (`^6.0.5`) pin old ranges. | Check whether a newer `ember-electron` brings in versions of `@electron/rebuild` and `cacache` that allow `tar ^7.x`. |
| `nomnom>underscore` | `^1.13.8` | `nomnom` pins `"underscore": "~1.6.0"`. All other consumers already declare ranges that include 1.13.8. | `nomnom@1.8.1` is the last published version and the package is abandoned. `jsonlint` is the actual consumer, currently pinned to `^1.6.3` which still depends on `nomnom`. |
| `ember-cli>markdown-it` | `^14.3.2` | `ember-cli` (`~5.12.0`) pins old ranges. | Once `ember-cli` has been updated to at least `6.3.0` then this override can be removed. |
## Security overrides added by the Threshold fork
The overrides keyed `<name>@>=X.0.0 <Y.0.0` were generated from `pnpm audit` (see the "About this fork" section in the README). Each one only raises a transitive dependency to the latest patched release **within the same major line** (patch/minor, lockfile-only effect). They are build/test tooling dependencies (`ember-cli`, `testem`, `stylelint`, `openapi-generator`, ...), not shipped code. Remove an entry once the dependents release versions that no longer need it; re-run `pnpm audit` after removing.
Packages covered: `@babel/core`, `@humanfs/node`, `@xmldom/xmldom`, `axios`, `baseline-browser-mapping`, `body-parser`, `brace-expansion`, `browserslist`, `colord`, `compression`, `diff`, `engine.io`, `follow-redirects`, `form-data`, `handlebars`, `immutable`, `ip-address`, `js-yaml`, `linkify-it`, `morgan`, `nanoid`, `postcss`, `postcss-selector-parser`, `proxy-addr`, `qs`, `seroval`, `shell-quote`, `socket.io-parser`, `source-map-js`, `websocket-driver`, `ws`.
Known unfixed without a major bump or an upstream fix: `svgo@1` (via `ember-inline-svg`), `@faker-js/faker@8` (api addon mirage data), `uuid@8` (testem), `diff@7` (sinon), `postcss-selector-parser@6` (stylelint), `basic-ftp@5` (openapi-generator), `extract-zip@2`, `http-cache-semantics@4`, `braces@3`, `sprintf-js@1` (no patched release).
+20 -9
View File
@@ -90,10 +90,10 @@ export default function transformer(file, api) {
} else {
// if an array is provided and it's argument is a literal (number, boolean, string) then this is used for the test name
// otherwise if it's an object the index in the array is what is used for the test name
eachTestNames = testArguments[1].elements.map((element, i) => element.type.endsWith('Literal') ? element.value : i);
eachTestNames = testArguments[1].elements?.map((element, i) => element.type.endsWith('Literal') ? element.value : i);
}
testName = eachTestNames.map(eachTestName => `${testName} [${eachTestName}]`);
testName = eachTestNames?.map(eachTestName => `${testName} [${eachTestName}]`) ?? [];
} else {
testName = [testName];
}
@@ -270,16 +270,27 @@ export default function transformer(file, api) {
}
};
const existingEmberA11yImport = f.find(j.ImportDeclaration, {
source: {
value: 'ember-a11y-testing/test-support',
},
});
tests.forEach(testHandler);
testsEach.forEach(testHandler);
const code = f.toSource();
// the codemod will insert a newline between existing import statements,
// this regex removes the extra new line
//
return code.replace(
/(?<newline>\n)(import .*ember-a11y-testing\/test-support.*)/g,
'$2',
);
if (existingEmberA11yImport.length > 0) {
return code;
} else {
// the codemod will insert a newline between existing import statements,
// this regex removes the extra new line
return code.replace(
/(?<newline>\n)(import .*ember-a11y-testing\/test-support.*)/g,
'$2',
);
}
}
+2 -2
View File
@@ -24,11 +24,11 @@ module.exports = {
'at-rule-no-vendor-prefix': null,
// As a team we want to stick to using kebab-case as our selector class pattern, but there are a couple
// of exceptions in which we overrride CSS from addons like CodeMirror and HDS, in which they use
// of exceptions in which we overrride CSS from addons like HDS, in which they use
// pascal case and a double-kebab-case. This regex checks for all of those 3 patterns and won't fail
// lint check, but will fail if its any other pattern.
'selector-class-pattern': [
'(^([a-z][a-z0-9]*)(-{1,2}[a-z0-9]+)*$)|(^CodeMirror)',
'(^([a-z][a-z0-9]*)(-{1,2}[a-z0-9]+)*$)',
{
message: (selector) =>
`Expected class selector "${selector}" to be kebab-case`,
@@ -82,37 +82,30 @@
{{/if}}
</Hds::Form::TextInput::Field>
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
<Form::Field::KeyValue
@name='certificates'
@model={{@model}}
@properties={{array 'value'}}
@data={{@model.certificates}}
@legend={{t 'resources.auth-method.form.certificates.label'}}
@helperText={{t 'resources.auth-method.form.certificates.help'}}
@disabled={{form.disabled}}
@isOptional={{true}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.auth-method.form.certificates.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.auth-method.form.certificates.help'}}
</F.HelperText>
{{#if @model.errors.certificates}}
<F.Error as |E|>
{{#each @model.errors.certificates as |error|}}
<E.Message>{{error.message}}</E.Message>
{{/each}}
</F.Error>
<:row as |R|>
<R.Field as |F|>
<F.Textarea
aria-label={{t 'resources.auth-method.form.certificates.label'}}
data-test-value-input
@value={{R.rowData.value}}
{{on 'input' (fn R.updateRow 'value')}}
/>
</R.Field>
{{#if R.canDelete}}
<R.DeleteRowButton data-test-delete-button @onClick={{R.removeRow}} />
{{/if}}
</:fieldset>
<:field as |F|>
<F.Textarea
@name='certificates'
@options={{@model.certificates}}
@model={{@model}}
/>
</:field>
</Form::Field::ListWrapper>
</:row>
</Form::Field::KeyValue>
{{#if @model.isNew}}
<Hds::Form::TextInput::Field
@@ -406,45 +399,46 @@
}}</F.HelperText>
</Hds::Form::TextInput::Field>
<Form::Field::ListWrapper
@layout='horizontal'
@isOptional={{true}}
<Form::field::KeyValue
@name='account_attribute_maps'
@model={{@model}}
@properties={{array 'key' 'value'}}
@data={{@model.account_attribute_maps}}
@legend={{t 'resources.auth-method.form.account_attribute_maps.label'}}
@helperText={{t 'resources.auth-method.form.account_attribute_maps.help'}}
@disabled={{form.disabled}}
@isOptional={{true}}
>
<:fieldset as |F|>
<F.Legend>
{{t 'resources.auth-method.form.account_attribute_maps.label'}}
</F.Legend>
<F.HelperText>
{{t 'resources.auth-method.form.account_attribute_maps.help'}}
</F.HelperText>
{{#if @model.errors.account_attribute_maps}}
<F.Error as |E|>
{{#each @model.errors.account_attribute_maps as |error|}}
<E.Message>{{error.message}}</E.Message>
<:row as |R|>
<R.Field as |F|>
<F.Label>{{t 'resources.auth-method.titles.from-attr'}}</F.Label>
<F.TextInput
data-test-key-input
@value={{R.rowData.key}}
{{on 'input' (fn R.updateRow 'key')}}
/>
</R.Field>
<R.Field as |F|>
<F.Label>{{t 'resources.auth-method.titles.to-attr'}}</F.Label>
<F.Select
data-test-value-select
{{on 'change' (fn R.updateRow 'value')}}
>
<option disabled hidden selected value=''>
{{t 'titles.choose-an-option'}}
</option>
{{#each this.toAttributes as |attr|}}
<option value={{attr}} selected={{eq R.rowData.value attr}}>
{{attr}}
</option>
{{/each}}
</F.Error>
</F.Select>
</R.Field>
{{#if R.canDelete}}
<R.DeleteRowButton data-test-delete-button @onClick={{R.removeRow}} />
{{/if}}
</:fieldset>
<:field as |F|>
<F.KeyValue
@name='account_attribute_maps'
@options={{@model.account_attribute_maps}}
@model={{@model}}
@keyLabel={{t 'resources.auth-method.titles.from-attr'}}
@valueLabel={{t 'resources.auth-method.titles.to-attr'}}
@width='100%'
>
<:key as |K|>
<K.text />
</:key>
<:value as |V|>
<V.select @selectOptions={{this.toAttributes}} />
</:value>
</F.KeyValue>
</:field>
</Form::Field::ListWrapper>
</:row>
</Form::field::KeyValue>
<div class='ldap-section'>
<Hds::Text::Display
Loaded 100 of 301 files, more files were not shown because too many files have changed in this diff. Show more