Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c868086c64 | ||
|
|
316c5627dc | ||
|
|
faae34ccf5 | ||
|
|
015b034feb | ||
|
|
f1e1603502 | ||
|
|
df2d5d688e | ||
|
|
a0227720a8 | ||
|
|
d07407e881 | ||
|
|
7be2dc6d03 | ||
|
|
1321df3265 | ||
|
|
8ad462288f | ||
|
|
16e83b7428 | ||
|
|
f705a386f0 | ||
|
|
5589cdd8d5 | ||
|
|
53d5a77631 | ||
|
|
357df84c22 | ||
|
|
557076276b | ||
|
|
09894ed376 | ||
|
|
d5bf053b41 | ||
|
|
8f11765b96 | ||
|
|
4ad88d1dcd | ||
|
|
92572395bb | ||
|
|
e389494a3b | ||
|
|
aa9694cae2 | ||
|
|
c462fa4c51 | ||
|
|
f7d3dfdac0 | ||
|
|
6df7624afc | ||
|
|
fe9ce25ed0 | ||
|
|
a3ee4d02e8 | ||
|
|
7988eee93d | ||
|
|
57fb26f37d | ||
|
|
f956f95e78 | ||
|
|
e613d55dcb | ||
|
|
f75529b5c4 | ||
|
|
75ed3b816b | ||
|
|
c7c54daaad | ||
|
|
6458f928b9 | ||
|
|
865e36c03c | ||
|
|
af87d85a34 | ||
|
|
9808e9a9d3 | ||
|
|
b22ea6fa79 | ||
|
|
0bda47ec29 | ||
|
|
0742987874 | ||
|
|
6b485b9f93 | ||
|
|
1978b1c08e | ||
|
|
bce44de8e0 | ||
|
|
0bdd1a1f04 | ||
|
|
255d727ffb | ||
|
|
7bfa3da53d | ||
|
|
28ebc93179 | ||
|
|
547330821f | ||
|
|
cf70ea3e01 | ||
|
|
52afb56057 | ||
|
|
df32a8eb4b | ||
|
|
14190fdf67 | ||
|
|
0459cab9ed | ||
|
|
db5b97fbda | ||
|
|
cf85826ff8 | ||
|
|
46f33aba17 | ||
|
|
177f96876c | ||
|
|
50b0aee33c | ||
|
|
73002a38c4 | ||
|
|
17bd4c69b6 | ||
|
|
a6b2557cd5 | ||
|
|
9a5110aa71 | ||
|
|
caf85c57ec | ||
|
|
878f3be747 | ||
|
|
af9555e7f3 | ||
|
|
4afdc77f4c | ||
|
|
7ea772ca31 | ||
|
|
10fb5c8265 | ||
|
|
6c58749a6e | ||
|
|
e6b99e1418 | ||
|
|
6babacd0bc | ||
|
|
4423c53cc4 | ||
|
|
451c1e861b | ||
|
|
157dda4f8e | ||
|
|
929f22a48b | ||
|
|
d8aca33fcd | ||
|
|
144c7d13ee | ||
|
|
2bbbceb9d9 | ||
|
|
cb948fb640 | ||
|
|
6d8d28e1b4 | ||
|
|
99e24500d3 | ||
|
|
0a8f1d4405 | ||
|
|
f32311def3 | ||
|
|
e2b0c3ad13 | ||
|
|
b454efe41f | ||
|
|
e778847375 | ||
|
|
eb5d4b2289 | ||
|
|
4bae0296cf | ||
|
|
594f951ca7 | ||
|
|
87938d968c | ||
|
|
372f8d5b96 | ||
|
|
f8f7831cc3 | ||
|
|
b08f228c9b | ||
|
|
6367f3a9bc | ||
|
|
b96510de65 | ||
|
|
e0d3b25be2 | ||
|
|
4b77732fd6 | ||
|
|
0b83b4bd85 | ||
|
|
0ed506a111 | ||
|
|
1a97472c04 | ||
|
|
551619dfaa | ||
|
|
a1528be4fd | ||
|
|
fbba6571db | ||
|
|
57de78e672 | ||
|
|
43cd357e9a | ||
|
|
5e6a3e9d9a | ||
|
|
aea3e6f3ca | ||
|
|
eac0ee6b53 | ||
|
|
9e8d7c9cae | ||
|
|
273b5c8df6 | ||
|
|
4b430f1a7f | ||
|
|
66b40e5318 | ||
|
|
42283596b2 | ||
|
|
cfd3fe468e | ||
|
|
7c0366c790 | ||
|
|
5c2540d22b | ||
|
|
a68d94161f | ||
|
|
6be0a2a17d | ||
|
|
fdfa88e089 | ||
|
|
b1a34115a5 | ||
|
|
5a1192ed2e | ||
|
|
5644e57fc5 | ||
|
|
f097eab53a | ||
|
|
ce7d743f81 | ||
|
|
166b35860a | ||
|
|
2bbc12ddea | ||
|
|
c36030c12d | ||
|
|
521d70a4bb | ||
|
|
13794c7bf8 | ||
|
|
5f8042a551 | ||
|
|
0ab5ff8370 | ||
|
|
9f28484bd7 | ||
|
|
57b9e86562 | ||
|
|
40a5e221e7 | ||
|
|
165e4aec75 | ||
|
|
2448c03b27 | ||
|
|
57ca18670f | ||
|
|
e7b553efda | ||
|
|
b27fe14dbc | ||
|
|
726f6d31e2 | ||
|
|
1fb3e466c6 | ||
|
|
2a8d42eaba | ||
|
|
32c69624f9 | ||
|
|
04af1a431f | ||
|
|
a6b7837af1 | ||
|
|
9343121f23 | ||
|
|
e76bc73c11 | ||
|
|
f344d73f32 | ||
|
|
eb185279b2 | ||
|
|
0dd0e16783 | ||
|
|
cb18e011a9 | ||
|
|
09d9432825 | ||
|
|
3ae2423aa4 | ||
|
|
ab1a642f66 | ||
|
|
d94c4921c8 | ||
|
|
910c0e4316 | ||
|
|
4f74f8578e | ||
|
|
77b1ca477d | ||
|
|
e291956c1b | ||
|
|
8b51bcf95b | ||
|
|
ce286964d1 | ||
|
|
c971d6d2ce | ||
|
|
064c857d20 | ||
|
|
161cf3cd53 | ||
|
|
2d1475d211 | ||
|
|
fc4dab606e | ||
|
|
3a3fdf2396 | ||
|
|
c82269c960 | ||
|
|
87619dc9dc | ||
|
|
99baefaffc | ||
|
|
40c2acd6ab | ||
|
|
4eaceabb00 | ||
|
|
a1cfa84919 | ||
|
|
350c4f5f70 | ||
|
|
7fb4110a1b | ||
|
|
a400892790 | ||
|
|
dae42c7288 | ||
|
|
8ad6664a10 | ||
|
|
b5fdb850f6 | ||
|
|
b2f11f712b | ||
|
|
b4d36438a4 | ||
|
|
1017295a85 | ||
|
|
b0326e0c99 | ||
|
|
fc1d5ff0ca | ||
|
|
a54ae21858 | ||
|
|
30cc58cd56 | ||
|
|
59934559a5 | ||
|
|
b8e99ce389 | ||
|
|
7d96d9125f | ||
|
|
4a0f1d04f2 | ||
|
|
1165d21ef6 | ||
|
|
2fff4ab587 | ||
|
|
2da521d643 | ||
|
|
c10838234a | ||
|
|
b652f759c8 | ||
|
|
0fc32bcbf7 | ||
|
|
329cf802d8 | ||
|
|
05178cc3a5 | ||
|
|
689fc249d8 | ||
|
|
03e06a22a4 | ||
|
|
39b71aa526 | ||
|
|
0053b09285 | ||
|
|
1e1387700c | ||
|
|
f6016453f1 | ||
|
|
afccd0a9f7 | ||
|
|
a79ec87954 | ||
|
|
ec7e66160f | ||
|
|
cb0c03264f | ||
|
|
ababc5812d | ||
|
|
46160aad6f | ||
|
|
d881b57b87 | ||
|
|
f510d2f4fe | ||
|
|
01e8b22a92 | ||
|
|
876fc0c71f | ||
|
|
858a3420e1 | ||
|
|
d7735bd402 | ||
|
|
0232570831 | ||
|
|
3fd8d2d01f | ||
|
|
73db666f43 | ||
|
|
3506b34863 | ||
|
|
7d08d69db0 | ||
|
|
bdb38a5324 | ||
|
|
c3381bc6fc | ||
|
|
8118781d06 | ||
|
|
10a133b2af | ||
|
|
ee5b346275 | ||
|
|
f6aa9ef7cf | ||
|
|
e7db3cb327 | ||
|
|
9794116562 | ||
|
|
7c377506c8 | ||
|
|
5a1e3e235a | ||
|
|
3c83375d84 | ||
|
|
4256c7e552 | ||
|
|
6cbde9c982 | ||
|
|
4659b54e9e | ||
|
|
f22922ee60 | ||
|
|
7fd7613c30 | ||
|
|
7478aa8f09 | ||
|
|
128f963d47 | ||
|
|
884a0ebbc5 | ||
|
|
eba1c7ad57 | ||
|
|
64928feef2 | ||
|
|
22156bdead | ||
|
|
50eb86b736 | ||
|
|
4b039c9f59 | ||
|
|
1846eae462 | ||
|
|
25228d3855 | ||
|
|
4f471c9799 | ||
|
|
12d063a440 | ||
|
|
79c90b5183 | ||
|
|
1f0d79aac3 | ||
|
|
2d35037365 | ||
|
|
5cdc7af588 | ||
|
|
6b9c8192a9 | ||
|
|
c84b561a58 | ||
|
|
e248bbe200 | ||
|
|
5ce8355a2a | ||
|
|
c9b90db36d | ||
|
|
7f022fc64c | ||
|
|
2d32726207 | ||
|
|
3d4bd2adf9 | ||
|
|
485f11cb01 | ||
|
|
2545753c2c | ||
|
|
c1654180c4 | ||
|
|
025297c619 | ||
|
|
39ec40d488 | ||
|
|
ce01358a7e | ||
|
|
4ed0178c7a | ||
|
|
4b84eec59d | ||
|
|
40168fa8c4 | ||
|
|
044fe778af | ||
|
|
3a1416e2ff | ||
|
|
dc0e866796 | ||
|
|
fe3b5e905f | ||
|
|
42bc4c1777 | ||
|
|
59565cf194 | ||
|
|
8cfcf47900 | ||
|
|
15522f31af | ||
|
|
da04a190e6 | ||
|
|
3cc5456bde | ||
|
|
f98bfc2130 | ||
|
|
168e7fbac6 | ||
|
|
7e29d47b70 | ||
|
|
a03983e2f3 | ||
|
|
b5b2f61cbe | ||
|
|
84fe240c1f | ||
|
|
27f8be2213 | ||
|
|
382c4cbc7e | ||
|
|
3b1837999b | ||
|
|
a1c4d00eed | ||
|
|
c2ffe19d44 | ||
|
|
14d96a4a8a | ||
|
|
5284514a05 | ||
|
|
a39752d1c9 | ||
|
|
b0bd9b6808 | ||
|
|
2cca6fe005 | ||
|
|
455b1a29b0 | ||
|
|
cdb9761be6 | ||
|
|
0ac657e378 | ||
|
|
00b62a6f50 | ||
|
|
f57619d1fc | ||
|
|
2d24617dba | ||
|
|
abc9687261 | ||
|
|
24c62fbd46 | ||
|
|
951f69d4b2 | ||
|
|
55ead33b4a | ||
|
|
8adf4bb95b | ||
|
|
6c7483cbf2 | ||
|
|
bf2e267696 | ||
|
|
d8de56ecf4 | ||
|
|
59932f52db | ||
|
|
dffc236860 | ||
|
|
d76a4b0055 | ||
|
|
3728e4cd51 | ||
|
|
b7bd40c688 | ||
|
|
47d4366397 | ||
|
|
9abb540d7f | ||
|
|
155ed7a6be | ||
|
|
699672218d | ||
|
|
8bfd9bd7a3 | ||
|
|
a1f428550d | ||
|
|
3fa6332ab4 | ||
|
|
9f6cfe4edb | ||
|
|
7cde048ac1 | ||
|
|
ee390a6fea | ||
|
|
7e3096f849 | ||
|
|
16c03a088b | ||
|
|
28e2b67aa8 | ||
|
|
b69f964b43 | ||
|
|
84bb640340 | ||
|
|
dbc06a4e0a | ||
|
|
3d900f5a2b | ||
|
|
e50ea4b555 | ||
|
|
119cbbe815 | ||
|
|
6e95686b9a | ||
|
|
4f5c0a3e98 | ||
|
|
0c9ba7e7a7 | ||
|
|
b78726caff | ||
|
|
d10de61fb4 | ||
|
|
aced3207f3 | ||
|
|
14f6b30b26 | ||
|
|
fb85869e2c | ||
|
|
03ce03bebf | ||
|
|
1e04c696ad | ||
|
|
89055089a3 | ||
|
|
f4e20b87c7 | ||
|
|
f402819945 | ||
|
|
cd2523bc20 | ||
|
|
7f994e9597 | ||
|
|
07e16ef71b | ||
|
|
ca56c37fb0 | ||
|
|
5e2d9e9e2b | ||
|
|
74df923775 | ||
|
|
839962186f | ||
|
|
86fbbe7cdf | ||
|
|
7b51d15ba4 | ||
|
|
d0ca5cc041 | ||
|
|
310539e2de | ||
|
|
9da86f8411 | ||
|
|
76e721bd91 | ||
|
|
a19aa2ff0c | ||
|
|
cdecf570e2 | ||
|
|
c04c8f4f0a | ||
|
|
8fb33b8ff5 | ||
|
|
e89f31a93b | ||
|
|
518bf19318 | ||
|
|
d35823fae6 | ||
|
|
0231dabbe6 | ||
|
|
263d2b8d95 | ||
|
|
98537f708c | ||
|
|
5596d55035 | ||
|
|
a8f1780731 | ||
|
|
8e6988c179 | ||
|
|
ea293d947f | ||
|
|
d23afb1c71 | ||
|
|
b8ec749d03 | ||
|
|
0c5c9387b5 | ||
|
|
bbbd5cea65 | ||
|
|
66888a4c95 | ||
|
|
ca35246a7b | ||
|
|
16fe712f4e | ||
|
|
296b5fbecd | ||
|
|
4240bbee56 | ||
|
|
c3a9ecb0ec | ||
|
|
e40ff1f63c | ||
|
|
87479b8e3c | ||
|
|
264f81a6f3 | ||
|
|
772c8b38c0 | ||
|
|
03aa4f32f1 | ||
|
|
0b145fac47 | ||
|
|
9bd7193833 | ||
|
|
f3a4017e5f | ||
|
|
43eca819bf | ||
|
|
9aa3f8f5ad | ||
|
|
8cceb4090f | ||
|
|
dc7ebec668 | ||
|
|
cffa419dd6 | ||
|
|
83a938fada | ||
|
|
61c80518de | ||
|
|
d1119efd11 | ||
|
|
a9cb6f35f9 | ||
|
|
6f85419675 | ||
|
|
0cb90353f0 | ||
|
|
cd9f05962c | ||
|
|
9d342cf517 | ||
|
|
88fc063ab9 | ||
|
|
d3e707d740 | ||
|
|
fc4af516a2 | ||
|
|
edb8836a2c | ||
|
|
b29f877e35 | ||
|
|
e299740951 | ||
|
|
03fbc19c2c | ||
|
|
e42647e6a2 | ||
|
|
a03a3811d4 | ||
|
|
d4ef4bc58c | ||
|
|
3d7166d059 | ||
|
|
025ae1bfe2 | ||
|
|
0bc4cd6e21 | ||
|
|
4a6555317a | ||
|
|
be7e3bb56e | ||
|
|
ed0c774714 | ||
|
|
2bdb008f65 | ||
|
|
8109d7789c | ||
|
|
3eb9f07fe0 | ||
|
|
a07cd9e1a1 | ||
|
|
bbbb1620f6 | ||
|
|
1d71e3bec8 | ||
|
|
63d302d716 | ||
|
|
341baa76fc | ||
|
|
cc6f0aa1b4 | ||
|
|
1fb98f8ed3 | ||
|
|
08543e9868 | ||
|
|
0fbcf8e16e | ||
|
|
7ff569779c | ||
|
|
8f2deadc49 | ||
|
|
89b7d0345a | ||
|
|
ad3b9ed684 | ||
|
|
a7a84d64b9 | ||
|
|
7fd89bb946 | ||
|
|
dbfc21ace3 | ||
|
|
7bd8d87ad2 | ||
|
|
f5b80f93d2 | ||
|
|
1e5d52410e | ||
|
|
0dedbfb411 | ||
|
|
e376bb8e20 | ||
|
|
48dc8a392d | ||
|
|
992df6b34b | ||
|
|
af7e539f3d | ||
|
|
83f9ab5c61 | ||
|
|
a9c06638c7 | ||
|
|
0637d805d3 | ||
|
|
f089200dc0 | ||
|
|
2726538c41 | ||
|
|
b6291bd3c7 | ||
|
|
0e89ecabdc | ||
|
|
debf6fbe06 | ||
|
|
517ba08f55 | ||
|
|
7f13365336 | ||
|
|
8ebdd995e3 | ||
|
|
e73cd828a5 | ||
|
|
d2a42f7468 | ||
|
|
b1614dc65d | ||
|
|
9931b8d3ca | ||
|
|
b7e5477b3d | ||
|
|
027371567b | ||
|
|
95612186d8 | ||
|
|
f7dd5ebe55 | ||
|
|
a6be658aa5 | ||
|
|
123476a3a8 | ||
|
|
d44528fc2c | ||
|
|
b2c85b9805 | ||
|
|
03ce6be3ee | ||
|
|
a35c4e0ffd | ||
|
|
710bcccfd5 | ||
|
|
8b9d945efb | ||
|
|
aa2114062b | ||
|
|
90055ea142 | ||
|
|
e1aa0c6bcb | ||
|
|
af337626f5 | ||
|
|
dad2d456d0 | ||
|
|
9dac6fc446 | ||
|
|
00348e4c55 | ||
|
|
b0dd9bf803 | ||
|
|
2e23bbea3e | ||
|
|
7e7bad050f | ||
|
|
afadb1d83a | ||
|
|
52c9fb5570 | ||
|
|
2dd3e13cd3 | ||
|
|
74fa0876fb | ||
|
|
c531553c07 | ||
|
|
0ad6d027e6 | ||
|
|
2eb7084643 | ||
|
|
06d162ae89 | ||
|
|
c43ced0380 | ||
|
|
28a4a7304e | ||
|
|
e74fd0202e | ||
|
|
0823c1cb2d | ||
|
|
d034631e4d | ||
|
|
c5af15f4c1 | ||
|
|
fce79d46d7 | ||
|
|
c45357bf25 | ||
|
|
d96d7425f8 | ||
|
|
2000c10006 | ||
|
|
a348e4830a | ||
|
|
7c496b5b90 | ||
|
|
5d6da1e52d | ||
|
|
2eb1d01622 | ||
|
|
f012d10dcb | ||
|
|
a96689e2cc | ||
|
|
c5f72c53d8 | ||
|
|
699d5063d9 | ||
|
|
08deda59aa | ||
|
|
f8cce58d49 | ||
|
|
f69477e556 | ||
|
|
7f5a792af4 | ||
|
|
dd96423ead | ||
|
|
a1117529e5 | ||
|
|
44510ea8d4 | ||
|
|
9c93ff6c13 | ||
|
|
59818fa24d | ||
|
|
98e14a642f | ||
|
|
45fc58fe75 | ||
|
|
6dad1cd276 | ||
|
|
2212c7dd9e | ||
|
|
98bc86497a | ||
|
|
f447fb11ef | ||
|
|
e091730ffb | ||
|
|
3e479fc02a | ||
|
|
ce2be6b4b3 | ||
|
|
816b50eb1b | ||
|
|
9067558082 | ||
|
|
d6129a7dfc | ||
|
|
3b4bb24eb7 | ||
|
|
2217482329 | ||
|
|
32bdf2acdc | ||
|
|
03187e2e2e | ||
|
|
cf72af4004 | ||
|
|
e4a0150ee7 | ||
|
|
1a46a6b183 | ||
|
|
cf377ad3dc | ||
|
|
c82a8be1ed | ||
|
|
b9c5ed0de3 | ||
|
|
885ce5ebf5 | ||
|
|
9f7d528582 | ||
|
|
8544d61e9c | ||
|
|
a1b3e5382b | ||
|
|
1fb41665e5 | ||
|
|
bd50c4f78c | ||
|
|
0f73cd5faf | ||
|
|
96de8f262f | ||
|
|
4a871134a5 | ||
|
|
9bcb402fa2 | ||
|
|
bb1580ab6e | ||
|
|
75f45a134d | ||
|
|
b272f85e68 | ||
|
|
fdb46df9f2 | ||
|
|
f39ac156cb | ||
|
|
986cde5941 | ||
|
|
c94f54c90f | ||
|
|
4c648ad4be | ||
|
|
cd10c30ca8 | ||
|
|
d964411f70 | ||
|
|
9d68eb495e | ||
|
|
ef80dd87c1 | ||
|
|
a59e012f52 | ||
|
|
580a2f4159 | ||
|
|
b0d999cf3c | ||
|
|
97b82818cf | ||
|
|
4f0f8fce26 | ||
|
|
2b7563ee7b | ||
|
|
d8f43531e1 | ||
|
|
0404fe308f | ||
|
|
d3bdab9014 | ||
|
|
4b41c8dc7a | ||
|
|
12913e70ce | ||
|
|
7630f454fd | ||
|
|
9a2e5d23b9 | ||
|
|
a0f371bfc5 | ||
|
|
9100fe9a34 | ||
|
|
87b76a91c4 | ||
|
|
aa4a487e97 | ||
|
|
94cfca561c | ||
|
|
58f46e9334 | ||
|
|
5519cf8557 | ||
|
|
371b89b5ad | ||
|
|
07d2667d14 | ||
|
|
39d19cab06 | ||
|
|
d7659bd9de | ||
|
|
e855697601 | ||
|
|
e5c4ac650e | ||
|
|
688b6652d1 |
No files matched your search
@@ -22,6 +22,7 @@ testem.log
|
||||
yarn-error.log
|
||||
storybook-static
|
||||
**/.storybook/*.html
|
||||
.watchman-cookie*
|
||||
|
||||
# ember-try
|
||||
.node_modules.ember-try
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Ability } from 'ember-can';
|
||||
|
||||
/**
|
||||
* Generic ability provides abilities common to all models in the API.
|
||||
*/
|
||||
export default class CollectionAbility extends Ability {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canList() {
|
||||
return this.hasAuthorizedCollectionAction('list');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canCreate() {
|
||||
return this.hasAuthorizedCollectionAction('create');
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Returns true if the given action is contained in the ability model's
|
||||
* `authorized_collection_actions` array.
|
||||
* @param {string} action
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasAuthorizedCollectionAction(action) {
|
||||
const authorized_collection_actions =
|
||||
(this.model || {}).authorized_collection_actions || {};
|
||||
const collection = this.collection || {};
|
||||
return authorized_collection_actions[collection]?.includes(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Ability } from 'ember-can';
|
||||
|
||||
/**
|
||||
* Generic ability provides abilities common to all models in the API.
|
||||
*/
|
||||
export default class ModelAbility extends Ability {
|
||||
// =permissions
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRead() {
|
||||
return this.hasAuthorizedAction('read');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canUpdate() {
|
||||
return this.hasAuthorizedAction('update');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canDelete() {
|
||||
return this.hasAuthorizedAction('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* To save an existing model (already persisted to the API), it must contain
|
||||
* the the `update` authorized action.
|
||||
*
|
||||
* For now, new models _always_ have this ability. The reason is because the
|
||||
* `create` action is authorized on collections, not individual records. So
|
||||
* this function can't actually tell from a model instance if create
|
||||
* is allowed.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSave() {
|
||||
if (this.model.isNew || (!this.model.isNew && this.canUpdate)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Returns true if the given action is contained in the ability model's
|
||||
* `authorized_actions` array.
|
||||
* @param {string} action
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasAuthorizedAction(action) {
|
||||
return this.model.authorized_actions?.includes(action);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import Model from '@ember-data/model';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragment } from 'ember-data-model-fragments/attributes';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
@@ -35,6 +36,17 @@ export default class BaseModel extends Model {
|
||||
|
||||
// =attributes
|
||||
|
||||
@attr({
|
||||
readOnly: true,
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
authorized_actions;
|
||||
|
||||
@attr({
|
||||
readOnly: true,
|
||||
})
|
||||
authorized_collection_actions;
|
||||
|
||||
set scopeModel(model) {
|
||||
if (model) {
|
||||
const json = model.serialize();
|
||||
@@ -45,7 +57,7 @@ export default class BaseModel extends Model {
|
||||
|
||||
/**
|
||||
* Convenience for getting and setting the parent scope ID.
|
||||
* @type {string
|
||||
* @type {string}
|
||||
*/
|
||||
@computed('scope.scope_id')
|
||||
get scopeID() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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/abilities/collection';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/abilities/model';
|
||||
@@ -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';
|
||||
@@ -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));
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import factory from '../generated/factories/account';
|
||||
import { random, internet } from 'faker';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('account') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
case 'password':
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import factory from '../generated/factories/auth-method';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
const types = ['password', 'oidc'];
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('auth-method') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
authorized_collection_actions: () => {
|
||||
return {
|
||||
accounts: ['create', 'list'],
|
||||
};
|
||||
},
|
||||
id: (i) => `auth-method-id-${i}`,
|
||||
|
||||
// Cycle through available types
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import factory from '../generated/factories/credential-library';
|
||||
import { random, system } from 'faker';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
const types = ['vault'];
|
||||
|
||||
export default factory.extend({
|
||||
id: (i) => `credential-library-id-${i}`,
|
||||
type: (i) => types[i % types.length],
|
||||
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('credential-library') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
case 'vault':
|
||||
return {
|
||||
http_method: 'GET',
|
||||
http_request_body: random.word(),
|
||||
path: system.directoryPath(),
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import factory from '../generated/factories/credential-store';
|
||||
import { random, internet, datatype } from 'faker';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
const types = ['vault'];
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('credential-store') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
authorized_collection_actions: () => {
|
||||
return {
|
||||
'credential-libraries': ['create', 'list'],
|
||||
};
|
||||
},
|
||||
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,
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -1,7 +1,15 @@
|
||||
import factory from '../generated/factories/group';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('group') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
id: (i) => `group-${i}`,
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import factory from '../generated/factories/host-catalog';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('host-catalog') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
authorized_collection_actions: () => {
|
||||
return {
|
||||
hosts: ['create', 'list'],
|
||||
'host-sets': ['create', 'list'],
|
||||
};
|
||||
},
|
||||
withChildren: trait({
|
||||
afterCreate(hostCatalog, server) {
|
||||
const { scope } = hostCatalog;
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import factory from '../generated/factories/host-set';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('host-set') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
id: (i) => `host-set-id-${i}`,
|
||||
});
|
||||
@@ -1,5 +1,13 @@
|
||||
import factory from '../generated/factories/host';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('host') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
id: (i) => `host-id-${i}`,
|
||||
});
|
||||
@@ -1,7 +1,15 @@
|
||||
import factory from '../generated/factories/role';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('role') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
|
||||
grant_strings: () => [
|
||||
'id=*;action=*',
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
import factory from '../generated/factories/scope';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
type: 'global',
|
||||
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('scope') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
|
||||
authorized_collection_actions: () => {
|
||||
return {
|
||||
scopes: ['create', 'list'],
|
||||
users: ['create', 'list'],
|
||||
sessions: ['list'],
|
||||
groups: ['create', 'list'],
|
||||
roles: ['create', 'list'],
|
||||
targets: ['create', 'list'],
|
||||
'credential-stores': ['create', 'list'],
|
||||
'auth-methods': ['create', 'list'],
|
||||
'host-catalogs': ['create', 'list'],
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates realistic-ish IDs while still being deterministic.
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import factory from '../generated/factories/target';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import { random, datatype } from 'faker';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
const randomBoolean = (chance = 0.5) => Math.random() < chance;
|
||||
const hostSetChance = 0.3;
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('target') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
/**
|
||||
* -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 +33,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 +46,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,
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -1,5 +1,13 @@
|
||||
import factory from '../generated/factories/user';
|
||||
import permissions from '../helpers/permissions';
|
||||
|
||||
export default factory.extend({
|
||||
authorized_actions: () =>
|
||||
permissions.authorizedActionsFor('user') || [
|
||||
'no-op',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
id: (i) => `user-${i}`,
|
||||
});
|
||||
@@ -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,58 @@
|
||||
import { copy } from 'ember-copy';
|
||||
|
||||
/**
|
||||
* Allows development-time toggling of resource permissions via the window
|
||||
* querystring. Pass a JSON object into the query to toggle permissions.
|
||||
* Note that the value must be a valid JSON object in order to be parsed.
|
||||
*
|
||||
* @example
|
||||
* ?authorized_actions={"account": ["read"]}
|
||||
*
|
||||
* Within a Mirage factory, pass the actions into the mock:
|
||||
*
|
||||
* @example
|
||||
* import { Factory } from 'ember-cli-mirage';
|
||||
* import permissions from '../helpers/permissions';
|
||||
*
|
||||
* export default Factory.extend({
|
||||
* authorized_actions: () =>
|
||||
* permissions.authorizedActionsFor('account') ||
|
||||
* ['no-op', 'read', 'update', 'delete']
|
||||
* });
|
||||
*/
|
||||
class ToggledCapabilities {
|
||||
// =attributes
|
||||
|
||||
// Get the query string and drop the leading '?'
|
||||
#rawQuery = window.location.search.substring(1);
|
||||
|
||||
// Split the querystring into key/value pairs
|
||||
#queryPairs = this.#rawQuery.split('&').map((item) => item.split('='));
|
||||
|
||||
// Find the value of a pair with key `authorized_actions`
|
||||
// Safe-ish when the item is not present
|
||||
#rawAuthorizedActions = (this.#queryPairs.find(
|
||||
(item) => item[0] === 'authorized_actions'
|
||||
) || [])[1];
|
||||
|
||||
// Safe-ish when the value is not present
|
||||
#authorizedActions =
|
||||
JSON.parse(
|
||||
decodeURIComponent(this.#rawAuthorizedActions || '') || 'null'
|
||||
) || {};
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Returns authorized actions for the specified field, if any are defined
|
||||
* in the `authorized_actions` query. Returns undefined otherwise.
|
||||
* @return {?string[]}
|
||||
*/
|
||||
authorizedActionsFor(field) {
|
||||
if (this.#authorizedActions[field]) {
|
||||
return copy(this.#authorizedActions[field], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new ToggledCapabilities();
|
||||
@@ -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(),
|
||||
});
|
||||
@@ -3,4 +3,5 @@ import { Model, belongsTo, hasMany } from 'ember-cli-mirage';
|
||||
export default Model.extend({
|
||||
scope: belongsTo(),
|
||||
hostSets: hasMany(),
|
||||
credentialLibraries: hasMany(),
|
||||
});
|
||||
@@ -47,7 +47,9 @@ 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('credential-store', 3, { scope }, 'withAssociations');
|
||||
server.createList('target', 2, { scope }, 'withAssociations');
|
||||
// Sessions have target data. Create it after targets.
|
||||
server.createList('session', 4, { 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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -24,25 +24,26 @@
|
||||
"doc:toc": "doctoc README.md"
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-can": "^3.1.0",
|
||||
"ember-cli-babel": "^7.23.0",
|
||||
"ember-cli-htmlbars": "^5.3.1",
|
||||
"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.28.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 +60,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,38 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Collection', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a resource may be listed based on authorized_collection_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_collection_actions: {
|
||||
foobars: ['list'],
|
||||
},
|
||||
};
|
||||
assert.ok(service.can('list collection', model, { collection: 'foobars' }));
|
||||
model.authorized_collection_actions.foobars = [];
|
||||
assert.notOk(
|
||||
service.can('list collection', model, { collection: 'foobars' })
|
||||
);
|
||||
});
|
||||
|
||||
test('it reflects when a resource may be created based on authorized_collection_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_collection_actions: {
|
||||
foobars: ['create'],
|
||||
},
|
||||
};
|
||||
assert.ok(
|
||||
service.can('create collection', model, { collection: 'foobars' })
|
||||
);
|
||||
model.authorized_collection_actions.foobars = [];
|
||||
assert.notOk(
|
||||
service.can('create collection', model, { collection: 'foobars' })
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | Model', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it reflects when a given model may be read based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['read'],
|
||||
};
|
||||
assert.ok(service.can('read model', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('read model', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given model may be updated based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['update'],
|
||||
};
|
||||
assert.ok(service.can('update model', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('update model', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given model may be saved based on isNew and authorized_actions', function (assert) {
|
||||
assert.expect(4);
|
||||
const service = this.owner.lookup('service:can');
|
||||
let model = {
|
||||
isNew: false,
|
||||
authorized_actions: ['update'],
|
||||
};
|
||||
assert.ok(service.can('save model', model));
|
||||
model = {
|
||||
isNew: false,
|
||||
authorized_actions: [],
|
||||
};
|
||||
assert.notOk(service.can('save model', model));
|
||||
// Model instances are always saveable at this level when isNew is true.
|
||||
// In practice, the ability to _create_ a resource depends not on instance
|
||||
// authorization, but on collection authorization, which is
|
||||
// tested separately.
|
||||
model = {
|
||||
isNew: true,
|
||||
authorized_actions: ['update'],
|
||||
};
|
||||
assert.ok(service.can('save model', model));
|
||||
model = {
|
||||
isNew: true,
|
||||
authorized_actions: [],
|
||||
};
|
||||
assert.ok(service.can('save model', model));
|
||||
});
|
||||
|
||||
test('it reflects when a given model may be deleted based on authorized_actions', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:can');
|
||||
const model = {
|
||||
authorized_actions: ['delete'],
|
||||
};
|
||||
assert.ok(service.can('delete model', model));
|
||||
model.authorized_actions = [];
|
||||
assert.notOk(service.can('delete model', model));
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -170,6 +170,7 @@ module('Unit | Serializer | application', function (hooks) {
|
||||
id: '1',
|
||||
type: 'user',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'User 1',
|
||||
account_ids: [],
|
||||
scope: { scope_id: 'o_123' },
|
||||
@@ -200,9 +201,11 @@ module('Unit | Serializer | application', function (hooks) {
|
||||
id: '1',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
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'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -66,6 +66,7 @@ module('Unit | Serializer | group', function (hooks) {
|
||||
id: '1',
|
||||
type: 'group',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Group 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
member_ids: [],
|
||||
|
||||
@@ -67,6 +67,7 @@ module('Unit | Serializer | host set', function (hooks) {
|
||||
id: '1',
|
||||
type: 'host-set',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Host Set 1',
|
||||
host_ids: [{ value: '1' }, { value: '2' }, { value: '3' }],
|
||||
},
|
||||
@@ -96,6 +97,7 @@ module('Unit | Serializer | host set', function (hooks) {
|
||||
id: '1',
|
||||
type: 'host-set',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Host Set 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_ids: [],
|
||||
|
||||
@@ -67,6 +67,7 @@ module('Unit | Serializer | role', function (hooks) {
|
||||
id: '1',
|
||||
type: 'role',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Role 1',
|
||||
grant_strings: ['*', '*'],
|
||||
principals: [],
|
||||
@@ -93,6 +94,7 @@ module('Unit | Serializer | role', function (hooks) {
|
||||
id: '1',
|
||||
type: 'role',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Role 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
principals: [],
|
||||
|
||||
@@ -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,
|
||||
@@ -87,18 +110,20 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
id: '1',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Target 1',
|
||||
host_sets: [
|
||||
{ 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');
|
||||
@@ -119,9 +144,11 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
id: '1',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'Target 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_sets: [],
|
||||
application_credential_library_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -62,6 +62,7 @@ module('Unit | Serializer | user', function (hooks) {
|
||||
id: '1',
|
||||
type: 'user',
|
||||
attributes: {
|
||||
authorized_actions: [],
|
||||
name: 'user 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
account_ids: [],
|
||||
|
||||
@@ -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.28.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",
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
title={{t "actions.get-topic-help"}}>
|
||||
<Rose::Icon @name="help-circle-outline" @size={{@iconSize}} />
|
||||
<Rose::Icon @name="flight-icons/help-24" @size={{@iconSize}} />
|
||||
</a>
|
||||
@@ -27,13 +27,12 @@ export default class ErrorMessageComponent extends MessageComponent {
|
||||
@computed('args.status')
|
||||
get icon() {
|
||||
switch (this.args.status) {
|
||||
case '404':
|
||||
return 'flight-icons/help-16';
|
||||
case '401':
|
||||
case '403':
|
||||
return 'disabled';
|
||||
case '404':
|
||||
return 'help-circle-outline';
|
||||
default:
|
||||
return 'alert-circle-outline';
|
||||
return 'flight-icons/alert-circle-16';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
name: require('./package').name,
|
||||
|
||||
included(app) {
|
||||
this._super.included.apply(this, arguments);
|
||||
this.includePublic(app);
|
||||
this.setupSVGO(app);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the public folder and includes it into the ember-inline-svg addon.
|
||||
*/
|
||||
includePublic(app) {
|
||||
const publicPath = path.resolve('public');
|
||||
const dummyPublicPath = path.resolve('tests/dummy/public');
|
||||
|
||||
app.options.svg = app.options.svg || {};
|
||||
app.options.svg.paths = app.options.svg.paths || [];
|
||||
|
||||
app.options.svg.paths.push(publicPath);
|
||||
if (this.isDevelopingAddon()) app.options.svg.paths.push(dummyPublicPath);
|
||||
|
||||
this.addons.forEach((addon) => {
|
||||
if (addon.name === 'ember-inline-svg') addon.included(app);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Adjust SVG optimizer defaults (used by ember-inline-svg).
|
||||
*/
|
||||
setupSVGO(app) {
|
||||
app.options.svg = app.options.svg || {};
|
||||
app.options.svg.optimize = app.options.svg.optimize || {};
|
||||
app.options.svg.optimize.plugins = app.options.svg.optimize.plugins || [];
|
||||
app.options.svg.optimize.plugins.push({ removeViewBox: false });
|
||||
},
|
||||
};
|
||||
@@ -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.28.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",
|
||||
|
||||
@@ -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 |
@@ -1,3 +1,5 @@
|
||||
<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="M5.97503 7.25H10.0232C9.83952 5.33678 9.13836 3.52559 7.99998 2.00183C6.85742 3.53084 6.15589 5.3466 5.97503 7.25ZM10.025 8.75C9.84413 10.6534 9.14259 12.4692 8.00003 13.9982C6.86166 12.4744 6.16049 10.6632 5.97679 8.75H10.025ZM11.5309 8.75H14.4572C14.1478 11.4433 12.1921 13.6359 9.62187 14.296C10.707 12.6405 11.3662 10.7347 11.5309 8.75ZM14.4572 7.25H11.5293C11.3619 5.25743 10.7034 3.35494 9.62165 1.70392C12.192 2.36401 14.1478 4.55661 14.4572 7.25ZM4.46913 7.25H1.5428C1.85224 4.55668 3.80789 2.36413 6.37814 1.70397C5.29298 3.35946 4.63378 5.2653 4.46913 7.25ZM4.47076 8.75H1.5428C1.85224 11.4434 3.808 13.636 6.37836 14.2961C5.29662 12.6451 4.63811 10.7426 4.47076 8.75ZM0 8C0 3.58172 3.58172 0 8 0L8.00195 3.05531e-06C12.4193 0.00105872 16 3.58237 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(0, 0)">
|
||||
<path d="M8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0ZM6.1281 1.77359C3.76051 2.48438 1.96114 4.50867 1.57645 7L4.06861 7C4.28628 5.12647 4.99696 3.33655 6.1281 1.77359ZM9.8719 1.77359C11.003 3.33655 11.7137 5.12647 11.9314 7L14.4236 7C14.0389 4.50867 12.2395 2.48438 9.8719 1.77359ZM10.4196 7C10.1652 5.12363 9.33287 3.33469 8 1.8356C6.66712 3.33469 5.83478 5.12363 5.58044 7L10.4196 7ZM5.52655 8.5L10.4734 8.5C10.3086 10.5575 9.45081 12.5327 8 14.1644C6.54919 12.5327 5.6914 10.5575 5.52655 8.5ZM4.02241 8.5L1.51894 8.5C1.7257 11.2179 3.60398 13.4686 6.1281 14.2264C4.89671 12.5249 4.16361 10.5545 4.02241 8.5ZM9.8719 14.2264C11.1033 12.5249 11.8364 10.5545 11.9776 8.5L14.4811 8.5C14.2743 11.2179 12.396 13.4686 9.8719 14.2264Z" fill-rule="EVENODD"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 990 B After Width: | Height: | Size: 973 B |
@@ -1,3 +1,12 @@
|
||||
<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="M7.15625 12.1875H5V16H0V0H12V5.125H15.5V16H12H10.5H7.15625V12.1875ZM10.5 14.5H8.65625V10.6875H3.5V14.5H1.5V1.5H10.5V5.125V14.5ZM12 14.5H14V6.625H12V14.5ZM3 3H4.88235V5H3V3ZM4.88235 7H3V9H4.88235V7ZM7 3H8.88235V5H7V3ZM8.88235 7H7V9H8.88235V7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(1, 1)">
|
||||
<path d="M0 2.25C0 1.00736 1.00736 0 2.25 0L8.75 0C9.99264 0 11 1.00736 11 2.25L11 4.36218L13.0506 5.81469C13.6461 6.23651 14.0001 6.92096 14.0001 7.65074L14.0001 12.7501C14.0001 13.4404 13.4404 14.0001 12.7501 14.0001L10.2501 14.0001C10.0421 14.0001 9.85396 13.9154 9.71811 13.7788C9.5833 13.6432 9.5 13.4563 9.5 13.25L9.5 2.25C9.5 1.83579 9.16421 1.5 8.75 1.5L2.25 1.5C1.83579 1.5 1.5 1.83579 1.5 2.25L1.5 13.25C1.5 13.6642 1.16421 14 0.75 14C0.335786 14 0 13.6642 0 13.25L0 2.25ZM11 12.5001L11 6.20036L12.1836 7.03872C12.3821 7.17933 12.5001 7.40748 12.5001 7.65074L12.5001 12.5001L11 12.5001Z" fill-rule="EVENODD"></path>
|
||||
<path d="M3.75005 9.5498C3.36345 9.5498 3.05005 9.8632 3.05005 10.2498L3.05005 13.2498C3.05005 13.6364 3.36345 13.9498 3.75005 13.9498C4.13665 13.9498 4.45005 13.6364 4.45005 13.2498L4.45005 10.9498L6.55005 10.9498L6.55005 13.2498C6.55005 13.6364 6.86345 13.9498 7.25005 13.9498C7.63665 13.9498 7.95005 13.6364 7.95005 13.2498L7.95005 10.2498C7.95005 9.8632 7.63665 9.5498 7.25005 9.5498L3.75005 9.5498Z" fill-rule="NONZERO"></path>
|
||||
<path d="M3.25 3.75C3.25 3.33579 3.58579 3 4 3L4.25 3C4.66421 3 5 3.33579 5 3.75C5 4.16421 4.66421 4.5 4.25 4.5L4 4.5C3.58579 4.5 3.25 4.16421 3.25 3.75Z" fill-rule="NONZERO"></path>
|
||||
<path d="M6.75 3C6.33579 3 6 3.33579 6 3.75C6 4.16421 6.33579 4.5 6.75 4.5L7 4.5C7.41421 4.5 7.75 4.16421 7.75 3.75C7.75 3.33579 7.41421 3 7 3L6.75 3Z" fill-rule="NONZERO"></path>
|
||||
<path d="M3.25 5.75C3.25 5.33579 3.58579 5 4 5L4.25 5C4.66421 5 5 5.33579 5 5.75C5 6.16421 4.66421 6.5 4.25 6.5L4 6.5C3.58579 6.5 3.25 6.16421 3.25 5.75Z" fill-rule="NONZERO"></path>
|
||||
<path d="M6.75 5C6.33579 5 6 5.33579 6 5.75C6 6.16421 6.33579 6.5 6.75 6.5L7 6.5C7.41421 6.5 7.75 6.16421 7.75 5.75C7.75 5.33579 7.41421 5 7 5L6.75 5Z" fill-rule="NONZERO"></path>
|
||||
<path d="M3.25 7.75C3.25 7.33579 3.58579 7 4 7L4.25 7C4.66421 7 5 7.33579 5 7.75C5 8.16421 4.66421 8.5 4.25 8.5L4 8.5C3.58579 8.5 3.25 8.16421 3.25 7.75Z" fill-rule="NONZERO"></path>
|
||||
<path d="M6.75 7C6.33579 7 6 7.33579 6 7.75C6 8.16421 6.33579 8.5 6.75 8.5L7 8.5C7.41421 8.5 7.75 8.16421 7.75 7.75C7.75 7.33579 7.41421 7 7 7L6.75 7Z" fill-rule="NONZERO"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 2.3 KiB |
@@ -1,3 +1,8 @@
|
||||
<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="M1.5 1.5H5.50461V5.63615H1.5V1.5ZM0 7.13615V0H7.00461V7.13615H0ZM1.5 10.3639H5.50461V14.5H1.5V10.3639ZM0 16V8.86386H7.00461V16H0ZM14.5 1.5H10.4954V5.63615H14.5V1.5ZM8.99536 0V7.13615H16V0H8.99536ZM10.4954 10.3639H14.5V14.5H10.4954V10.3639ZM8.99536 16V8.86386H16V16H8.99536Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(1, 1)">
|
||||
<path d="M1.25 0C0.559644 0 0 0.559644 0 1.25L0 4.75C0 5.44036 0.559644 6 1.25 6L4.75 6C5.44036 6 6 5.44036 6 4.75L6 1.25C6 0.559644 5.44036 0 4.75 0L1.25 0ZM1.5 4.5L1.5 1.5L4.5 1.5L4.5 4.5L1.5 4.5Z" fill-rule="EVENODD"></path>
|
||||
<path d="M9.25 0C8.55964 0 8 0.559644 8 1.25L8 4.75C8 5.44036 8.55964 6 9.25 6L12.75 6C13.4404 6 14 5.44036 14 4.75L14 1.25C14 0.559644 13.4404 0 12.75 0L9.25 0ZM9.5 4.5L9.5 1.5L12.5 1.5L12.5 4.5L9.5 4.5Z" fill-rule="EVENODD"></path>
|
||||
<path d="M8 9.25C8 8.55964 8.55964 8 9.25 8L12.75 8C13.4404 8 14 8.55964 14 9.25L14 12.75C14 13.4404 13.4404 14 12.75 14L9.25 14C8.55964 14 8 13.4404 8 12.75L8 9.25ZM9.5 9.5L9.5 12.5L12.5 12.5L12.5 9.5L9.5 9.5Z" fill-rule="EVENODD"></path>
|
||||
<path d="M1.25 8C0.559644 8 0 8.55964 0 9.25L0 12.75C0 13.4404 0.559644 14 1.25 14L4.75 14C5.44036 14 6 13.4404 6 12.75L6 9.25C6 8.55964 5.44036 8 4.75 8L1.25 8ZM1.5 12.5L1.5 9.5L4.5 9.5L4.5 12.5L1.5 12.5Z" fill-rule="EVENODD"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 1.1 KiB |
@@ -33,3 +33,6 @@ edit-form: Edit Form
|
||||
change-origin: Change Origin URL
|
||||
change-state: Change State
|
||||
close: Close
|
||||
toggle-secret: Toggle secret visibility
|
||||
minimize: Minimize
|
||||
fullscreen: Fullscreen
|
||||
@@ -32,6 +32,8 @@ titles:
|
||||
descriptions:
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
origin-initialization: To get started, please enter your origin URL
|
||||
create-but-not-list: You do not have permission to list {resource}, but you can create new instances.
|
||||
neither-list-nor-create: You do not have permission to list or create {resource}.
|
||||
questions:
|
||||
delete-confirm: Are you sure you want to delete this resource?
|
||||
remove-confirm: Are you sure you want to remove this association?
|
||||
|
||||
@@ -253,18 +253,101 @@ 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
|
||||
scope:
|
||||
title: Scope
|
||||
title_plural: Scopes
|
||||
messages:
|
||||
cannot_read: While you do not have permission to read this scope, you may have permission to view resources within it.
|
||||
@@ -13,7 +13,7 @@ When style definition isn't supplied, a default style will be supplied.
|
||||
Styles: outline, dark, informational, informational-outline, success, success-outline, error, error-outline, warning, warning-outline, alert, alert-outline
|
||||
|
||||
```html
|
||||
<Rose::Badge @style="outline" @icon="user-organization">Badge</Rose::Badge>
|
||||
<Rose::Badge @style="outline" @icon="flight-icons/org-16">Badge</Rose::Badge>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
@@ -43,11 +43,9 @@ Styles: outline, dark, informational, informational-outline, success, success-ou
|
||||
'none'
|
||||
),
|
||||
icon: select('icon', {
|
||||
'user-organization': 'user-organization',
|
||||
'partner': 'partner',
|
||||
'gift-fill': 'gift-fill',
|
||||
'flight-icons/org-16': 'flight-icons/org-16',
|
||||
},
|
||||
'user-organization'
|
||||
'flight-icons/org-16'
|
||||
),
|
||||
}
|
||||
}}</Story>
|
||||
@@ -58,7 +56,7 @@ Styles: outline, dark, informational, informational-outline, success, success-ou
|
||||
template: hbs`
|
||||
<Rose::Badge>default style</Rose::Badge>
|
||||
<Rose::Badge @style="outline">outline</Rose::Badge>
|
||||
<Rose::Badge @icon="user-organization">default style with icon</Rose::Badge>
|
||||
<Rose::Badge @icon="flight-icons/org-16">default style with icon</Rose::Badge>
|
||||
<Rose::Badge @style="dark">dark</Rose::Badge>
|
||||
<Rose::Badge @style="informational">informational</Rose::Badge>
|
||||
<Rose::Badge @style="informational-outline">informational-outline</Rose::Badge>
|
||||
|
||||
@@ -12,7 +12,7 @@ to trigger actions or to submit forms. Avoid using buttons as simple links to
|
||||
other routes. For accessibility, use links instead.
|
||||
|
||||
```htmlbars
|
||||
<Rose::Button @style="primary" @iconRight="chevron-right">
|
||||
<Rose::Button @style="primary" @iconRight="flight-icons/entry-point-16">
|
||||
Action
|
||||
</Rose::Button>
|
||||
```
|
||||
@@ -49,23 +49,18 @@ other routes. For accessibility, use links instead.
|
||||
iconLeft: select(
|
||||
'iconLeft',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'check-circle-outline'
|
||||
'flight-icons/check-circle-16'
|
||||
),
|
||||
iconRight: select(
|
||||
'iconRight',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'chevron-right',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'chevron-right'
|
||||
'flight-icons/clipboard-copy-16'
|
||||
),
|
||||
onClick: action('clicked'),
|
||||
},
|
||||
@@ -86,12 +81,10 @@ button in this case for accessibility.
|
||||
iconOnly: select(
|
||||
'iconOnly',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'check-circle-outline'
|
||||
'flight-icons/check-circle-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
|
||||
@@ -13,7 +13,7 @@ Card link component wraps content with `LinkTo` component. Supports title and de
|
||||
@title='Card title'
|
||||
@description='Card description'
|
||||
@id='resource_id_1234'
|
||||
@icon='user-organization'
|
||||
@icon='flight-icons/org-16'
|
||||
@route='about'/>
|
||||
```
|
||||
|
||||
@@ -32,11 +32,9 @@ Card link component wraps content with `LinkTo` component. Supports title and de
|
||||
description: text('description', 'Card description'),
|
||||
id: text('id', 'resource_id_1234'),
|
||||
icon: select('icon', {
|
||||
'user-organization': 'user-organization',
|
||||
'partner': 'partner',
|
||||
'gift-fill': 'gift-fill',
|
||||
'flight-icons/org-16': 'flight-icons/org-16',
|
||||
},
|
||||
'user-organization'
|
||||
'flight-icons/org-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
|
||||
@@ -18,35 +18,35 @@ A set of cards arranged side-by-side.
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
<cards.link
|
||||
@route="index"
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
<cards.link
|
||||
@route="index"
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
<cards.link
|
||||
@route="index"
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
<cards.link
|
||||
@route="index"
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
</Rose::Cards>
|
||||
`,
|
||||
@@ -62,14 +62,14 @@ A set of cards arranged side-by-side.
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
<cards.link
|
||||
@route="index"
|
||||
@title="Card Title"
|
||||
@description="Description"
|
||||
@id="resource_id_1234"
|
||||
@icon="user-organization"
|
||||
@icon="flight-icons/org-16"
|
||||
/>
|
||||
</Rose::Cards>
|
||||
```
|
||||
@@ -27,7 +27,7 @@
|
||||
title={{@dismissButtonText}}
|
||||
{{on "click" (fn this.dismiss @dismiss)}}
|
||||
>
|
||||
<Rose::Icon @name="cancel-plain" @size="small" />
|
||||
<Rose::Icon @name="flight-icons/x-16" @size="small" />
|
||||
</button>
|
||||
|
||||
</header>
|
||||
@@ -62,7 +62,7 @@
|
||||
title={{@dismissButtonText}}
|
||||
{{on "click" (fn this.dismiss @dismiss)}}
|
||||
>
|
||||
<Rose::Icon @name="cancel-plain" @size="small" />
|
||||
<Rose::Icon @name="flight-icons/x-16" @size="small" />
|
||||
</button>
|
||||
|
||||
</header>
|
||||
|
||||
@@ -36,12 +36,10 @@ A dialog component with optional modal capabilities.
|
||||
icon: select(
|
||||
'icon',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less'
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'check-circle-outline'
|
||||
'flight-icons/check-circle-16'
|
||||
),
|
||||
style: select(
|
||||
'style',
|
||||
@@ -60,7 +58,7 @@ A dialog component with optional modal capabilities.
|
||||
<Rose::Dialog
|
||||
@modal={{true}}
|
||||
@dismiss={{@dismissFn}}
|
||||
@icon="check-circle-outline"
|
||||
@icon="flight-icons/check-circle-16"
|
||||
@heading="Heading"
|
||||
@dismissButtonText="Dismiss"
|
||||
as |dialog|>
|
||||
|
||||
@@ -11,7 +11,7 @@ A simple dropdown component with support for links and buttons.
|
||||
<Preview>
|
||||
<Story name="Basic Dropdown" height="7rem">{{
|
||||
template: hbs`
|
||||
<Rose::Dropdown @text="Choose One…" @icon="user-organization" @showCaret={{showCaret}} as |dropdown|>
|
||||
<Rose::Dropdown @text="Choose One…" @icon="flight-icons/org-16" @showCaret={{showCaret}} as |dropdown|>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.link @route="about">Menu item</dropdown.link>
|
||||
<dropdown.separator />
|
||||
@@ -31,7 +31,7 @@ A simple dropdown component with support for links and buttons.
|
||||
<Preview>
|
||||
<Story name="Icon-only Dropdown" height="5rem">{{
|
||||
template: hbs`
|
||||
<Rose::Dropdown @text="Choose One…" @icon="user-organization" @iconOnly={{true}} as |dropdown|>
|
||||
<Rose::Dropdown @text="Choose One…" @icon="flight-icons/org-16" @iconOnly={{true}} as |dropdown|>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.link @route="about">Menu item</dropdown.link>
|
||||
<dropdown.separator />
|
||||
@@ -117,7 +117,7 @@ Dropdown supports `danger` styling through `style` attribute.
|
||||
Dropdown indicator can be hidden using `showCaret` attribute.
|
||||
|
||||
```html
|
||||
<Rose::Dropdown @showCaret={{true}} @icon="more-horizontal" as |dropdown|>
|
||||
<Rose::Dropdown @showCaret={{true}} @icon="flight-icons/more-horizontal-16" as |dropdown|>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.button>Button menu item</dropdown.button>
|
||||
</Rose::Dropdown>
|
||||
|
||||
@@ -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>
|
||||
@@ -1,4 +1,4 @@
|
||||
<p class="rose-form-error-message" ...attributes>
|
||||
<Rose::Icon @name="cancel-square-fill" @size="medium" />
|
||||
<Rose::Icon @name="flight-icons/x-square-16" @size="medium" />
|
||||
{{yield}}
|
||||
</p>
|
||||
@@ -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,18 +61,17 @@ 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'),
|
||||
firstFieldIcon: select(
|
||||
'first icon',
|
||||
{
|
||||
'user-organization': 'user-organization',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/org-16': 'flight-icons/org-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'user-organization'
|
||||
'flight-icons/org-16'
|
||||
),
|
||||
secondFieldLabel: text('second label', 'Maybe'),
|
||||
secondFieldValue: text('second value', 'maybe'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -44,13 +44,77 @@ Renders a form radiofield.
|
||||
'icon',
|
||||
{
|
||||
'none': null,
|
||||
'user-organization': 'user-organization',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/org-16': 'flight-icons/org-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
null
|
||||
),
|
||||
}
|
||||
}}</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,
|
||||
'flight-icons/org-16': 'flight-icons/org-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
null
|
||||
),
|
||||
inline: boolean('inline', false)
|
||||
}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -27,7 +27,7 @@ A header component that allows three distinct sections to be configured: brand,
|
||||
<utilities.dropdown
|
||||
@text="user@example.net"
|
||||
@iconOnly={{true}}
|
||||
@icon="user-square-fill" as |dropdown|
|
||||
@icon="flight-icons/user-circle-16" as |dropdown|
|
||||
>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.link @route="about">Menu item</dropdown.link>
|
||||
@@ -69,13 +69,11 @@ A header component that allows three distinct sections to be configured: brand,
|
||||
dropdownIcon: select(
|
||||
'dropdownIcon',
|
||||
{
|
||||
'user-square-fill': 'user-square-fill',
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/user-circle-16': 'flight-icons/user-circle-16',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'user-square-fill'
|
||||
'flight-icons/user-circle-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
|
||||
@@ -7,9 +7,7 @@ import { text, boolean, select } from '@storybook/addon-knobs';
|
||||
|
||||
# Icon
|
||||
|
||||
Renders a simple icon from [Structure Icons][structure-icons].
|
||||
|
||||
[structure-icons]: https://github.com/hashicorp/structure-icons
|
||||
Renders a simple icon.
|
||||
|
||||
```htmlbars
|
||||
<Rose::Icon @name={{name}} @size={{size}} />
|
||||
@@ -24,12 +22,10 @@ Renders a simple icon from [Structure Icons][structure-icons].
|
||||
name: select(
|
||||
'name',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'check-circle-outline'
|
||||
'flight-icons/check-circle-16'
|
||||
),
|
||||
}
|
||||
}}</Story>
|
||||
|
||||
@@ -12,7 +12,7 @@ Styles supported: primary, secondary, warning, and ghost.
|
||||
Disabling link button is not supported as it is an action on form components only.
|
||||
|
||||
```html
|
||||
<Rose::LinkButton @route="index" @style="primary" @iconRight="chevron-right">
|
||||
<Rose::LinkButton @route="index" @style="primary" @iconRight="flight-icons/entry-point-16">
|
||||
Content
|
||||
</Rose::LinkButton>
|
||||
```
|
||||
@@ -45,21 +45,18 @@ Disabling link button is not supported as it is an action on form components onl
|
||||
iconLeft: select(
|
||||
'iconLeft',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'copy-action'
|
||||
'flight-icons/clipboard-copy-16'
|
||||
),
|
||||
iconRight: select(
|
||||
'iconRight',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'chevron-right': 'chevron-right',
|
||||
'copy-action': 'copy-action',
|
||||
'search': 'search',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/clipboard-copy-16': 'flight-icons/clipboard-copy-16',
|
||||
},
|
||||
'chevron-right'
|
||||
'flight-icons/clipboard-copy-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
@@ -68,7 +65,7 @@ Disabling link button is not supported as it is an action on form components onl
|
||||
Similar to `Rose::Button`, icon only link button is supported.
|
||||
|
||||
```html
|
||||
<Rose::LinkButton @route="index" @style="primary" @iconOnly="help-circle-outline" />
|
||||
<Rose::LinkButton @route="index" @style="primary" @iconOnly="flight-icons/help-16" />
|
||||
```
|
||||
|
||||
<Preview>
|
||||
@@ -91,12 +88,10 @@ Similar to `Rose::Button`, icon only link button is supported.
|
||||
iconOnly: select(
|
||||
'iconOnly',
|
||||
{
|
||||
'check-circle-outline': 'check-circle-outline',
|
||||
'help-circle-outline': 'help-circle-outline',
|
||||
'plus-circle-fill': 'plus-circle-fill',
|
||||
'user-square-outline': 'user-square-outline',
|
||||
'flight-icons/check-circle-16': 'flight-icons/check-circle-16',
|
||||
'flight-icons/help-16': 'flight-icons/help-16',
|
||||
},
|
||||
'help-circle-outline'
|
||||
'flight-icons/help-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
|
||||
@@ -46,7 +46,7 @@ A simple key/value list with support for embedded form fields and buttons.
|
||||
<input.field />
|
||||
</item.cell>
|
||||
<item.cell>
|
||||
<Rose::Button @style="secondary" @iconOnly="trash" title="Remove">Remove</Rose::Button>
|
||||
<Rose::Button @style="secondary" @iconOnly="flight-icons/trash-16" title="Remove">Remove</Rose::Button>
|
||||
</item.cell>
|
||||
</list.item>
|
||||
</Rose::Form::Input>
|
||||
@@ -94,7 +94,7 @@ A simple key/value list with support for embedded form fields and buttons.
|
||||
<input.field />
|
||||
</item.cell>
|
||||
<item.cell>
|
||||
<Rose::Button @style="secondary" @iconOnly="trash" title="Remove">Remove</Rose::Button>
|
||||
<Rose::Button @style="secondary" @iconOnly="flight-icons/trash-16" title="Remove">Remove</Rose::Button>
|
||||
</item.cell>
|
||||
</list.item>
|
||||
</Rose::Form::Input>
|
||||
|
||||
@@ -18,7 +18,7 @@ Title can be configured with optional icon and subtitle.
|
||||
{{description}}
|
||||
</message.description>
|
||||
<message.link @route="index">
|
||||
<Rose::Icon @name="chevron-left" />
|
||||
<Rose::Icon @name="flight-icons/chevron-left-16" />
|
||||
Go back
|
||||
</message.link>
|
||||
<message.link @route="index">
|
||||
@@ -33,24 +33,23 @@ Title can be configured with optional icon and subtitle.
|
||||
icon: select(
|
||||
'icon',
|
||||
{
|
||||
'alert-circle-outline': 'alert-circle-outline',
|
||||
'help-circle-outline': 'help-circle-outline',
|
||||
'disabled': 'disabled',
|
||||
'flight-icons/alert-circle-16': 'flight-icons/alert-circle-16',
|
||||
'flight-icons/help-16': 'flight-icons/help-16',
|
||||
},
|
||||
'help-circle-outline'
|
||||
'flight-icons/help-16'
|
||||
),
|
||||
},
|
||||
}}</Story>
|
||||
</Preview>
|
||||
|
||||
```html
|
||||
<Rose::Message @title="Page not found" @subtitle="Error 404" @icon="disabled" as |message|>
|
||||
<Rose::Message @title="Page not found" @subtitle="Error 404" @icon="flight-icons/alert-circle-16" as |message|>
|
||||
<message.description>
|
||||
You must be granted permissions to view this data. Ask your administrator
|
||||
if you think you should have access.
|
||||
</message.description>
|
||||
<message.link @route="index">
|
||||
<Rose::Icon @name="chevron-left" />
|
||||
<Rose::Icon @name="flight-icons/chevron-left-16" />
|
||||
Go back
|
||||
</message.link>
|
||||
<message.link @route="index">Need help?</message.link>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
{{on "click" @dismiss}}
|
||||
>
|
||||
<Rose::Icon
|
||||
@name="cancel-plain"
|
||||
@name="flight-icons/x-16"
|
||||
@size="medium" />
|
||||
<span class="rose-notification-dismiss-text">
|
||||
{{@dismissText}}
|
||||
|
||||