Compare commits

...
235 Commits
Author SHA1 Message Date
Susmitha Girumala ca56c37fb0 Merge pull request #690 from hashicorp/bump-desktop-cli-version
chore: Bump cli version used by desktop
2021-08-05 15:20:51 -04:00
Susmitha Girumala 5e2d9e9e2b chore: Bump cli version used by desktop 2021-08-05 15:08:08 -04:00
Susmitha Girumala d0ca5cc041 Merge pull request #685 from hashicorp/use-external-version-file-for-desktop-cli
chore: Refactor cli version into a VERSION file
2021-08-03 14:40:06 -04:00
Susmitha Girumala 310539e2de Merge branch 'main' into use-external-version-file-for-desktop-cli 2021-08-03 14:12:30 -04:00
Susmitha Girumala 9da86f8411 Merge pull request #684 from hashicorp/bump-desktop-changelog
docs: Bump desktop changelog for next release
2021-08-03 14:12:21 -04:00
Susmitha Girumala 76e721bd91 chore: Check for http error status codes including 400 2021-08-03 13:16:10 -04:00
Susmitha Girumala a19aa2ff0c chore: Refactor cli version into it's own file
In addition, fail cli setup step for desktop build when cli cannot be
downloaded and extracted successfully
2021-08-03 13:11:21 -04:00
Susmitha Girumala cdecf570e2 docs: Bump desktop changelog for next release 2021-08-02 18:02:59 -04:00
Susmitha Girumala c04c8f4f0a Merge pull request #683 from hashicorp/enable-credentials-brokering-feature
chore: Enable credential brokering feature in admin
2021-07-29 17:09:16 -04:00
Susmitha Girumala 8fb33b8ff5 chore: Enable credential brokering feature in admin 2021-07-29 15:14:28 -04:00
Susmitha Girumala e89f31a93b Merge pull request #682 from hashicorp/label-update-for-target-associations
refactor: Rename target host sets and credential libraries
2021-07-29 15:13:51 -04:00
Susmitha Girumala 518bf19318 Merge branch 'main' into label-update-for-target-associations 2021-07-29 14:37:02 -04:00
Susmitha Girumala d35823fae6 Merge pull request #681 from hashicorp/target-credential-source-association-tests
test: Add target association tests for credential libraries
2021-07-29 14:36:52 -04:00
Susmitha Girumala 0231dabbe6 refactor: Rename target host sets and credential libraries
Rename to Target Host Sources and Credential Sources
2021-07-29 14:17:58 -04:00
Susmitha Girumala 263d2b8d95 Merge branch 'main' into target-credential-source-association-tests 2021-07-29 13:53:18 -04:00
Susmitha Girumala 98537f708c Merge pull request #680 from hashicorp/fix-doc-naming
fix: credential library naming does not change
2021-07-29 13:53:05 -04:00
Susmitha Girumala 5596d55035 Merge branch 'main' into fix-doc-naming 2021-07-29 13:00:48 -04:00
Susmitha Girumala a8f1780731 test: Add target association tests for credential libraries 2021-07-29 12:59:54 -04:00
Randall Morey 8e6988c179 formatting 2021-07-29 12:21:03 -04:00
Susmitha Girumala ea293d947f Merge pull request #663 from hashicorp/refactor-delete-jquery
refactor: 💡 Delete jQuery and refactor handler
2021-07-29 11:18:02 -04:00
Randall Morey d23afb1c71 fix: credential library naming does not change 2021-07-29 10:34:33 -04:00
Carlos Alcaide Corvo b8ec749d03 Merge branch 'main' into refactor-delete-jquery 2021-07-29 07:25:46 -07:00
Susmitha Girumala 0c5c9387b5 Merge pull request #676 from hashicorp/test-credential-library-create-errors
Test credential library create errors
2021-07-29 10:19:43 -04:00
Susmitha Girumala bbbd5cea65 Merge branch 'main' into test-credential-library-create-errors 2021-07-29 09:34:07 -04:00
Susmitha Girumala 66888a4c95 Merge pull request #675 from hashicorp/credential-store-attributes
Add Credential Store Attributes
2021-07-29 09:33:48 -04:00
Susmitha Girumala ca35246a7b Merge branch 'main' into credential-store-attributes 2021-07-29 09:01:34 -04:00
Susmitha Girumala 16fe712f4e Merge pull request #678 from hashicorp/doc-links
docs: ✏️ add doc links for cred stores and sources
2021-07-29 09:01:16 -04:00
Randall Morey 296b5fbecd docs: ✏️ add doc links for cred stores and sources 2021-07-28 16:27:40 -04:00
Carlos Alcaide 4240bbee56 test: 💍 Change assertion
Change assertion to avoid false positive
2021-07-28 10:51:57 -07:00
Susmitha Girumala c3a9ecb0ec Merge remote-tracking branch 'origin' into credential-store-attributes 2021-07-28 13:22:57 -04:00
Susmitha Girumala e40ff1f63c fix: Protect credentialstore attrs hmac values in serializati 2021-07-28 13:22:47 -04:00
Carlos Alcaide Corvo 87479b8e3c Merge branch 'main' into test-credential-library-create-errors 2021-07-28 10:17:14 -07:00
Carlos Alcaide 264f81a6f3 refactor: 💡 Prevent opening unknown
Prevent opening unknown links
2021-07-28 10:16:37 -07:00
Carlos Alcaide Corvo 772c8b38c0 Merge branch 'main' into refactor-delete-jquery 2021-07-28 10:08:24 -07:00
Carlos Alcaide Corvo 03aa4f32f1 Merge pull request #677 from hashicorp/chore-update-sass
chore: 🤖 Update Sass version
2021-07-28 07:57:12 -07:00
Carlos Alcaide Corvo 0b145fac47 Merge branch 'main' into chore-update-sass 2021-07-28 07:46:38 -07:00
Susmitha Girumala 9bd7193833 Merge pull request #674 from hashicorp/add-type-field-to-credential-library
fix: Add type field to Credential Library
2021-07-28 10:20:19 -04:00
Carlos Alcaide f3a4017e5f chore: 🤖 Update Sass version 2021-07-27 15:35:51 -07:00
Carlos Alcaide Corvo 43eca819bf Merge branch 'main' into refactor-delete-jquery 2021-07-27 14:55:02 -07:00
Carlos Alcaide Corvo 9aa3f8f5ad Merge branch 'main' into test-credential-library-create-errors 2021-07-27 14:32:59 -07:00
Carlos Alcaide 8cceb4090f refactor: 💡 Change save crdential library behaviour
Change save credential library behaviour to not redirect after saving a
change
2021-07-27 14:27:13 -07:00
Carlos Alcaide dc7ebec668 test: 💍 Add creation error test
Add credential library creation error tests
2021-07-27 14:15:59 -07:00
Susmitha Girumala cffa419dd6 Merge branch 'main' into add-type-field-to-credential-library 2021-07-27 14:45:15 -04:00
Susmitha Girumala 83a938fada Merge pull request #673 from hashicorp/remove-credential-library-from-a-target
feat: Remove a Credential Library Added to a Target
2021-07-27 14:45:04 -04:00
Susmitha Girumala 61c80518de feat: Add credential store attributes to it's form 2021-07-27 14:32:32 -04:00
Susmitha Girumala d1119efd11 Merge branch 'main' into remove-credential-library-from-a-target 2021-07-27 12:31:31 -04:00
Susmitha Girumala a9cb6f35f9 Merge pull request #671 from hashicorp/credential-list-test
test: Credential Library List Acceptance Tests
2021-07-27 12:30:02 -04:00
Dharini Jeeva 6f85419675 Merge pull request #672 from hashicorp/del-test
test: Credential library deletion error test
2021-07-27 09:11:03 -07:00
Susmitha Girumala 0cb90353f0 fix: Add type field to credential library 2021-07-27 11:52:02 -04:00
dharini cd9f05962c fix: 🐛 spelling, merge conflicts 2021-07-27 08:31:25 -07:00
Susmitha Girumala 9d342cf517 feat: Remove a credential library associated to a target 2021-07-27 11:22:06 -04:00
dharini 88fc063ab9 Merge branch 'main' of github.com:hashicorp/boundary-ui 2021-07-27 08:21:50 -07:00
Susmitha Girumala d3e707d740 Merge pull request #670 from hashicorp/test-credential-library-create
test: 💍 Add credential library creation acceptance test
2021-07-27 10:40:44 -04:00
Susmitha Girumala fc4af516a2 Merge branch 'main' into test-credential-library-create 2021-07-27 10:26:47 -04:00
Susmitha Girumala edb8836a2c Merge pull request #667 from hashicorp/add-credential-libraries-to-a-target
Add Credential Libraries to a Target
2021-07-27 10:26:08 -04:00
Susmitha Girumala b29f877e35 Merge branch 'main' into add-credential-libraries-to-a-target 2021-07-27 10:15:54 -04:00
Susmitha Girumala e299740951 Merge pull request #668 from hashicorp/credentials-store-icon-update
style: Update Credentials Store Icon
2021-07-27 10:15:47 -04:00
Susmitha Girumala 03fbc19c2c Merge remote-tracking branch 'origin/main' into add-credential-libraries-to-a-target 2021-07-27 10:04:04 -04:00
Susmitha Girumala e42647e6a2 fix: Add missing empty credential libraries message 2021-07-27 10:03:56 -04:00
Susmitha Girumala a03a3811d4 Merge branch 'main' into credentials-store-icon-update 2021-07-27 09:58:55 -04:00
Susmitha Girumala d4ef4bc58c Merge pull request #666 from hashicorp/target-credential-libraries-list
List Credential Libraries in a Target
2021-07-27 09:53:45 -04:00
Susmitha Girumala 3d7166d059 Merge branch 'main' into target-credential-libraries-list 2021-07-27 09:22:46 -04:00
dharini 025ae1bfe2 test: 💍 Credential library deletion error test 2021-07-26 16:58:23 -07:00
dharini 0bc4cd6e21 test: 💍 add cred list tests 2021-07-26 16:43:28 -07:00
Carlos Alcaide 4a6555317a test: 💍 Add credential library creation acceptance test
Add credential library creation acceptance test
2021-07-26 15:37:36 -07:00
Dharini Jeeva be7e3bb56e Merge pull request #669 from hashicorp/del-action
credential library - delete
2021-07-26 14:08:12 -07:00
Dharini Jeeva ed0c774714 Merge branch 'main' into del-action 2021-07-26 13:49:01 -07:00
Susmitha Girumala 2bdb008f65 Merge branch 'main' into target-credential-libraries-list 2021-07-26 16:46:38 -04:00
Susmitha Girumala 8109d7789c Merge pull request #665 from hashicorp/chore-update-ember-fetch
chore: 🤖 Update ember-fetch
2021-07-26 16:46:21 -04:00
Carlos Alcaide Corvo 3eb9f07fe0 Merge branch 'main' into refactor-delete-jquery 2021-07-26 13:09:35 -07:00
Carlos Alcaide a07cd9e1a1 refactor: 💡 Allow external links just for specific domain
Allow external links just for boundary.io domain. So far the only urls
we want to open externally are our own documentation
2021-07-26 13:09:08 -07:00
dharini bbbb1620f6 Merge branch 'main' into del-action 2021-07-26 12:43:03 -07:00
dharini 1d71e3bec8 feat: 🎸 delete action and tests
✅ Closes: #1996
2021-07-26 12:37:37 -07:00
Susmitha Girumala 63d302d716 style: Update credentials store icon 2021-07-26 14:41:33 -04:00
Susmitha Girumala 341baa76fc Merge branch 'main' into chore-update-ember-fetch 2021-07-26 14:37:56 -04:00
Susmitha Girumala cc6f0aa1b4 Merge pull request #664 from hashicorp/chore-update-ember-cli-app-version
chore: 🤖 Update ember-cli-app version dependency
2021-07-26 14:37:49 -04:00
Susmitha Girumala 1fb98f8ed3 Merge branch 'target-credential-libraries-list' into add-credential-libraries-to-a-target 2021-07-25 10:27:40 -04:00
Susmitha Girumala 08543e9868 Merge remote-tracking branch 'origin/main' into add-credential-libraries-to-a-target 2021-07-25 10:25:13 -04:00
Susmitha Girumala 0fbcf8e16e feat: Show message when no cred-libs are available in target 2021-07-25 10:25:09 -04:00
Susmitha Girumala 7ff569779c Merge branch 'main' into target-credential-libraries-list 2021-07-25 10:24:26 -04:00
Susmitha Girumala 8f2deadc49 feat: Add credential libraries association form in target 2021-07-25 10:11:11 -04:00
Susmitha Girumala 89b7d0345a fix: Add feature flag to target credential libraries list 2021-07-25 09:26:03 -04:00
dharini ad3b9ed684 add success message 2021-07-23 15:41:14 -07:00
dharini a7a84d64b9 feat: 🎸 add delete cred library action 2021-07-23 15:33:29 -07:00
Carlos Alcaide 7fd89bb946 chore: 🤖 Update ember-fetch 2021-07-23 15:15:24 -07:00
Carlos Alcaide dbfc21ace3 chore: 🤖 Update ember-cli-app-version dependency 2021-07-23 15:00:07 -07:00
Carlos Alcaide Corvo 7bd8d87ad2 Merge branch 'main' into refactor-delete-jquery 2021-07-23 14:42:03 -07:00
Carlos Alcaide f5b80f93d2 refactor: 💡 Delete jQuery and refactor handler
Delete jQuery from desktop client and refactor the handler using a
native electron way.
2021-07-23 14:38:26 -07:00
Susmitha Girumala 1e5d52410e refactor: Cleanup credential library parameter name 2021-07-23 17:34:31 -04:00
Susmitha Girumala 0dedbfb411 docs: Fix comment for listing target credential libraries 2021-07-23 17:04:39 -04:00
Susmitha Girumala 992df6b34b Merge pull request #662 from hashicorp/feat-credential-stores-empty-list
feat: 🎸 Add credential stores empty list message
2021-07-23 16:34:06 -04:00
Susmitha Girumala af7e539f3d feat: List credential libraries for a target 2021-07-23 16:33:45 -04:00
Carlos Alcaide Corvo 83f9ab5c61 Merge branch 'main' into feat-credential-stores-empty-list 2021-07-23 13:19:48 -07:00
Carlos Alcaide Corvo a9c06638c7 Merge pull request #659 from hashicorp/chore-update-eslint-config-prettier
chore: 🤖 Update eslint-config-prettier dependency
2021-07-23 13:19:40 -07:00
Carlos Alcaide Corvo 0637d805d3 Merge branch 'main' into chore-update-eslint-config-prettier 2021-07-23 12:57:57 -07:00
Susmitha Girumala f089200dc0 Merge branch 'main' into feat-credential-stores-empty-list 2021-07-23 15:50:34 -04:00
Susmitha Girumala 2726538c41 Merge pull request #661 from hashicorp/credential-library-read
Credential Library - Read
2021-07-23 15:50:17 -04:00
Susmitha Girumala b6291bd3c7 Merge branch 'main' into credential-library-read 2021-07-23 15:11:30 -04:00
Susmitha Girumala 0e89ecabdc Merge pull request #657 from hashicorp/chore-update-eslint
chore: 🤖 Update eslint dependency
2021-07-23 15:09:40 -04:00
Susmitha Girumala debf6fbe06 Merge branch 'main' into credential-library-read 2021-07-23 14:59:49 -04:00
Carlos Alcaide Corvo 517ba08f55 Merge branch 'main' into feat-credential-stores-empty-list 2021-07-23 11:56:09 -07:00
Carlos Alcaide 7f13365336 feat: 🎸 Add credential stores empty list message
Add credential stores empty list message with link to create a new one.
2021-07-23 11:28:11 -07:00
Susmitha Girumala 8ebdd995e3 Merge branch 'main' into chore-update-eslint 2021-07-23 13:19:09 -04:00
Susmitha Girumala e73cd828a5 Merge pull request #654 from hashicorp/target-credential-library-api-modeling
API: Target Credential Library api modeling
2021-07-23 13:18:26 -04:00
Susmitha Girumala d2a42f7468 Merge branch 'main' into target-credential-library-api-modeling 2021-07-23 13:08:19 -04:00
Susmitha Girumala b1614dc65d Merge pull request #647 from hashicorp/credential-library-create
Credential Library - Create
2021-07-23 13:08:04 -04:00
Susmitha Girumala 9931b8d3ca Merge branch 'credential-library-create' into credential-library-read 2021-07-23 09:30:21 -04:00
Susmitha Girumala b7e5477b3d fix: Shorten credential library POST form area 2021-07-23 09:29:42 -04:00
Susmitha Girumala 027371567b fix: Disable credential library view in non-edit mode 2021-07-22 18:02:29 -04:00
Susmitha Girumala 95612186d8 feat: View credential library details 2021-07-22 17:52:28 -04:00
Susmitha Girumala f7dd5ebe55 Merge branch 'main' into credential-library-create 2021-07-22 17:44:49 -04:00
Susmitha Girumala a6be658aa5 fix: Add error handling to credential-library creation form 2021-07-22 17:42:37 -04:00
Dharini Jeeva 123476a3a8 Merge pull request #658 from hashicorp/fieldset
fieldset usage
2021-07-22 14:00:28 -07:00
Carlos Alcaide Corvo d44528fc2c Merge branch 'main' into chore-update-eslint-config-prettier 2021-07-22 13:53:27 -07:00
Carlos Alcaide Corvo b2c85b9805 Merge branch 'main' into chore-update-eslint 2021-07-22 13:53:24 -07:00
dharini 03ce6be3ee fix: 🐛 cleanup 2021-07-22 13:44:02 -07:00
Susmitha Girumala a35c4e0ffd fix: Surface fieldset as a form component 2021-07-22 16:26:05 -04:00
Dharini Jeeva 710bcccfd5 Merge pull request #660 from hashicorp/update-font-weight
fix: 🐛 update fieldset font weight
2021-07-22 13:00:43 -07:00
dharini 8b9d945efb fix: 🐛 use named block 2021-07-22 10:24:56 -07:00
dharini aa2114062b fix: 🐛 update to semi bold to match labels 2021-07-22 10:13:33 -07:00
Carlos Alcaide 90055ea142 chore: 🤖 Update eslint-config-prettier dependency 2021-07-22 09:58:49 -07:00
dharini e1aa0c6bcb fix: 🐛 space 2021-07-22 09:45:45 -07:00
dharini af337626f5 feat: 🎸 add fieldset 2021-07-22 09:37:41 -07:00
Carlos Alcaide dad2d456d0 chore: 🤖 Update eslint dependency 2021-07-22 08:50:54 -07:00
Susmitha Girumala 9dac6fc446 Merge branch 'main' into target-credential-library-api-modeling 2021-07-22 11:38:13 -04:00
Carlos Alcaide Corvo 00348e4c55 Merge pull request #652 from hashicorp/feature-credential-store-crud-delete
feat: 🎸 Add credential store delete CRUD operation
2021-07-22 06:21:00 -07:00
Susmitha Girumala b0dd9bf803 Merge branch 'main' into credential-library-create 2021-07-21 18:19:53 -04:00
Carlos Alcaide 2e23bbea3e Fix typo 2021-07-21 15:09:21 -07:00
Carlos Alcaide Corvo 7e7bad050f Merge branch 'main' into feature-credential-store-crud-delete 2021-07-21 14:34:36 -07:00
Carlos Alcaide afadb1d83a Comment dropdown option to create a new library store until that route is available 2021-07-21 14:31:10 -07:00
Dharini Jeeva 52c9fb5570 Merge pull request #653 from hashicorp/add-align-attr
feat: 🎸 add inline alignment support to checkbox and radio
2021-07-21 14:20:49 -07:00
dharini 2dd3e13cd3 fix: 🐛 lint issues 2021-07-21 14:07:29 -07:00
Carlos Alcaide 74fa0876fb fix: 🐛 Fix translation misplacement 2021-07-21 14:01:57 -07:00
dharini c531553c07 Merge branch 'add-align-attr' of https://github.com/hashicorp/boundary-ui into add-align-attr 2021-07-21 13:33:35 -07:00
dharini 0ad6d027e6 fix: 🐛 space 2021-07-21 13:33:03 -07:00
Carlos Alcaide 2eb7084643 test: 💍 Adding acceptance test for delete CRUD
Add acceptance test for delete CRUD operation in credential stores
2021-07-21 13:31:48 -07:00
Dharini Jeeva 06d162ae89 Merge branch 'main' into add-align-attr 2021-07-21 13:04:52 -07:00
dharini c43ced0380 feat: 🎸 add toggle functionality 2021-07-21 13:01:59 -07:00
Susmitha Girumala 28a4a7304e feat: Add credential library add and remove methods to target 2021-07-21 15:46:21 -04:00
Susmitha Girumala e74fd0202e feat: API: Load credential libraries associated to a target 2021-07-21 15:46:21 -04:00
dharini 0823c1cb2d feat: 🎸 add stories for inline radio & checkbox, radiogrp 2021-07-21 12:39:24 -07:00
dharini d034631e4d fix: 🐛 add attr in storybook 2021-07-21 10:51:33 -07:00
Carlos Alcaide c5af15f4c1 Add missing translation after rebase 2021-07-21 10:51:00 -07:00
Carlos Alcaide fce79d46d7 feat: 🎸 Add credential store delete CRUD operation 2021-07-21 10:51:00 -07:00
Carlos Alcaide Corvo c45357bf25 Merge pull request #650 from hashicorp/test-acceptance-credential-stores
Test acceptance credential stores
2021-07-21 10:49:58 -07:00
Carlos Alcaide d96d7425f8 Fix skip test with false positive 2021-07-21 10:36:16 -07:00
Carlos Alcaide Corvo 2000c10006 Merge branch 'main' into test-acceptance-credential-stores 2021-07-21 07:59:52 -07:00
Susmitha Girumala a348e4830a Merge pull request #646 from hashicorp/credential-library-list
feat: List credential libraries in a credential store
2021-07-21 08:20:51 -04:00
dharini 7c496b5b90 fix: 🐛 undo unnecessary change 2021-07-20 16:26:13 -07:00
dharini 5d6da1e52d feat: 🎸 add inline aligntment support to checkbox and radio 2021-07-20 16:11:57 -07:00
Carlos Alcaide Corvo 2eb1d01622 Merge branch 'main' into test-acceptance-credential-stores 2021-07-20 14:07:37 -07:00
Susmitha Girumala f012d10dcb Merge branch 'main' into credential-library-list 2021-07-20 17:03:08 -04:00
Carlos Alcaide Corvo a96689e2cc Merge pull request #651 from hashicorp/fix-hidden-secret-component
fix: 🐛 Fix style and accessibility issues for hidden secret
2021-07-20 13:55:49 -07:00
Carlos Alcaide c5f72c53d8 Move translation to actions file 2021-07-20 13:43:11 -07:00
Carlos Alcaide 699d5063d9 fix: 🐛 Fix style and accessibility issues for hidden secret
Fix style and accessibility issues for hidden secret component
2021-07-20 13:28:39 -07:00
Susmitha Girumala 08deda59aa Merge branch 'main' into credential-library-list 2021-07-20 13:38:38 -04:00
Carlos Alcaide Corvo f8cce58d49 Merge branch 'main' into test-acceptance-credential-stores 2021-07-20 10:37:56 -07:00
Carlos Alcaide f69477e556 test: 💍 Add credential stores create and edit tests
Add credential store create and edit acceptance tests
2021-07-20 10:36:34 -07:00
Dharini Jeeva 7f5a792af4 Merge pull request #649 from hashicorp/form-fieldset-style
style: 💄 update rose fieldset font-size
2021-07-20 08:48:03 -07:00
Susmitha Girumala dd96423ead fix: Disable credential library request body in form 2021-07-20 09:32:41 -04:00
dharini a1117529e5 style: 💄 update rose fieldset font-size 2021-07-19 15:45:58 -07:00
Carlos Alcaide 44510ea8d4 test: 💍 Adding integration test for credential stores
Adding more integration test cases for credential stores
2021-07-19 14:17:24 -07:00
Carlos Alcaide 9c93ff6c13 test: 💍 Add test cases to credential stores route
Add new test cases to credential stores route
2021-07-19 14:17:24 -07:00
Susmitha Girumala 59818fa24d feat: Add credential libraries breadcrumbs 2021-07-19 13:13:35 -04:00
Susmitha Girumala 98e14a642f fix: Treat credential library attributes as an object 2021-07-19 12:05:40 -04:00
Susmitha Girumala 45fc58fe75 feat: Init credential library creation 2021-07-18 14:31:45 -04:00
Susmitha Girumala 6dad1cd276 Merge branch 'main' into credential-library-list 2021-07-18 14:31:12 -04:00
Susmitha Girumala 2212c7dd9e feat: List credential libraries in a credential store 2021-07-18 12:34:33 -04:00
Susmitha Girumala 98bc86497a Merge pull request #645 from hashicorp/chore-upgrade-ember-cli-inject-live-reload
chore: 🤖 Upgrade ember-cli-inject-live-reload
2021-07-18 12:29:26 -04:00
Susmitha Girumala f447fb11ef Merge branch 'main' into chore-upgrade-ember-cli-inject-live-reload 2021-07-18 11:34:53 -04:00
Susmitha Girumala e091730ffb Merge pull request #644 from hashicorp/chore-upgrade-ember-cli
chore: 🤖 Upgrade ember-cli version
2021-07-18 11:05:45 -04:00
Susmitha Girumala 3e479fc02a Merge branch 'main' into chore-upgrade-ember-cli 2021-07-16 14:01:12 -04:00
Carlos Alcaide Corvo ce2be6b4b3 Merge pull request #643 from hashicorp/feature-credential-store-crud-new
Feature credential store crud new
2021-07-16 11:00:20 -07:00
Carlos Alcaide 816b50eb1b feat: 🎸 Fix creation type issue
Fix credential store creation type issue
2021-07-16 10:41:04 -07:00
Carlos Alcaide 9067558082 Add type to credential store creation 2021-07-16 10:28:31 -07:00
Carlos Alcaide d6129a7dfc Fix lint issues 2021-07-16 09:41:41 -07:00
Carlos Alcaide 3b4bb24eb7 feat: 🎸 Add credential store
Add screen to create new credential store
2021-07-16 09:24:22 -07:00
Carlos Alcaide 2217482329 feat: 🎸 Adding credential store edit form
Add credential-store mirage config. Add edit credential store form. Add
credential store route. Add credential store template.
2021-07-16 09:23:43 -07:00
Carlos Alcaide Corvo 32bdf2acdc Merge pull request #642 from hashicorp/feature-credential-store-crud-detail
Edit credential store
2021-07-16 09:19:52 -07:00
Carlos Alcaide 03187e2e2e Fix typo in comment 2021-07-16 09:07:53 -07:00
Carlos Alcaide cf72af4004 Add comments to route functions 2021-07-16 09:05:36 -07:00
Carlos Alcaide e4a0150ee7 Delete watchman cookie file 2021-07-16 08:53:37 -07:00
Carlos Alcaide 1a46a6b183 Add .watchman-cookie to gitignore 2021-07-16 08:52:18 -07:00
Carlos Alcaide cf377ad3dc Delete model check 2021-07-16 08:37:57 -07:00
Carlos Alcaide c82a8be1ed feat: 🎸 Stop using model on template
Stop using model on template, so no more updating template while editing
the form. Add link to documentation
2021-07-16 08:37:57 -07:00
Carlos Alcaide b9c5ed0de3 Add cancel action when editing a credential-store. Add breadcrumb feature on the credential store controller. 2021-07-16 08:37:57 -07:00
Carlos Alcaide 885ce5ebf5 feat: 🎸 Adding credential store edit form
Add credential-store mirage config. Add edit credential store form. Add
credential store route. Add credential store template.
2021-07-16 08:37:57 -07:00
Susmitha Girumala 9f7d528582 Merge pull request #641 from hashicorp/credential-library-api
Credential Library Modeling
2021-07-16 11:20:36 -04:00
Carlos Alcaide 8544d61e9c chore: 🤖 Upgrade ember-cli-inject-live-reload
Upgrade ember-cli-inject-live-reload dependency.
2021-07-15 13:24:59 -07:00
Carlos Alcaide a1b3e5382b chore: 🤖 Upgrade ember-cli version
Upgrade ember-cli version
2021-07-15 13:08:48 -07:00
Susmitha Girumala 1fb41665e5 feat: Mock credential library in api addon 2021-07-12 16:03:06 -04:00
Susmitha Girumala bd50c4f78c feat: Add credential library api 2021-07-12 14:50:53 -04:00
Susmitha Girumala 0f73cd5faf Merge pull request #639 from hashicorp/feature-credential-store-crud-read
Add first iteration of list credential store
2021-07-12 14:49:48 -04:00
Carlos Alcaide Corvo 96de8f262f Merge branch 'main' into feature-credential-store-crud-read 2021-07-12 11:27:16 -07:00
Susmitha Girumala 4a871134a5 Merge pull request #637 from hashicorp/feature-toggle-secret
feat: 🎸 Add toggle secret feature
2021-07-12 14:27:09 -04:00
Carlos Alcaide 9bcb402fa2 feat: 🎸 Refactor details of the component
Delete unnecessary <span>. Make icon smaller and aligned. Fix linting
issues.
2021-07-12 09:46:56 -07:00
Carlos Alcaide Corvo bb1580ab6e Merge branch 'main' into feature-credential-store-crud-read 2021-07-12 07:46:24 -07:00
Carlos Alcaide 75f45a134d feat: 🎸 Add more features to the credential stores list
Add breadcrumb functionality. Add detail route. Add acceptance test to
load the page. Add some styling improvements

✅ Closes: \
2021-07-12 07:32:35 -07:00
Carlos Alcaide Corvo b272f85e68 Merge branch 'main' into feature-toggle-secret 2021-07-12 06:44:33 -07:00
Susmitha Girumala fdb46df9f2 Merge pull request #640 from hashicorp/update-font-color
fix: Update text font color and weight
2021-07-09 16:17:34 -04:00
dharini f39ac156cb nest styles to header cell 2021-07-09 11:59:02 -07:00
Carlos Alcaide 986cde5941 feat: 🎸 Add first iteration for list credential store
First iteration adding credential store list crud operations
2021-07-09 10:39:42 -07:00
dharini c94f54c90f undo file change 2021-07-09 09:15:22 -07:00
dharini 4c648ad4be fix: font color and weight 2021-07-08 18:01:30 -07:00
Carlos Alcaide cd10c30ca8 Delete not used translation 2021-07-08 08:36:09 -07:00
Carlos Alcaide Corvo d964411f70 Merge pull request #638 from hashicorp/add-credential-feature-flag
feat: Enable credential feature flag in dev mode for admin
2021-07-08 08:03:55 -07:00
Susmitha Girumala 9d68eb495e feat: Enable credential feature flag in dev mode for admin 2021-07-08 09:01:56 -04:00
Susmitha Girumala ef80dd87c1 Merge pull request #636 from hashicorp/feature-credentials-modeling
feat: 🎸 initial credential-store modeling and mocking
2021-07-08 08:18:40 -04:00
Carlos Alcaide a59e012f52 Fix faker issue 2021-07-07 15:13:34 -07:00
Carlos Alcaide 580a2f4159 Add lint changes 2021-07-07 13:09:47 -07:00
Carlos Alcaide b0d999cf3c feat: 🎸 Add toggle secret feature
Add toggle secret feature to the hidde secret component
2021-07-07 12:39:44 -07:00
Susmitha Girumala 97b82818cf Merge branch 'main' into feature-credentials-modeling 2021-07-06 16:07:26 -04:00
Carlos Alcaide Corvo 4f0f8fce26 Merge pull request #631 from hashicorp/chore-update-ember-test-helpers
chore: 🤖 Update ember test-helpers dependency
2021-07-06 11:34:58 -07:00
Susmitha Girumala 2b7563ee7b Merge branch 'main' into chore-update-ember-test-helpers 2021-07-06 14:18:00 -04:00
Carlos Alcaide Corvo d8f43531e1 Merge pull request #630 from hashicorp/chore-upgrade-storybook
chore: 🤖 Update Storybook version
2021-07-06 11:16:50 -07:00
Susmitha Girumala 0404fe308f Merge branch 'main' into chore-upgrade-storybook 2021-07-06 14:03:04 -04:00
Susmitha Girumala d3bdab9014 Merge pull request #629 from hashicorp/chore-resolutions-mem
chore: 🤖 Add mem to resolutions
2021-07-06 14:02:55 -04:00
Carlos Alcaide Corvo 4b41c8dc7a Merge branch 'main' into chore-upgrade-storybook 2021-07-06 10:46:46 -07:00
Carlos Alcaide Corvo 12913e70ce Merge branch 'main' into chore-resolutions-mem 2021-07-06 10:46:33 -07:00
Carlos Alcaide Corvo 7630f454fd Merge pull request #628 from hashicorp/chore-resolutions-minimist
chore: 🤖 Update resolutions for minimist
2021-07-06 10:46:01 -07:00
Carlos Alcaide Corvo 9a2e5d23b9 Merge branch 'main' into chore-resolutions-minimist 2021-07-06 10:29:04 -07:00
Susmitha Girumala a0f371bfc5 Merge pull request #627 from hashicorp/chore-resolutions-prismjs
chore: 🤖 Update prismjs version in resolutions
2021-07-06 13:09:49 -04:00
Carlos Alcaide Corvo 9100fe9a34 Merge branch 'main' into chore-resolutions-prismjs 2021-07-06 08:37:19 -07:00
Carlos Alcaide Corvo 87b76a91c4 Merge pull request #626 from hashicorp/chore-resolutions-normalize-url
chore: 🤖 Add normalize-url to resolutions
2021-07-06 08:37:11 -07:00
Carlos Alcaide Corvo aa4a487e97 Merge branch 'main' into chore-resolutions-normalize-url 2021-07-06 08:14:20 -07:00
Susmitha Girumala 94cfca561c Merge pull request #613 from hashicorp/chore-update-resolution-glob-parent
chore: 🤖 Update resolutions for glob-parent
2021-07-06 11:10:51 -04:00
Susmitha Girumala 58f46e9334 Merge branch 'main' into chore-update-resolution-glob-parent 2021-07-06 10:58:21 -04:00
Randall Morey 5519cf8557 feat: 🎸 initial credential-store modeling and mocking 2021-07-01 14:28:53 -04:00
Carlos Alcaide 371b89b5ad chore: 🤖 Update ember test-helpers dependency
Update ember test-helpers dependency
2021-06-28 17:39:50 -07:00
Carlos Alcaide 07d2667d14 chore: 🤖 Update Storybook version
Update Storybook to the latest version
2021-06-28 17:22:40 -07:00
Carlos Alcaide 39d19cab06 chore: 🤖 Add mem to resolutions
Add mem to resolutions to avoid a security vulnerability
2021-06-28 17:02:25 -07:00
Carlos Alcaide d7659bd9de chore: 🤖 Update resolutions for minimist
Add minimist to resolutions to avoid a security vulnerability
2021-06-28 16:45:02 -07:00
Carlos Alcaide e855697601 chore: 🤖 Update prismjs version in resolutions
Update prismjs version in resolutions to avoid a security vulnerability
2021-06-28 15:52:00 -07:00
Carlos Alcaide e5c4ac650e chore: 🤖 Add normalize-url to resolutions
Add normalize-url to resolutions due to security vulnerability
2021-06-28 15:27:31 -07:00
Carlos Alcaide 688b6652d1 chore: 🤖 Update resolutions for glob-parent
Update resolutions field in package json to avoid security vulnerability
with glob-parent older versions.
2021-06-28 14:04:42 -07:00
153 changed files with 4759 additions and 504 deletions

No files matched your search

+1
View File
@@ -22,6 +22,7 @@ testem.log
yarn-error.log
storybook-static
**/.storybook/*.html
.watchman-cookie*
# ember-try
.node_modules.ember-try
@@ -0,0 +1,47 @@
import BaseModel from '../../models/base';
import { attr } from '@ember-data/model';
/**
* A credential library is a resource that can generate and retrieve
* credentials.
*/
export default class GeneratedCredentialLibraryModel extends BaseModel {
// =attributes
@attr('string', {
description: 'The type of the resource, to help differentiate schemas',
})
type;
@attr('string', {
description: 'The owning credential store ID.',
})
credential_store_id;
@attr('string', {
description: 'Optional name for identification purposes',
})
name;
@attr('string', {
description: 'Optional user-set description for identification purposes',
})
description;
@attr('date', {
description: 'The time this resource was created\nOutput only.',
readOnly: true,
})
created_time;
@attr('date', {
description: 'The time this resource was last updated\nOutput only.',
readOnly: true,
})
updated_time;
@attr('number', {
description: 'Current version number of this resource.',
})
version;
}
@@ -0,0 +1,42 @@
import BaseModel from '../../models/base';
import { attr } from '@ember-data/model';
/**
* A credential store is a resource that can retrieve, store, and potentially
* generate credentials.
*/
export default class GeneratedCredentialStoreModel extends BaseModel {
// =attributes
@attr('string', {
description: 'The type of the resource, to help differentiate schemas',
})
type;
@attr('string', {
description: 'Optional name for identification purposes',
})
name;
@attr('string', {
description: 'Optional user-set description for identification purposes',
})
description;
@attr('date', {
description: 'The time this resource was created\nOutput only.',
readOnly: true,
})
created_time;
@attr('date', {
description: 'The time this resource was last updated\nOutput only.',
readOnly: true,
})
updated_time;
@attr('number', {
description: 'Current version number of this resource.',
})
version;
}
@@ -0,0 +1,30 @@
import GeneratedCredentialLibraryModel from '../generated/models/credential-library';
import { attr } from '@ember-data/model';
import { fragment } from 'ember-data-model-fragments/attributes';
/**
* Enum options for credential library.
*/
export const options = {
http_method: ['GET', 'POST'],
};
export default class CredentialLibraryModel extends GeneratedCredentialLibraryModel {
// =error attributes
// These attributes exist solely to capture errors on nested fields.
// See the application adapter's error normalization method for
// more information.
@attr('string', { readOnly: true }) attributes_http_method;
@attr('string', { readOnly: true }) attributes_http_request_body;
@attr('string', { readOnly: true }) attributes_path;
// =attributes
/**
* Attributes of this resource, if any, represented as a JSON fragment.
* @type {FragmentCredentialLibraryAttributesModel}
*/
@fragment('fragment-credential-library-attributes', { defaultValue: {} })
attributes;
}
@@ -0,0 +1,34 @@
import GeneratedCredentialStoreModel from '../generated/models/credential-store';
import { attr } from '@ember-data/model';
import { fragment } from 'ember-data-model-fragments/attributes';
import { equal } from '@ember/object/computed';
export default class CredentialStoreModel extends GeneratedCredentialStoreModel {
// =error attributes
// These attributes exist solely to capture errors on nested fields.
// See the application adapter's error normalization method for
// more information.
@attr('string', { readOnly: true }) attributes_address;
@attr('string', { readOnly: true }) attributes_namespace;
@attr('string', { readOnly: true }) attributes_ca_cert;
@attr('string', { readOnly: true }) attributes_tls_server_name;
@attr('string', { readOnly: true }) attributes_tls_skip_verify;
@attr('string', { readOnly: true }) attributes_token;
@attr('string', { readOnly: true }) attributes_client_certificate;
@attr('string', { readOnly: true }) attributes_client_certificate_key;
// =attributes
/**
* Attributes of this resource, if any, represented as a JSON fragment.
* @type {FragmentAuthMethodAttributesModel}
*/
@fragment('fragment-credential-store-attributes', { defaultValue: {} })
attributes;
/**
* @type {boolean}
*/
@equal('type', 'vault') isVault;
}
@@ -0,0 +1,25 @@
import Fragment from 'ember-data-model-fragments/fragment';
import { attr } from '@ember-data/model';
import { options } from './credential-library';
export default class FragmentCredentialLibraryAttributesModel extends Fragment {
//= attributes (vault)
@attr('string', {
description:
'The HTTP method the library uses when requesting credentials from Vault.',
defaultValue: () => options.http_method[0],
})
http_method;
@attr('string', {
description:
'The body of the HTTP request the library sends to Vault when requesting credentials. Only valid if `http_method` is set to `POST`.',
})
http_request_body;
@attr('string', {
description: 'The path in Vault to request credentials from.',
})
path;
}
@@ -0,0 +1,72 @@
import Fragment from 'ember-data-model-fragments/fragment';
import { attr } from '@ember-data/model';
export default class FragmentCredentialStoreAttributesModel extends Fragment {
// =attributes (vault)
@attr('string', {
for: 'vault',
description:
"The address to Vault server. This should be a complete URL such as 'https://127.0.0.1:8200'",
})
address;
@attr('string', {
for: 'vault',
description: 'The namespace within Vault to use.',
})
namespace;
@attr('string', {
for: 'vault',
description:
"A PEM-encoded CA certificate to verify the Vault server's TLS certificate.",
})
ca_cert;
@attr('string', {
for: 'vault',
description:
'Name to use as the SNI host when connecting to Vault via TLS.',
})
tls_server_name;
@attr('boolean', {
for: 'vault',
description: 'Whether or not to skip TLS verification.',
})
tls_skip_verify;
@attr('string', {
for: 'vault',
description: 'A token used for accessing Vault.',
})
token;
@attr('string', {
for: 'vault',
description: 'The Vault token hmac.',
readOnly: true,
})
token_hmac;
@attr('string', {
for: 'vault',
description:
'A PEM-encoded client certificate to use for TLS authentication to the Vault server.',
})
client_certificate;
@attr('string', {
for: 'vault',
description:
"A PEM-encoded private key matching the client certificate from 'client_certificate'.",
})
client_certificate_key;
@attr('string', {
for: 'vault',
description: 'The Vault client certificate key hmac.',
readOnly: true,
})
client_certificate_key_hmac;
}
+90
View File
@@ -20,6 +20,16 @@ export default class TargetModel extends GeneratedTargetModel {
})
host_sets;
/**
* Credential library ids are read only and can be
* persisted via a dedicated call to `addCredentialLibraries()`.
*/
@fragmentArray('fragment-string', {
readOnly: true,
emptyArrayIfMissing: true,
})
application_credential_library_ids;
/**
* An array of resolved host set and host catalog instances. Model instances
* must already be loaded into the store (this method will not load unloaded
@@ -36,6 +46,24 @@ export default class TargetModel extends GeneratedTargetModel {
.filter((hostSetRef) => hostSetRef.model !== null);
}
/**
* An array of resolved credential library instances. Model instances
* must already be loaded into the store (this method will not load unloaded
* instances). Unresolvable instances are excluded from the array.
* @type {[CredentialLibraryModel]}
*/
@computed('application_credential_library_ids.[]', 'store')
get credentialLibraries() {
return this.application_credential_library_ids
.map((credential_library_fragment) =>
this.store.peekRecord(
'credential-library',
credential_library_fragment.value
)
)
.filter(Boolean);
}
/**
* Sessions associated with this target (but only already loaded sessions).
* @type {SessionModel[]}
@@ -122,4 +150,66 @@ export default class TargetModel extends GeneratedTargetModel {
removeHostSet(hostSetID, options) {
return this.removeHostSets([hostSetID], options);
}
/**
* Adds credential libraries via the `add-credential-libraries` method.
* See serializer and adapter for more information.
* @param {[string]} credentialLibraryIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
addCredentialLibraries(
credentialLibraryIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'add-credential-libraries',
credentialLibraryIDs,
};
// There is no "deep merge" in ES.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
/**
* Delete credential libraries via the `remove-credential-libraries` method.
* See serializer and adapter for more information.
* @param {[string]} credentialLibraryIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
removeCredentialLibraries(
credentialLibraryIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'remove-credential-libraries',
credentialLibraryIDs,
};
// There is no "deep merge" in ES.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
/**
* Delete a single credential library set via the `remove-credential-libraries` method.
* @param {number} credentialLibraryID
* @param {object} options
* @return {Promise}
*/
removeCredentialLibrary(credentialLibraryID, options) {
return this.removeCredentialLibraries([credentialLibraryID], options);
}
}
@@ -0,0 +1,3 @@
import ApplicationSerializer from './application';
export default class CredentialLibrarySerializer extends ApplicationSerializer {}
@@ -0,0 +1,3 @@
import ApplicationSerializer from './application';
export default class CredentialStoreSerializer extends ApplicationSerializer {}
@@ -0,0 +1,27 @@
import JSONSerializer from '@ember-data/serializer/json';
export default class FragmentCredentialLibraryAttributesSerializer extends JSONSerializer {
/**
* If an attribute is annotated as readOnly in the model, don't serialize it.
* Otherwise delegate to default attribute serializer.
* Only serialize http_request_body when `http_method` is set to `POST`.
* @override
* @method serializeAttribute
* @param {Snapshot} snapshot
* @param {Object} json
* @param {String} key
* @param {Object} attribute
*/
serializeAttribute(snapshot, json, key, attribute) {
const { type, options } = attribute;
let value = super.serializeAttribute(...arguments);
// Convert empty strings to null.
if (type === 'string' && json[key] === '') json[key] = null;
// Do not serialize read-only attributes.
if (options.readOnly) delete json[key];
// Do not serialize http_request_body unless http_method is POST
if (key === 'http_request_body' && !json['http_method']?.match(/post/i))
delete json[key];
return value;
}
}
@@ -0,0 +1,34 @@
import JSONSerializer from '@ember-data/serializer/json';
export default class FragmentCredentialStoreAttributesSerializer extends JSONSerializer {
/**
* If an attribute is annotated as readOnly in the model, don't serialize it.
* Otherwise delegate to default attribute serializer.
*
* @override
* @method serializeAttribute
* @param {Snapshot} snapshot
* @param {Object} json
* @param {String} key
* @param {Object} attribute
*/
serializeAttribute(snapshot, json, key, attribute) {
const { type, options } = attribute;
let value = super.serializeAttribute(...arguments);
// Convert empty strings to null.
if (type === 'string' && json[key] === '') json[key] = null;
// Do not serialize read-only attributes.
if (options.readOnly) delete json[key];
// Token cannot be unset. If it's falsy, it must be removed.
if (key === 'token' && !json[key]) delete json[key];
// Client certificate key cannot be unset when certificate is set.
// If it's falsy, it must be removed.
if (
key === 'client_certificate_key' &&
!json[key] &&
json['client_certificate']
)
delete json[key];
return value;
}
}
+25 -1
View File
@@ -6,7 +6,11 @@ export default class TargetSerializer extends ApplicationSerializer {
/**
* If `adapterOptions.hostSets` is set to an array of host set models,
* the resulting target serialization should include **only host sets**
* and the version. Normally, host sets are not serialized.
* and the version.
* If `adapterOptions.credentialLibraries` is set to an array of
* credential library models, the resulting target serialization should
* include **only credential libraries** and the version.
* Normally, neither host sets or credential libraries are not serialized.
* @override
* @param {Snapshot} snapshot
* @return {object}
@@ -16,6 +20,12 @@ export default class TargetSerializer extends ApplicationSerializer {
const hostSetIDs = snapshot?.adapterOptions?.hostSetIDs;
if (hostSetIDs)
serialized = this.serializeWithHostSets(snapshot, hostSetIDs);
const credentialLibraryIDs = snapshot?.adapterOptions?.credentialLibraryIDs;
if (credentialLibraryIDs)
serialized = this.serializeWithCredentialLibraries(
snapshot,
credentialLibraryIDs
);
return serialized;
}
@@ -32,4 +42,18 @@ export default class TargetSerializer extends ApplicationSerializer {
host_set_ids: hostSetIDs,
};
}
/**
* Returns a payload containing only the credential_library_ids array using IDs
* passed into the function (rather than existing credential libraries on the model).
* @param {Snapshot} snapshot
* @param {[string]} credentialLibraryIDs
* @return {object}
*/
serializeWithCredentialLibraries(snapshot, credentialLibraryIDs) {
return {
version: snapshot.attr('version'),
application_credential_library_ids: credentialLibraryIDs,
};
}
}
@@ -0,0 +1 @@
export { default } from 'api/models/credential-library';
@@ -0,0 +1 @@
export { default } from 'api/models/credential-store';
@@ -0,0 +1 @@
export { default } from 'api/models/fragment-credential-library-attributes';
@@ -0,0 +1 @@
export { default } from 'api/models/fragment-credential-store-attributes';
@@ -0,0 +1 @@
export { default } from 'api/serializers/credential-library';
@@ -0,0 +1 @@
export { default } from 'api/serializers/credential-store';
@@ -0,0 +1 @@
export { default } from 'api/serializers/fragment-credential-library-attributes';
@@ -0,0 +1 @@
export { default } from 'api/serializers/fragment-credential-store-attributes';
+54 -1
View File
@@ -366,10 +366,10 @@ export default function () {
const target = targets.find(id);
const updatedAttrs = {
version: attrs.version,
hostSetIds: target.hostSetIds,
};
// If adding host sets, push them into the array
if (method === 'add-host-sets') {
updatedAttrs.hostSetIds = target.hostSetIds;
attrs.hostSetIds.forEach((id) => {
if (!updatedAttrs.hostSetIds.includes(id)) {
updatedAttrs.hostSetIds.push(id);
@@ -378,10 +378,28 @@ export default function () {
}
// If deleting host sets, filter them out of the array
if (method === 'remove-host-sets') {
updatedAttrs.hostSetIds = target.hostSetIds;
updatedAttrs.hostSetIds = updatedAttrs.hostSetIds.filter((id) => {
return !attrs.hostSetIds.includes(id);
});
}
// If adding credential libraries, push them into the array
if (method === 'add-credential-libraries') {
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
attrs.applicationCredentialLibraryIds.forEach((id) => {
if (!updatedAttrs.credentialLibraryIds.includes(id)) {
updatedAttrs.credentialLibraryIds.push(id);
}
});
}
// If deleting credential libraries, filter them out of the array
if (method === 'remove-credential-libraries') {
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
updatedAttrs.credentialLibraryIds =
updatedAttrs.credentialLibraryIds.filter((id) => {
return !attrs.applicationCredentialLibraryIds.includes(id);
});
}
return target.update(updatedAttrs);
}
);
@@ -456,6 +474,41 @@ export default function () {
return new Response(200);
});
// credential-stores
this.get(
'/credential-stores',
({ credentialStores }, { queryParams: { scope_id: scopeId } }) =>
credentialStores.where({ scopeId })
);
this.get('/credential-stores/:id');
this.post('/credential-stores');
this.patch('/credential-stores/:id');
this.del('/credential-stores/:id');
// credential-libraries
this.get(
'/credential-libraries',
(
{ credentialLibraries },
{ queryParams: { credential_store_id: credentialStoreId } }
) => credentialLibraries.where({ credentialStoreId })
);
this.get('/credential-libraries/:id');
this.post(
'/credential-libraries',
function ({ credentialStores, credentialLibraries }) {
const attrs = this.normalizedRequestAttrs();
const credentialStore = credentialStores.find(attrs.credentialStoreId);
attrs.scopeId = credentialStore.scope.id;
return credentialLibraries.create(attrs);
}
);
this.del('/credential-libraries/:id');
this.patch('/credential-libraries/:id');
/* Uncomment the following line and the Response import above
* Then change the response code to simulate error responses.
* this.get('/scopes', () => new Response(505));
@@ -0,0 +1,20 @@
import factory from '../generated/factories/credential-library';
import { random, system } from 'faker';
const types = ['vault'];
export default factory.extend({
id: (i) => `credential-library-id-${i}`,
type: (i) => types[i % types.length],
attributes() {
switch (this.type) {
case 'vault':
return {
http_method: 'GET',
http_request_body: random.word(),
path: system.directoryPath(),
};
}
},
});
@@ -0,0 +1,36 @@
import factory from '../generated/factories/credential-store';
import { random, internet, datatype } from 'faker';
import { trait } from 'ember-cli-mirage';
const types = ['vault'];
export default factory.extend({
id: (i) => `credential-store-id-${i}`,
type: (i) => types[i % types.length],
attributes() {
switch (this.type) {
case 'vault':
return {
address: internet.ip(),
namespace: random.word(),
ca_cert: 'ca-cert-123',
tls_server_name: internet.domainName(),
tls_skip_verify: datatype.boolean(),
token_hmac: 'token-hmac-abcde0987654321',
client_certificate: 'client-cert-123',
client_certificate_key_hmac: 'client-cert-key-hmac-abcde0987654321',
};
}
},
withAssociations: trait({
afterCreate(credentialStore, server) {
const { scope } = credentialStore;
server.createList('credential-library', 2, {
scope,
credentialStore,
});
},
}),
});
+15 -5
View File
@@ -7,7 +7,7 @@ const hostSetChance = 0.3;
export default factory.extend({
/**
* -1 means "unlimited" and we want to generate these on occassion.
* -1 means "unlimited" and we want to generate these on occasion.
*/
session_connection_limit: () => random.arrayElement([-1, datatype.number()]),
@@ -25,11 +25,11 @@ export default factory.extend({
},
/**
* Randomly selects existing host sets to assign to target.
* Randomly selects existing host sets and credential libraries to assign to target.
*/
withRandomHostSets: trait({
withAssociations: trait({
afterCreate(target, server) {
let randomlySelectedHostSets;
let randomlySelectedHostSets, randomlySelectedCredentialLibraries;
randomlySelectedHostSets = server.schema.hostSets
// BLERG: fun fact, for no reason at all, the element passed
// into a where function is not a full model instance, as you might
@@ -38,7 +38,17 @@ export default factory.extend({
// the result set of `where` _is a collection of model instances_.
.where((hostSet) => hostSet.scopeId === target.scope.id)
.models.filter(() => randomBoolean(hostSetChance));
target.update({ hostSets: randomlySelectedHostSets });
randomlySelectedCredentialLibraries = server.schema.credentialLibraries
.where(
(credentialLibrary) => credentialLibrary.scopeId === target.scope.id
)
.models.filter(() => randomBoolean());
target.update({
hostSets: randomlySelectedHostSets,
credentialLibraries: randomlySelectedCredentialLibraries,
});
},
}),
});
@@ -0,0 +1,12 @@
import { Factory } from 'ember-cli-mirage';
import { random, date } from 'faker';
/**
* GeneratedCredentialLibraryModelFactory
*/
export default Factory.extend({
name: () => random.words(),
description: () => random.words(),
created_time: () => date.recent(),
updated_time: () => date.recent(),
});
@@ -0,0 +1,12 @@
import { Factory } from 'ember-cli-mirage';
import { random, date } from 'faker';
/**
* GeneratedCredentialStoreModelFactory
*/
export default Factory.extend({
name: () => random.words(),
description: () => random.words(),
created_time: () => date.recent(),
updated_time: () => date.recent(),
});
@@ -0,0 +1,6 @@
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
scope: belongsTo(),
credentialStore: belongsTo(),
});
@@ -0,0 +1,5 @@
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
scope: belongsTo(),
});
+1
View File
@@ -3,4 +3,5 @@ import { Model, belongsTo, hasMany } from 'ember-cli-mirage';
export default Model.extend({
scope: belongsTo(),
hostSets: hasMany(),
credentialLibraries: hasMany(),
});
+2 -1
View File
@@ -47,7 +47,8 @@ export default function (server) {
// Other resources
server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => {
server.createList('host-catalog', 2, { scope }, 'withChildren');
server.createList('target', 2, { scope }, 'withRandomHostSets');
server.createList('session', 4, { scope }, 'withAssociations');
server.createList('credential-store', 3, { scope }, 'withAssociations');
server.createList('target', 2, { scope }, 'withAssociations');
});
}
@@ -0,0 +1,14 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
modelName: 'credential-library',
_hashForModel(model) {
const json = ApplicationSerializer.prototype._hashForModel.apply(
this,
arguments
);
json.credential_store_id = model.credentialStoreId;
return json;
},
});
@@ -0,0 +1,17 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
modelName: 'credential-store',
_hashForModel(/* model */) {
const json = ApplicationSerializer.prototype._hashForModel.apply(
this,
arguments
);
// in a real API, the following fields are set-only and returned in
// associated HMAC fields
delete json.attributes?.token;
delete json.attributes?.client_certificate_key;
return json;
},
});
+3
View File
@@ -23,6 +23,9 @@ export default ApplicationSerializer.extend({
return { id: host_set_id, host_catalog_id };
});
}
if (model.credentialLibraryIds?.length) {
json.application_credential_library_ids = model.credentialLibraryIds;
}
return json;
},
});
+6 -6
View File
@@ -29,20 +29,20 @@
"ember-cli-mirage": "^1.1.8",
"ember-data": "^3.20.0",
"ember-data-model-fragments": "5.0.0-beta.2",
"ember-fetch": "^8.0.1"
"ember-fetch": "^8.1.0"
},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.1.4",
"@ember/test-helpers": "^2.2.8",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"doctoc": "^2.0.0",
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.24.0",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-cli-update": "^0.54.6",
@@ -59,8 +59,8 @@
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^2.15.0",
"ember-try": "^1.4.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ember": "^10.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | credential library', function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('credential-library', {});
assert.ok(model);
});
});
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | credential store', function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('credential-store', {});
assert.ok(model);
});
});
@@ -0,0 +1,19 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Model | fragment credential library attributes',
function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord(
'fragment-credential-library-attributes',
{}
);
assert.ok(model);
});
}
);
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | fragment credential store attributes', function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('fragment-credential-store-attributes', {});
assert.ok(model);
});
});
+158
View File
@@ -223,4 +223,162 @@ module('Unit | Model | target', function (hooks) {
'Target has two associated sessions loaded in the store'
);
});
test('it has a `credentialLibraries` array of resolved model instances (if those instances are already in the store)', function (assert) {
assert.expect(4);
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'target',
attributes: {
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
},
},
});
const target = store.peekRecord('target', '123abc');
assert.equal(
target.application_credential_library_ids.length,
2,
'Target has two entires in application_credential_library_ids'
);
assert.equal(
target.credentialLibraries.length,
0,
'Target has no resolved credentialLibraries because they are not loaded yet'
);
store.push({
data: {
id: '1',
type: 'credential-library',
attributes: {},
},
});
store.push({
data: {
id: '2',
type: 'credential-library',
attributes: {},
},
});
// Since `credentialLibraries` is computed on `application_credential_library_ids`,
// not the store itself, it's necessary to do this assignment to kick-off the
// computed update.
/* eslint-disable no-self-assign */
target.application_credential_library_ids =
target.application_credential_library_ids;
/* eslint-enable no-self-assign */
assert.equal(
target.application_credential_library_ids.length,
2,
'Target has two entires in application_credential_library_ids'
);
assert.equal(
target.credentialLibraries.length,
2,
'Target has two resolved credentialLibraries'
);
});
test('it has an `addCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
assert.expect(1);
this.server.post(
'/v1/targets/123abc:add-credential-libraries',
(schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
application_credential_library_ids: ['123_abc', 'foobar'],
version: 1,
});
return { id: '123abc' };
}
);
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'target',
attributes: {
name: 'Target',
description: 'Description',
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope',
},
},
},
});
const model = store.peekRecord('target', '123abc');
await model.addCredentialLibraries(['123_abc', 'foobar']);
});
test('it has a `removeCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
assert.expect(1);
this.server.post(
'/v1/targets/123abc:remove-credential-libraries',
(schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
application_credential_library_ids: ['1', '2'],
version: 1,
});
return { id: '123abc' };
}
);
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'target',
attributes: {
name: 'Target',
description: 'Description',
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope',
},
},
},
});
const model = store.peekRecord('target', '123abc');
await model.removeCredentialLibraries(['1', '2']);
});
test('it has a `removeCredentialLibrary` method that deletes a single credential library using `removeCredentialLibraries` method', async function (assert) {
assert.expect(1);
this.server.post(
'/v1/targets/123abc:remove-credential-libraries',
(schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
application_credential_library_ids: ['2'],
version: 1,
});
return { id: '123abc' };
}
);
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'target',
attributes: {
name: 'Target',
description: 'Description',
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope',
},
},
},
});
const model = store.peekRecord('target', '123abc');
await model.removeCredentialLibrary('2');
});
});
@@ -203,6 +203,7 @@ module('Unit | Serializer | application', function (hooks) {
name: 'Target 1',
scope: { scope_id: 'o_123' },
host_sets: [],
application_credential_library_ids: [],
},
relationships: {},
},
@@ -0,0 +1,71 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Serializer | credential library', function (hooks) {
setupTest(hooks);
test('it serializes correctly on create', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('credential-library');
const record = store.createRecord('credential-library', {
type: 'vault',
name: 'Name',
description: 'Description',
attributes: {
path: '/vault/path',
http_method: 'GET',
},
version: 1,
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
type: 'vault',
credential_store_id: null,
name: 'Name',
description: 'Description',
version: 1,
attributes: {
path: '/vault/path',
http_method: 'GET',
},
});
});
test('it serializes correctly on update', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('credential-library');
store.push({
data: {
id: '1',
type: 'credential-library',
attributes: {
type: 'vault',
name: 'Name',
description: 'Description',
attributes: {
path: '/vault/path',
http_method: 'GET',
},
version: 1,
},
},
});
const record = store.peekRecord('credential-library', '1');
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
type: 'vault',
credential_store_id: null,
name: 'Name',
description: 'Description',
attributes: {
path: '/vault/path',
http_method: 'GET',
},
version: 1,
});
});
});
@@ -0,0 +1,93 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Serializer | credential store', function (hooks) {
setupTest(hooks);
test('it serializes vault-type correctly on create', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('credential-store');
const record = store.createRecord('credential-store', {
type: 'vault',
name: 'Name',
description: 'Description',
attributes: {
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
token: 'foo',
},
version: 1,
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
type: 'vault',
name: 'Name',
description: 'Description',
version: 1,
attributes: {
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
token: 'foo',
},
});
});
test('it serializes vault-type correctly on update', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('credential-store');
store.push({
data: {
id: '1',
type: 'credential-store',
attributes: {
type: 'vault',
name: 'Name',
description: 'Description',
attributes: {
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
token: 'foo',
},
version: 1,
},
},
});
const record = store.peekRecord('credential-store', '1');
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
type: 'vault',
name: 'Name',
description: 'Description',
attributes: {
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
token: 'foo',
},
version: 1,
});
});
});
@@ -0,0 +1,59 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Serializer | fragment credential library attributes',
function (hooks) {
setupTest(hooks);
test('it serializes empty strings to null', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const record = store.createRecord(
'fragment-credential-library-attributes',
{
http_method: '',
path: null,
}
);
let serializedRecord = record.serialize();
assert.deepEqual(serializedRecord, {
http_method: null,
path: null,
});
});
test('it does not serialize http_request_body unless http_method is set to POST', function (assert) {
assert.expect(2);
const store = this.owner.lookup('service:store');
const record = store.createRecord(
'fragment-credential-library-attributes',
{
http_method: 'GET',
http_request_body: 'body',
}
);
let serializedRecord = record.serialize();
assert.deepEqual(
serializedRecord,
{
http_method: 'GET',
path: null,
},
'http_request_body attribute is not expected'
);
record.http_method = 'POST';
serializedRecord = record.serialize();
assert.deepEqual(
serializedRecord,
{
http_method: 'POST',
http_request_body: 'body',
path: null,
},
'http_request_body attribute is expected'
);
});
}
);
@@ -0,0 +1,117 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Serializer | fragment credential store attributes',
function (hooks) {
setupTest(hooks);
test('it serializes empty strings to null', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const record = store.createRecord(
'fragment-credential-store-attributes',
{
address: '',
ca_cert: '',
client_certificate: '',
client_certificate_key: '',
namespace: '',
tls_server_name: '',
tls_skip_verify: false,
}
);
let serializedRecord = record.serialize();
assert.deepEqual(serializedRecord, {
address: null,
ca_cert: null,
client_certificate: null,
client_certificate_key: null,
namespace: null,
tls_server_name: null,
tls_skip_verify: false,
});
});
test('it does not serialize token if present but falsy', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const record = store.createRecord(
'fragment-credential-store-attributes',
{
token: null,
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
}
);
let serializedRecord = record.serialize();
assert.deepEqual(
serializedRecord,
{
address: 'http://localhost:5000',
ca_cert: 'ca-cert-123',
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
},
'token attribute is not expected'
);
});
test('it does not serialize client certificate key if present but falsy when client certificate is present', function (assert) {
assert.expect(2);
const store = this.owner.lookup('service:store');
const record = store.createRecord(
'fragment-credential-store-attributes',
{
client_certificate: 'client-cert-123',
client_certificate_key: '',
address: 'http://localhost:5000',
token: 'foo',
ca_cert: 'ca-cert-123',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
}
);
let serializedRecord = record.serialize();
assert.deepEqual(
serializedRecord,
{
client_certificate: 'client-cert-123',
address: 'http://localhost:5000',
token: 'foo',
ca_cert: 'ca-cert-123',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
},
'client certificate key attribute is not expected'
);
record.client_certificate_key = 'client-cert-key-456';
serializedRecord = record.serialize();
assert.deepEqual(
serializedRecord,
{
client_certificate: 'client-cert-123',
client_certificate_key: 'client-cert-key-456',
address: 'http://localhost:5000',
token: 'foo',
ca_cert: 'ca-cert-123',
namespace: 'foobar',
tls_server_name: 'yes',
tls_skip_verify: false,
},
'client certificate key attribute is expected'
);
});
}
);
@@ -4,7 +4,7 @@ import { setupTest } from 'ember-qunit';
module('Unit | Serializer | target', function (hooks) {
setupTest(hooks);
test('it serializes targets normally, without host sets', function (assert) {
test('it serializes targets normally, without host sets or credential libraries', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('target');
@@ -15,6 +15,7 @@ module('Unit | Serializer | target', function (hooks) {
{ host_set_id: '1', host_catalog_id: '2' },
{ host_set_id: '3', host_catalog_id: '4' },
],
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
scope: {
scope_id: 'org_1',
type: 'org',
@@ -63,6 +64,27 @@ module('Unit | Serializer | target', function (hooks) {
});
});
test('it serializes only credential libraries and version when an `adapterOptions.credentialLibraryIDs` array is passed', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('target');
const record = store.createRecord('target', {
name: 'User',
description: 'Description',
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
version: 1,
});
const snapshot = record._createSnapshot();
snapshot.adapterOptions = {
credentialLibraryIDs: ['4', '5'],
};
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
application_credential_library_ids: ['4', '5'],
version: 1,
});
});
test('it normalizes records with array fields', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
@@ -75,6 +97,7 @@ module('Unit | Serializer | target', function (hooks) {
{ host_set_id: '1', host_catalog_id: '2' },
{ host_set_id: '3', host_catalog_id: '4' },
],
application_credential_library_ids: ['1'],
};
const normalized = serializer.normalizeSingleResponse(
store,
@@ -92,13 +115,14 @@ module('Unit | Serializer | target', function (hooks) {
{ host_set_id: '1', host_catalog_id: '2' },
{ host_set_id: '3', host_catalog_id: '4' },
],
application_credential_library_ids: [{ value: '1' }],
},
relationships: {},
},
});
});
test('it normalizes missing host_sets to empty array', function (assert) {
test('it normalizes missing host_sets and credential libraries to empty array', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('target');
@@ -122,6 +146,7 @@ module('Unit | Serializer | target', function (hooks) {
name: 'Target 1',
scope: { scope_id: 'o_123' },
host_sets: [],
application_credential_library_ids: [],
},
relationships: {},
},
+5 -5
View File
@@ -27,7 +27,7 @@
},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.1.4",
"@ember/test-helpers": "^2.2.8",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"babel-eslint": "^10.1.0",
@@ -35,9 +35,9 @@
"core": "*",
"doctoc": "^2.0.0",
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.24.0",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-cli-update": "^0.54.6",
@@ -53,8 +53,8 @@
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^2.15.0",
"ember-try": "^1.4.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ember": "^10.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
+5 -5
View File
@@ -43,16 +43,16 @@
},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.1.4",
"@ember/test-helpers": "^2.2.8",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"doctoc": "^2.0.0",
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.24.0",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-disable-prototype-extensions": "^1.1.3",
@@ -68,8 +68,8 @@
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^2.15.0",
"ember-try": "^1.4.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ember": "^10.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
+4 -8
View File
@@ -1,9 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.4577 1.67511L5.56323 7.36908L10.5292 13.3821L13.2972 11.0961L17.1724 15.5238L13.7226 18.5433L15.2044 20.2364L18.6543 17.2169L20.1097 18.8797L16.662 21.8973L18.1438 23.5904L23.2846 19.091L15.0328 9.66276L17.4237 7.68814L12.4577 1.67511Z" fill="black"/>
<mask id="path-2-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.766 4.35302L5.62911 4.255L5.53249 11.2901L9.12206 11.3394L9.20978 17.2227L4.62573 17.2911L4.6593 19.5409L9.24333 19.4725L9.27627 21.682L4.69506 21.7503L4.7286 24L11.5596 23.8982L11.3728 11.3703L12.6694 11.3882L12.766 4.35302Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.766 4.35302L5.62911 4.255L5.53249 11.2901L9.12206 11.3394L9.20978 17.2227L4.62573 17.2911L4.6593 19.5409L9.24333 19.4725L9.27627 21.682L4.69506 21.7503L4.7286 24L11.5596 23.8982L11.3728 11.3703L12.6694 11.3882L12.766 4.35302Z" fill="black"/>
<path d="M5.62911 4.255L5.64971 2.75515L4.14985 2.73455L4.12925 4.23441L5.62911 4.255ZM12.766 4.35302L14.2659 4.37362L14.2865 2.87376L12.7866 2.85316L12.766 4.35302ZM5.53249 11.2901L4.03264 11.2695L4.01204 12.7694L5.5119 12.79L5.53249 11.2901ZM9.12206 11.3394L10.6219 11.3171L10.6002 9.85959L9.14265 9.83958L9.12206 11.3394ZM9.20978 17.2227L9.23216 18.7226L10.732 18.7002L10.7096 17.2004L9.20978 17.2227ZM4.62573 17.2911L4.60335 15.7913L3.10352 15.8137L3.1259 17.3135L4.62573 17.2911ZM4.6593 19.5409L3.15947 19.5633L3.18185 21.0631L4.68168 21.0407L4.6593 19.5409ZM9.24333 19.4725L10.7432 19.4501L10.7208 17.9503L9.22095 17.9727L9.24333 19.4725ZM9.27627 21.682L9.29863 23.1818L10.7985 23.1594L10.7761 21.6596L9.27627 21.682ZM4.69506 21.7503L4.6727 20.2504L3.17286 20.2728L3.19522 21.7726L4.69506 21.7503ZM4.7286 24L3.22877 24.0224L3.25113 25.5222L4.75096 25.4998L4.7286 24ZM11.5596 23.8982L11.5819 25.398L13.0818 25.3756L13.0594 23.8758L11.5596 23.8982ZM11.3728 11.3703L11.3934 9.87048L9.84992 9.84929L9.87293 11.3927L11.3728 11.3703ZM12.6694 11.3882L12.6488 12.888L14.1487 12.9086L14.1693 11.4087L12.6694 11.3882ZM5.60851 5.75486L12.7454 5.85288L12.7866 2.85316L5.64971 2.75515L5.60851 5.75486ZM7.03235 11.3107L7.12897 4.2756L4.12925 4.23441L4.03264 11.2695L7.03235 11.3107ZM9.14265 9.83958L5.55309 9.79028L5.5119 12.79L9.10146 12.8393L9.14265 9.83958ZM7.62222 11.3618L7.70995 17.2451L10.7096 17.2004L10.6219 11.3171L7.62222 11.3618ZM9.1874 15.7229L4.60335 15.7913L4.64811 18.791L9.23216 18.7226L9.1874 15.7229ZM3.1259 17.3135L3.15947 19.5633L6.15913 19.5185L6.12557 17.2688L3.1259 17.3135ZM4.68168 21.0407L9.2657 20.9723L9.22095 17.9727L4.63692 18.0411L4.68168 21.0407ZM7.74349 19.4949L7.77644 21.7043L10.7761 21.6596L10.7432 19.4501L7.74349 19.4949ZM9.25391 20.1821L4.6727 20.2504L4.71742 23.2501L9.29863 23.1818L9.25391 20.1821ZM3.19522 21.7726L3.22877 24.0224L6.22843 23.9777L6.19489 21.7279L3.19522 21.7726ZM4.75096 25.4998L11.5819 25.398L11.5372 22.3983L4.70624 22.5002L4.75096 25.4998ZM13.0594 23.8758L12.8726 11.348L9.87293 11.3927L10.0597 23.9205L13.0594 23.8758ZM12.69 9.88829L11.3934 9.87048L11.3522 12.8702L12.6488 12.888L12.69 9.88829ZM11.2662 4.33242L11.1696 11.3676L14.1693 11.4087L14.2659 4.37362L11.2662 4.33242Z" fill="black" mask="url(#path-2-inside-1)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2796 1.26121C9.20838 0.46864 7.88301 0 6.44818 0C2.88695 0 0 2.88695 0 6.44818C0 9.00798 1.4916 11.2194 3.65306 12.2607L3.68943 9.61275C2.80735 8.84314 2.25 7.71075 2.25 6.44818C2.25 4.12959 4.12959 2.25 6.44818 2.25C7.18811 2.25 7.88333 2.44143 8.48703 2.77746L10.2796 1.26121Z" fill="black"/>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.18544 1.11673L4.58911 4.91271L7.89977 8.92139L9.74509 7.3974L12.3286 10.3492L10.0287 12.3622L11.0166 13.4909L13.3165 11.4779L14.2867 12.5865L11.9883 14.5982L12.9762 15.7269L16.4034 12.7273L10.9021 6.44183L12.4961 5.12541L9.18544 1.11673Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.39097 2.9997L4.63303 2.93436L4.56862 7.62444L6.96166 7.65731L7.02014 11.5795L3.96411 11.6251L3.98649 13.1249L7.04251 13.0793L7.06447 14.5523L4.01033 14.5979L4.03269 16.0977L8.58667 16.0298L8.46213 7.67792L9.32656 7.68979L9.39097 2.9997Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.73342 0.840799C7.01928 0.312425 6.13571 0 5.17915 0C2.805 0 0.880371 1.92463 0.880371 4.29878C0.880371 6.00532 1.87477 7.47961 3.31575 8.1738L3.33999 6.4085C2.75194 5.89543 2.38037 5.1405 2.38037 4.29878C2.38037 2.75306 3.63343 1.5 5.17915 1.5C5.67244 1.5 6.13592 1.62762 6.53838 1.85163L7.73342 0.840799Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -33,3 +33,4 @@ edit-form: Edit Form
change-origin: Change Origin URL
change-state: Change State
close: Close
toggle-secret: Toggle secret visibility
+86 -8
View File
@@ -253,18 +253,96 @@ target:
actions:
create: New Target
delete: Delete Target
add-host-sets: Add Host Sets
add-host-sets: Add Host Sources
add-credential-libraries: Add Credential Sources
types:
tcp: TCP
host-set:
title: Host Source
title_plural: Host Sources
messages:
welcome:
title: Welcome to Host Sets
description: No hosts sets in this target.
title: Welcome to Host Sources
description: No hosts sources in this target.
none:
title: No Host Sets Available
description: No hosts sets available to add to target.
title: No Host Sources Available
description: No hosts sources available to add to target.
add:
title: Add Host Sets
description: Select host sets to assign to this target.
secret: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
title: Add Host Sources
description: Select host sources to assign to this target.
credential-library:
title: Credential Source
title_plural: Credential Sources
messages:
welcome:
title: Welcome to Credential Sources
description: No credential sources in this target.
none:
title: No Credential Sources Available
description: No credential sources available to add to this target.
add:
title: Add Credential Sources
description: Select credential sources to assign to this target.
credential-store:
title: Credential Store
title_plural: Credential Stores
description: A credential store is a collection of credentials and credential libraries.
messages:
welcome:
title: Welcome to Credential Stores
actions:
delete: Delete Credential Store
types:
vault: Vault
form:
address:
label: Address
help: The address of the Vault server. This should be a complete URL such as https://127.0.0.1:8200.
namespace:
label: Namespace
help: A Vault namespace. Requires Vault Enterprise.
ca_cert:
label: CA Certificate
help: A PEM-encoded CA certificate to verify the Vault server's TLS certificate.
tls_server_name:
label: TLS Server Name
help: Name to use as the SNI host when connecting to Vault via TLS.
tls_skip_verify:
label: TLS Skip Verify
help: Disable verification of TLS certificates. Using this option is highly discouraged as it decreases the security of data transmissions to and from the Vault server.
token:
label: Token
help: A token used for accessing Vault.
token_hmac:
label: Token HMAC
client_certificate:
label: Client Certificate
help: A PEM-encoded client certificate to use for TLS authentication to the Vault server.
client_certificate_key:
label: Client Certificate Key
help: A PEM-encoded private key matching the client certificate from Client Certificate.
client_certificate_key_hmac:
label: Client Certificate Key HMAC
credential-library:
title: Credential Library
title_plural: Credential Libraries
description: A credential library is a resource that provides credentials.
actions:
create: New Credential Library
delete: Delete Credential Library
messages:
none:
title: No Credential Libraries Available
description: No Credential Libraries available in this credential store.
form:
vault_path:
label: Vault Path
help: The path the library uses when requesting credentials from Vault.
http_method:
label: HTTP Method
help: The HTTP method the library uses when requesting credentials from Vault.
http_request_body:
label: HTTP Method POST Request Body
help: The body of the HTTP request the library sends to Vault when requesting credentials. Only allowed when HTTP method is set to POST.
types:
vault: Vault
@@ -1,4 +1,4 @@
<div class="rose-form-checkbox{{if @error " error"}}">
<div class="rose-form-checkbox{{if @error " error"}} {{if @inline " inline"}}">
<Input
...attributes
@@ -68,3 +68,61 @@ Renders a form input checkbox.
}
}}</Story>
</Preview>
```htmlbars
<Rose::Form::Checkbox
@name="boolean-value"
@label="Yes?"
@checked={{false}}
@error={{false}}
@disabled={{false}}
@inline={{false}}
/>
```
<Preview>
<Story name="Checkbox with inline style" height="10rem">{{
template: hbs`
<Rose::Form::Checkbox
@name='checkbox-1'
@label='Checkbox1 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
<Rose::Form::Checkbox
@name='checkbox-2'
@label='Checkbox2 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
<Rose::Form::Checkbox
@name='checkbox-3'
@label='Checkbox3 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
`,
context: {
description: text('description', 'Short description in label'),
helperText: text('helperText', 'This is an optional help text.'),
checked: boolean('checked', false),
error: boolean('error', false),
disabled: boolean('disabled', false),
inline: boolean('inline', false)
}
}}</Story>
</Preview>
@@ -20,6 +20,7 @@
input=(component 'rose/form/input' disabled=this.disabled)
select=(component 'rose/form/select' disabled=this.disabled)
textarea=(component 'rose/form/textarea' disabled=this.disabled)
fieldset=(component 'rose/form/fieldset')
actions=(component
(if this.showEditToggleButton
'rose/form/actions/edit-toggle'
@@ -1,3 +1,3 @@
{{yield (hash
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed)
)}}
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed inline=@inline)
)}}
@@ -15,6 +15,7 @@ Renders a form radiofield group.
@name={{name}}
@selectedValue="disagree"
@changed={{fn}}
@inline={{false}}
as |radioGroup|
>
<radioGroup.radio
@@ -39,6 +40,7 @@ Renders a form radiofield group.
@name={{name}}
@selectedValue={{selectedValue}}
@changed={{changed}}
@inline={{inline}}
as |radioGroup|
>
<radioGroup.radio
@@ -59,6 +61,7 @@ Renders a form radiofield group.
context: {
name: text('name', 'mustagree'),
disabled: boolean('disabled', false),
inline: boolean('inline', true),
selectedValue: text('selectedValue', 'disagree'),
firstFieldLabel: text('first label', 'Agree'),
firstFieldValue: text('first value', 'agree'),
@@ -1,4 +1,4 @@
<div class="rose-form-radio{{if @error " error"}}">
<div class="rose-form-radio{{if @error " error"}} {{if @inline "inline"}}">
<RadioButton
...attributes
@@ -54,3 +54,71 @@ Renders a form radiofield.
}
}}</Story>
</Preview>
```htmlbars
<Rose::Form::Radio::Radio
@label="Yes"
@error={{false}}
@value="yes-agree"
@selectedValue="yes-agree"
@disabled={{false}}
@changed={{fn}}
@inline={{true}}
/>
```
<Preview>
<Story name="Radio with inline style" height="10rem">{{
template: hbs`
<Rose::Form::Radio::Radio
@label='Radiofield1'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
<Rose::Form::Radio::Radio
@label='Radiofield2'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
<Rose::Form::Radio::Radio
@label='Radiofield3'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
`,
context: {
error: boolean('error', false),
value: text('value', 'value and selectedValue must match to select radiofield'),
selectedValue: text(
'selectedValue',
'value and selectedValue must match to select radiofield'
),
disabled: boolean('disabled', false),
icon: select(
'icon',
{
'none': null,
'user-organization': 'user-organization',
'copy-action': 'copy-action',
'unfold-more': 'unfold-more',
'unfold-less': 'unfold-less',
},
null
),
inline: boolean('inline', false)
}
}}</Story>
</Preview>
@@ -2,6 +2,10 @@
@use '../../utilities/type';
.rose-form-checkbox {
&.inline {
display: inline-block;
margin-right: sizing.rems(s);
}
@include type.type(m);
position: relative;
margin-bottom: sizing.rems(m);
@@ -5,7 +5,7 @@
margin-bottom: sizing.rems(m);
.rose-form-fieldset-title {
@include type.type(l, bold);
@include type.type(s, semibold);
}
.rose-form-fieldset-description {
@@ -21,8 +21,13 @@ $radio-svg: (
);
$data-svg-prefix: 'data:image/svg+xml;utf-8,';
.rose-form-radio {
&.inline {
display: inline-block;
.rose-form-radio-label {
margin-right: sizing.rems(s);
}
}
@include type.type(m);
position: relative;
margin-bottom: sizing.rems(xs);
+10 -10
View File
@@ -40,22 +40,22 @@
},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.1.4",
"@ember/test-helpers": "^2.2.8",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"@hashicorp/structure-icons": "^1.9.0-0",
"@storybook/addon-actions": "^6.0.26",
"@storybook/addon-docs": "^6.0.26",
"@storybook/addon-knobs": "^6.0.26",
"@storybook/ember": "^6.0.26",
"@storybook/addon-actions": "^6.3.1",
"@storybook/addon-docs": "^6.3.1",
"@storybook/addon-knobs": "^6.3.0",
"@storybook/ember": "^6.3.1",
"@storybook/ember-cli-storybook": "^0.4.0",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"doctoc": "^2.0.0",
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.24.0",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sass": "^10.0.1",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
@@ -73,8 +73,8 @@
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^2.15.0",
"ember-try": "^1.4.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ember": "^10.1.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
@@ -83,7 +83,7 @@
"prettier": "^2.2.1",
"qunit": "^2.13.0",
"qunit-dom": "^1.6.0",
"sass": "^1.26.5",
"sass": "^1.36.0",
"sass-lint": "^1.13.1"
},
"engines": {
+6 -2
View File
@@ -52,7 +52,7 @@
"request": "~2.68.0",
"tunnel-agent": "~0.6.0",
"cryptiles": "~4.1.2",
"prismjs": "~1.23.0",
"prismjs": "^1.24.0",
"npm-user-validate": "^1.0.1",
"caniuse-lite": "^1.0.30001157",
"immer": "^8.0.1",
@@ -71,7 +71,11 @@
"ws": "^7.4.6",
"css-what": "^5.0.1",
"trim-newlines": "^3.0.1",
"clean-css": "^4.1.11"
"clean-css": "^4.1.11",
"mem": "^4.0.0",
"minimist": "^1.2.3",
"normalize-url": "^4.5.1",
"glob-parent": "^5.1.2"
},
"config": {
"commitizen": {
@@ -0,0 +1,127 @@
<Rose::Form
@onSubmit={{@submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@name='name'
@type='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@error={{@model.errors.name}}
@helperText={{t 'form.name.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.textarea
@name='description'
@type='description'
@value={{@model.description}}
@label={{t 'form.description.label'}}
@error={{@model.errors.description}}
@helperText={{t 'form.description.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.description}}
<field.errors as |errors|>
{{#each @model.errors.description as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.input
@name='vault_path'
@type='text'
@value={{@model.attributes.path}}
@label={{t 'resources.credential-library.form.vault_path.label'}}
@error={{@model.errors.attributes_path}}
@helperText={{t 'resources.credential-library.form.vault_path.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_path}}
<field.errors as |errors|>
{{#each @model.errors.attributes_path as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.fieldset>
<:title>{{t 'resources.credential-library.form.http_method.label'}}</:title>
<:description>{{t
'resources.credential-library.form.http_method.help'
}}</:description>
<:body>
<form.radioGroup
@name='http_method'
@selectedValue={{@model.attributes.http_method}}
@changed={{fn (mut @model.attributes.http_method)}}
@inline={{true}}
disabled={{@model.isSaving}}
as |radioGroup|
>
{{#each this.httpMethodOptions as |httpMethod|}}
<radioGroup.radio @label={{httpMethod}} @value={{httpMethod}} />
{{/each}}
</form.radioGroup>
</:body>
</form.fieldset>
{{#if this.isHttpRequestBodyAllowed}}
<form.textarea
@name='http_request_body'
@type='text'
@value={{@model.attributes.http_request_body}}
@label={{t 'resources.credential-library.form.http_request_body.label'}}
@error={{@model.errors.attributes_http_request_body}}
@helperText={{t
'resources.credential-library.form.http_request_body.help'
}}
@disabled={{this.cannotSaveHttpRequestBody}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_http_request_body}}
<field.errors as |errors|>
{{#each @model.errors.attributes_http_request_body as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
{{/if}}
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
</Rose::Form>
@@ -0,0 +1,20 @@
import Component from '@glimmer/component';
import { options } from 'api/models/credential-library';
import { computed } from '@ember/object';
export default class FormCredentialLibraryIndexComponent extends Component {
// =properties
/**
* @type {object}
*/
httpMethodOptions = options.http_method;
/**
* Only allow HTTP request body field if http_method is set to POST.
* @type {boolean}
*/
@computed('args.model.attributes.http_method')
get isHttpRequestBodyAllowed() {
return this.args.model.attributes.http_method?.match(/post/i);
}
}
@@ -0,0 +1,243 @@
<Rose::Form
@onSubmit={{@submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@error={{@model.errors.base}}
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@name='name'
@type='name'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.textarea
@name='description'
@type='description'
@value={{@model.description}}
@label={{t 'form.description.label'}}
@error={{@model.errors.description}}
@helperText={{t 'form.description.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.description}}
<field.errors as |errors|>
{{#each @model.errors.description as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.input
@name='address'
@type='text'
@value={{@model.attributes.address}}
@label={{t 'resources.credential-store.form.address.label'}}
@error={{@model.errors.attributes_address}}
@helperText={{t 'resources.credential-store.form.address.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_address}}
<field.errors as |errors|>
{{#each @model.errors.attributes_address as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.input
@name='token'
@type='text'
@value={{@model.attributes.token}}
@label={{t 'resources.credential-store.form.token.label'}}
@error={{@model.errors.attributes_token}}
@helperText={{t 'resources.credential-store.form.token.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_token}}
<field.errors as |errors|>
{{#each @model.errors.attributes_token as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
{{#unless @model.isNew}}
<form.input
@value={{@model.attributes.token_hmac}}
@label={{t 'resources.credential-store.form.token_hmac.label'}}
readonly={{true}}
@disabled={{true}}
/>
{{/unless}}
<form.input
@name='namespace'
@type='text'
@value={{@model.attributes.namespace}}
@label={{t 'resources.credential-store.form.namespace.label'}}
@error={{@model.errors.attributes_namespace}}
@helperText={{t 'resources.credential-store.form.namespace.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_namespace}}
<field.errors as |errors|>
{{#each @model.errors.attributes_namespace as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.input
@name='tls_server_name'
@type='text'
@value={{@model.attributes.tls_server_name}}
@label={{t 'resources.credential-store.form.tls_server_name.label'}}
@error={{@model.errors.attributes_tls_server_name}}
@helperText={{t 'resources.credential-store.form.tls_server_name.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_tls_server_name}}
<field.errors as |errors|>
{{#each @model.errors.attributes_tls_server_name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.checkbox
@label={{t 'resources.credential-store.form.tls_skip_verify.label'}}
@helperText={{t 'resources.credential-store.form.tls_skip_verify.help'}}
@checked={{@model.attributes.tls_skip_verify}}
onChange={{fn
(mut @model.attributes.tls_skip_verify)
(if @model.attributes.tls_skip_verify false true)
}}
@error={{@model.errors.attributes_tls_skip_verify}}
as |field|
>
{{#if @model.errors.attributes_tls_skip_verify}}
<field.errors as |errors|>
{{#each @model.errors.attributes_tls_skip_verify as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.checkbox>
<form.textarea
@name='client_certificate'
@type='text'
@value={{@model.attributes.client_certificate}}
@label={{t 'resources.credential-store.form.client_certificate.label'}}
@error={{@model.errors.attributes_client_certificate}}
@helperText={{t 'resources.credential-store.form.client_certificate.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_client_certificate}}
<field.errors as |errors|>
{{#each @model.errors.attributes_client_certificate as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.textarea
@name='client_certificate_key'
@type='text'
@value={{@model.attributes.client_certificate_key}}
@label={{t 'resources.credential-store.form.client_certificate_key.label'}}
@error={{@model.errors.attributes_client_certificate_key}}
@helperText={{t
'resources.credential-store.form.client_certificate_key.help'
}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_client_certificate_key}}
<field.errors as |errors|>
{{#each @model.errors.attributes_client_certificate_key as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
{{#unless @model.isNew}}
<form.input
@value={{@model.attributes.client_certificate_key_hmac}}
@label={{t
'resources.credential-store.form.client_certificate_key_hmac.label'
}}
readonly={{true}}
@disabled={{true}}
/>
{{/unless}}
<form.textarea
@name='ca_cert'
@type='text'
@value={{@model.attributes.ca_cert}}
@label={{t 'resources.credential-store.form.ca_cert.label'}}
@error={{@model.errors.attributes_ca_cert}}
@helperText={{t 'resources.credential-store.form.ca_cert.help'}}
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.attributes_ca_cert}}
<field.errors as |errors|>
{{#each @model.errors.attributes_ca_cert as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
</Rose::Form>
@@ -0,0 +1,63 @@
{{#if this.hasAvailableCredentialLibraries}}
<Rose::Form
class='full-width'
@onSubmit={{fn this.submit @submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@error={{@model.errors.base}}
as |form|
>
<form.actions
@submitText={{t 'resources.target.actions.add-credential-libraries'}}
@cancelText={{t 'actions.cancel'}}
/>
<Rose::Table as |table|>
<table.header as |header|>
<header.row as |row|>
<row.headerCell>{{t 'form.id.label'}}</row.headerCell>
<row.headerCell>{{t 'form.name.label'}}</row.headerCell>
<row.headerCell>{{t 'form.type.label'}}</row.headerCell>
</header.row>
</table.header>
<table.body as |body|>
{{#each this.filteredCredentialLibraries as |credentialLibrary|}}
<body.row as |row|>
<row.headerCell>
<form.checkbox
@label={{credentialLibrary.id}}
@description={{credentialLibrary.description}}
onChange={{fn this.toggleCredentialLibrary credentialLibrary}}
/>
</row.headerCell>
<row.cell>{{credentialLibrary.name}}</row.cell>
<row.cell>
<Rose::Badge>{{t
(concat
'resources.credential-library.types.' credentialLibrary.type
)
}}</Rose::Badge>
</row.cell>
</body.row>
{{/each}}
</table.body>
</Rose::Table>
</Rose::Form>
{{/if}}
{{#unless this.hasAvailableCredentialLibraries}}
<Rose::Layout::Centered>
<Rose::Message
@title={{t 'resources.target.credential-library.messages.none.title'}}
as |message|
>
<message.description>
{{t 'resources.target.credential-library.messages.none.description'}}
</message.description>
<message.link @route='scopes.scope.targets.target.credential-libraries'>
<Rose::Icon @name='chevron-left' />
{{t 'actions.back'}}
</message.link>
</Rose::Message>
</Rose::Layout::Centered>
{{/unless}}
@@ -0,0 +1,65 @@
import Component from '@glimmer/component';
import { computed, action } from '@ember/object';
import { A } from '@ember/array';
export default class FormTargetAddCredentialLibrariesIndexComponent extends Component {
// =properties
/**
* Array of selected credential library IDs.
* @type {EmberArray}
*/
selectedCredentialLibraryIDs = A();
/**
* Checks for unassigned credential libraries.
* @param {[CredentialLibraryModel]} filteredCredentialLibraries
* @type {boolean}
*/
@computed('filteredCredentialLibraries.length')
get hasAvailableCredentialLibraries() {
return this.filteredCredentialLibraries.length > 0;
}
/**
* Filter out credential libraries not already added to the target.
* @type {[CredentialLibraryModel]}
*/
@computed(
'args.{credentialLibraries.[],model.application_credential_library_ids.[]}'
)
get filteredCredentialLibraries() {
// Get IDs for credential libraries already added to the current target
const currentCredentialLibraryIDs =
this.args.model.application_credential_library_ids.map(
(credentialLibrary) => credentialLibrary.value
);
const notAddedCredentialLibraries = this.args.credentialLibraries.filter(
({ id }) => !currentCredentialLibraryIDs.includes(id)
);
return notAddedCredentialLibraries;
}
// =actions
/**
* Add/Remove credential library to current selection
* @param CredentialLibraryModel
*/
@action
toggleCredentialLibrary(credentialLibrary) {
if (!this.selectedCredentialLibraryIDs.includes(credentialLibrary.id)) {
this.selectedCredentialLibraryIDs.addObject(credentialLibrary.id);
} else {
this.selectedCredentialLibraryIDs.removeObject(credentialLibrary.id);
}
}
/**
* Submit selected credential library ids
*/
@action
submit(fn) {
fn(this.selectedCredentialLibraryIDs);
}
}
@@ -9,7 +9,7 @@
>
<form.actions
@submitText={{t 'actions.add-host-sets'}}
@submitText={{t 'resources.target.actions.add-host-sets'}}
@cancelText={{t 'actions.cancel'}}
/>
@@ -0,0 +1,17 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeCredentialStoresController extends Controller {
// =services
@service intl;
// = attributes
/**
* Translated breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('resources.credential-store.title_plural');
}
}
@@ -0,0 +1,13 @@
import Controller from '@ember/controller';
export default class ScopesScopeCredentialStoresCredentialStoreController extends Controller {
// =attributes
/**
* A credential store breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.model.displayName;
}
}
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated credential libraries breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('resources.credential-library.title_plural');
}
}
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated new credential libraries breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('titles.new');
}
}
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeTargetsTargetCredentialLibrariesController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated credential library breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('resources.credential-library.title_plural');
}
}
+19
View File
@@ -65,6 +65,8 @@ Router.map(function () {
this.route('target', { path: ':target_id' }, function () {
this.route('host-sets', function () {});
this.route('add-host-sets');
this.route('credential-libraries');
this.route('add-credential-libraries');
});
this.route('new');
});
@@ -86,6 +88,23 @@ Router.map(function () {
});
});
});
this.route('credential-stores', function () {
this.route(
'credential-store',
{ path: ':credential_store_id' },
function () {
this.route('credential-libraries', function () {
this.route('new');
this.route(
'credential-library',
{ path: ':credential_library_id' },
function () {}
);
});
}
);
this.route('new');
});
});
});
@@ -0,0 +1,63 @@
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import loading from 'ember-loading/decorator';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeCredentialStoresRoute extends Route {
// =methods
/**
* Load all credential stores under current scope
* @returns {Promise[CredentialStoreModel]}
*/
async model() {
const { id: scope_id } = this.modelFor('scopes.scope');
return this.store.query('credential-store', { scope_id });
}
// =actions
/**
* Handle save
* @param {CredentialStoreModel} credentialStore
*/
@action
@loading
@notifyError(({ message }) => message)
@notifySuccess('notifications.save-success')
async save(credentialStore) {
await credentialStore.save();
await this.transitionTo(
'scopes.scope.credential-stores.credential-store',
credentialStore
);
this.refresh();
}
/**
* Rollback changes on credential stores.
* @param {CredentialStoreModel} credentialStore
*/
@action
cancel(credentialStore) {
const { isNew } = credentialStore;
credentialStore.rollbackAttributes();
if (isNew) this.transitionTo('scopes.scope.credential-stores');
}
/**
* Deletes the credential store and redirects to index
* @param {CredentialStoreModel} credentialStore
*/
@action
@loading
@confirm('questions.delete-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.delete-success')
async delete(credentialStore) {
await credentialStore.destroyRecord();
await this.replaceWith('scopes.scope.credential-stores');
this.refresh();
}
}
@@ -0,0 +1,34 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Route {
/**
* Load a specific credential store in current scope
* @return {Promise[CredentialStoreModel]}
*/
async model({ credential_store_id }) {
return this.store.findRecord('credential-store', credential_store_id);
}
/**
* Render credential store header, navigation and actions page sections.
* @override
*/
renderTemplate() {
super.renderTemplate(...arguments);
this.render('scopes/scope/credential-stores/credential-store/-header', {
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'header',
});
this.render('scopes/scope/credential-stores/credential-store/-navigation', {
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'navigation',
});
this.render('scopes/scope/credential-stores/credential-store/-actions', {
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'actions',
});
}
}
@@ -0,0 +1,72 @@
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import loading from 'ember-loading/decorator';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
// =methods
/**
* Loads all credential libraries under the current credential store.
* @return {Promise{[CredentialLibraryModel]}}
*/
model() {
const { id: credential_store_id } = this.modelFor(
'scopes.scope.credential-stores.credential-store'
);
return this.store.query('credential-library', { credential_store_id });
}
// =actions
/**
* Rollback changes on a credential library.
* @param {CredentialLibraryModel} credentialLibrary
*/
@action
cancel(credentialLibrary) {
const { isNew } = credentialLibrary;
credentialLibrary.rollbackAttributes();
if (isNew)
this.transitionTo(
'scopes.scope.credential-stores.credential-store.credential-libraries'
);
}
/**
* Handle save of a credential library
* @param {CredentialLibraryModel} credentialLibrary
*/
@action
@loading
@notifyError(({ message }) => message)
@notifySuccess(({ isNew }) =>
isNew ? 'notifications.create-success' : 'notifications.save-success'
)
async save(credentialLibrary) {
await credentialLibrary.save();
await this.transitionTo(
'scopes.scope.credential-stores.credential-store.credential-libraries.credential-library',
credentialLibrary
);
this.refresh();
}
/**
* Handle delete of a credential library
* @param {CredentialLibraryModel} credentialLibrary
*/
@action
@loading
@confirm('questions.delete-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.delete-success')
async delete(credentialLibrary) {
await credentialLibrary.destroyRecord();
await this.replaceWith(
'scopes.scope.credential-stores.credential-store.credential-libraries'
);
this.refresh();
}
}
@@ -0,0 +1,49 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryRoute extends Route {
// =methods
/**
* Load a credential-library using current credential store and its parent scope.
* @param {object} params
* @param {string} params.credential_library_id
* @return {CredentialLibraryModel}
*/
async model({ credential_library_id }) {
return this.store.findRecord('credential-library', credential_library_id, {
reload: true,
});
}
/**
* Renders the credential library specific templates for header, navigation, and action page sections.
* @override
*/
renderTemplate() {
super.renderTemplate(...arguments);
this.render(
'scopes/scope/credential-stores/credential-store/credential-libraries/credential-library/-header',
{
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'header',
}
);
this.render(
'scopes/scope/credential-stores/credential-store/credential-libraries/credential-library/-navigation',
{
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'navigation',
}
);
this.render(
'scopes/scope/credential-stores/credential-store/credential-libraries/credential-library/-actions',
{
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'actions',
}
);
}
}
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryIndexRoute extends Route {}
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesIndexRoute extends Route {}
@@ -0,0 +1,46 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewRoute extends Route {
// =methods
/**
* Creates a new unsaved credential library in current credential store.
* @return {CredentialLibraryModel}
*/
model() {
const { id: credential_store_id, type } = this.modelFor(
'scopes.scope.credential-stores.credential-store'
);
return this.store.createRecord('credential-library', {
type,
credential_store_id,
});
}
/**
* Renders new credential library specific templates for header, navigation,
* and action page sections.
* @override
*/
renderTemplate() {
super.renderTemplate(...arguments);
this.render(
'scopes/scope/credential-stores/credential-store/credential-libraries/new/-header',
{
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'header',
}
);
this.render('_empty', {
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'navigation',
});
this.render('_empty', {
into: 'scopes/scope/credential-stores/credential-store',
outlet: 'actions',
});
}
}
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresCredentialStoreIndexRoute extends Route {}
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresIndexRoute extends Route {}
@@ -0,0 +1,15 @@
import Route from '@ember/routing/route';
export default class ScopesScopeCredentialStoresNewRoute extends Route {
// =methods
/**
* Creates a new unsaved credential-store
*/
model() {
const scopeModel = this.modelFor('scopes.scope');
return this.store.createRecord('credential-store', {
type: 'vault',
scopeModel,
});
}
}
@@ -0,0 +1,94 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { all } from 'rsvp';
import { action } from '@ember/object';
import loading from 'ember-loading/decorator';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeTargetsTargetAddCredentialLibrariesRoute extends Route {
// =services
@service intl;
@service notify;
// =methods
/**
* Empty out any previously loaded credential libraries.
*/
beforeModel() {
this.store.unloadAll('credential-library');
}
/**
* Returns the current target and all credential libraries.
* @return {{target: TargetModel, credentialLibraries: [CredentialLibraryModel]}}
*/
async model() {
const target = this.modelFor('scopes.scope.targets.target');
const { id: scope_id } = this.modelFor('scopes.scope');
const credentialStores = await this.store.query('credential-store', {
scope_id,
});
await all(
credentialStores.map(({ id: credential_store_id }) =>
this.store.query('credential-library', { credential_store_id })
)
);
const credentialLibraries = this.store.peekAll('credential-library');
return {
target,
credentialLibraries,
};
}
/**
* Renders credential libraries specific header template.
* @override
*/
renderTemplate() {
super.renderTemplate(...arguments);
this.render(
'scopes/scope/targets/target/add-credential-libraries/-header',
{
into: 'scopes/scope/targets/target',
outlet: 'header',
}
);
this.render('-empty', {
into: 'scopes/scope/targets/target',
outlet: 'navigation',
});
this.render('-empty', {
into: 'scopes/scope/targets/target',
outlet: 'actions',
});
}
// =actions
/**
* Add credential libraries to current target
* @param {TargetModel} target
* @param {[string]} credentialLibraryIDs
*/
@action
@loading
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.add-success')
async save(target, credentialLibraryIDs) {
await target.addCredentialLibraries(credentialLibraryIDs);
this.replaceWith('scopes.scope.targets.target.credential-libraries');
}
/**
* Redirect to target credential libraries as if nothing ever happened.
*/
@action
cancel() {
this.replaceWith('scopes.scope.targets.target.credential-libraries');
}
}
@@ -0,0 +1,51 @@
import Route from '@ember/routing/route';
import { all } from 'rsvp';
import { action } from '@ember/object';
import loading from 'ember-loading/decorator';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeTargetsTargetCredentialLibrariesRoute extends Route {
// =methods
/**
* Loads all credential libraries under the current target.
* @return {Promise{[CredentialLibraryModel]}}
*/
beforeModel() {
const { application_credential_library_ids: credential_libraries } =
this.modelFor('scopes.scope.targets.target');
return all(
credential_libraries.map((credential_library) =>
this.store.findRecord('credential-library', credential_library.value, {
reload: true,
})
)
);
}
/**
* Returns the previously loaded target instance.
* @return {TargetModel}
*/
model() {
return this.modelFor('scopes.scope.targets.target');
}
// =actions
/**
* Remove a credential library from the current target.
* @param {TargetModel} target
* @param {CredentialLibraryModel} credentialLibrary
*/
@action
@loading
@confirm('questions.remove-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.remove-success')
async removeCredentialLibrary(target, credentialLibrary) {
await target.removeCredentialLibrary(credentialLibrary.id);
this.refresh();
}
}
+7
View File
@@ -96,6 +96,13 @@
margin-bottom: 0;
}
.rose-table-header-cell {
p {
color: var(--ui-gray-subtler-1);
font-weight: normal;
}
}
.rose-form-checkbox {
margin-bottom: 0;
+6
View File
@@ -83,6 +83,12 @@
<Rose::Icon @name='app-icons/host-catalogs' @size='medium' />
{{t 'resources.host-catalog.title_plural'}}
</nav.link>
{{#if (feature-flag 'credential-store')}}
<nav.link @route='scopes.scope.credential-stores'>
<Rose::Icon @name='app-icons/credentials' @size='medium' />
{{t 'resources.credential-store.title_plural'}}
</nav.link>
{{/if}}
</Rose::Nav::Sidebar>
{{/if}}
@@ -0,0 +1,3 @@
{{page-title (t 'resources.credential-store.title_plural')}}
{{outlet}}
@@ -0,0 +1,24 @@
{{page-title @model.displayName}}
<Rose::Layout::Page as |page|>
<page.breadcrumbs>
<BreadCrumbs />
</page.breadcrumbs>
<page.header>
{{outlet 'header'}}
</page.header>
<page.navigation>
{{outlet 'navigation'}}
</page.navigation>
<page.actions>
{{outlet 'actions'}}
</page.actions>
<page.body>
{{outlet}}
</page.body>
</Rose::Layout::Page>
@@ -0,0 +1,20 @@
<Rose::Dropdown
@text={{t 'actions.manage'}}
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link
@route='scopes.scope.credential-stores.credential-store.credential-libraries.new'
@model={{@model}}
>
{{t 'resources.credential-library.actions.create'}}
</dropdown.link>
<dropdown.separator />
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'delete' @model)}}
>
{{t 'resources.credential-store.actions.delete'}}
</dropdown.button>
</Rose::Dropdown>
@@ -0,0 +1,9 @@
<h2>{{t 'resources.credential-store.title'}}</h2>
<p>{{t 'resources.credential-store.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
@@ -0,0 +1,10 @@
<Rose::Nav::Tabs as |nav|>
<nav.link @route='scopes.scope.credential-stores.credential-store.index'>
{{t 'titles.details'}}
</nav.link>
<nav.link
@route='scopes.scope.credential-stores.credential-store.credential-libraries'
>
{{t 'resources.credential-library.title_plural'}}
</nav.link>
</Rose::Nav::Tabs>
@@ -0,0 +1,3 @@
{{page-title (t 'resources.credential-library.title_plural')}}
{{outlet}}
@@ -0,0 +1,3 @@
{{page-title @model.displayName}}
{{outlet}}
@@ -0,0 +1,13 @@
<Rose::Dropdown
@text={{t 'actions.manage'}}
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'delete' @model)}}
>
{{t 'resources.credential-library.actions.delete'}}
</dropdown.button>
</Rose::Dropdown>
@@ -0,0 +1,12 @@
<h2>
{{t 'resources.credential-library.title'}}
<DocLink @doc='credential-library' @iconSize='large' />
</h2>
<p>{{t 'resources.credential-library.description'}}</p>
<Copyable
@text={{@model.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{@model.id}}</code>
</Copyable>
@@ -0,0 +1,7 @@
<Rose::Nav::Tabs as |nav|>
<nav.link
@route='scopes.scope.credential-stores.credential-store.credential-libraries.credential-library.index'
>
{{t 'titles.details'}}
</nav.link>
</Rose::Nav::Tabs>
@@ -0,0 +1,5 @@
<Form::CredentialLibrary
@model={{@model}}
@submit={{route-action 'save' @model}}
@cancel={{route-action 'cancel' @model}}
/>
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff. Show more