@strapi/admin 4.4.0-rc.0 → 4.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/admin/src/StrapiApp.js +12 -4
  2. package/admin/src/components/Providers/index.js +10 -14
  3. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +11 -1
  4. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +11 -7
  5. package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +6 -5
  6. package/admin/src/content-manager/components/DynamicTable/TableRows/index.js +5 -0
  7. package/admin/src/content-manager/components/DynamicTable/index.js +1 -1
  8. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +23 -17
  9. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +123 -24
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +17 -1
  11. package/admin/src/content-manager/components/FieldTypeIcon/index.js +1 -31
  12. package/admin/src/content-manager/components/Inputs/index.js +22 -36
  13. package/admin/src/content-manager/components/RelationInput/RelationInput.js +364 -0
  14. package/admin/src/content-manager/components/{SelectWrapper → RelationInput/components}/Option.js +15 -25
  15. package/admin/src/content-manager/components/RelationInput/components/Relation.js +48 -0
  16. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +52 -0
  17. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +52 -0
  18. package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
  19. package/admin/src/content-manager/components/RelationInput/index.js +1 -0
  20. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +250 -0
  21. package/admin/src/content-manager/components/RelationInputDataManager/constants.js +8 -0
  22. package/admin/src/content-manager/components/RelationInputDataManager/index.js +1 -0
  23. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/connect.js +0 -1
  24. package/admin/src/content-manager/components/RelationInputDataManager/utils/getRelationLink.js +5 -0
  25. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/index.js +1 -0
  26. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +58 -0
  27. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/select.js +31 -1
  28. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +19 -2
  29. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +7 -69
  30. package/admin/src/content-manager/hooks/useRelation/index.js +1 -0
  31. package/admin/src/content-manager/hooks/useRelation/useRelation.js +73 -0
  32. package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +4 -4
  33. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +2 -7
  34. package/admin/src/content-manager/pages/EditSettingsView/index.js +22 -51
  35. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +0 -25
  36. package/admin/src/content-manager/pages/EditView/Header/index.js +3 -90
  37. package/admin/src/content-manager/pages/EditView/Header/utils/index.js +0 -1
  38. package/admin/src/content-manager/pages/EditView/Header/utils/select.js +0 -2
  39. package/admin/src/content-manager/pages/EditView/index.js +93 -155
  40. package/admin/src/content-manager/pages/ListView/FieldPicker/index.js +0 -1
  41. package/admin/src/content-manager/pages/ListView/index.js +0 -1
  42. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +0 -1
  43. package/admin/src/content-manager/utils/formatLayoutToApi.js +1 -3
  44. package/admin/src/core/apis/index.js +0 -1
  45. package/admin/src/hooks/index.js +0 -1
  46. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +197 -215
  47. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -2
  48. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +0 -1
  49. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +36 -3
  50. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +11 -13
  51. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +2 -3
  52. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
  53. package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
  54. package/admin/src/permissions/defaultPermissions.js +6 -2
  55. package/admin/src/translations/ar.json +0 -1
  56. package/admin/src/translations/ca.json +0 -9
  57. package/admin/src/translations/cs.json +0 -2
  58. package/admin/src/translations/de.json +0 -11
  59. package/admin/src/translations/dk.json +0 -11
  60. package/admin/src/translations/en.json +0 -28
  61. package/admin/src/translations/es.json +0 -11
  62. package/admin/src/translations/fr.json +0 -11
  63. package/admin/src/translations/gu.json +0 -11
  64. package/admin/src/translations/hi.json +0 -11
  65. package/admin/src/translations/hu.json +0 -10
  66. package/admin/src/translations/id.json +0 -10
  67. package/admin/src/translations/it.json +0 -10
  68. package/admin/src/translations/ja.json +0 -11
  69. package/admin/src/translations/ko.json +0 -11
  70. package/admin/src/translations/ml.json +0 -11
  71. package/admin/src/translations/ms.json +0 -2
  72. package/admin/src/translations/nl.json +0 -11
  73. package/admin/src/translations/pl.json +0 -11
  74. package/admin/src/translations/pt-BR.json +0 -11
  75. package/admin/src/translations/pt.json +0 -1
  76. package/admin/src/translations/ru.json +0 -10
  77. package/admin/src/translations/sa.json +0 -11
  78. package/admin/src/translations/sk.json +0 -10
  79. package/admin/src/translations/sv.json +0 -9
  80. package/admin/src/translations/th.json +0 -2
  81. package/admin/src/translations/tr.json +0 -1
  82. package/admin/src/translations/uk.json +0 -2
  83. package/admin/src/translations/vi.json +0 -1
  84. package/admin/src/translations/zh-Hans.json +0 -12
  85. package/admin/src/translations/zh.json +0 -11
  86. package/build/7098.40dcd7bf.chunk.js +1 -0
  87. package/build/8851.e4ac62f2.chunk.js +158 -0
  88. package/build/{8773.c06c24c0.chunk.js → 9311.7cc03f29.chunk.js} +291 -108
  89. package/build/{Admin-authenticatedApp.9dec5230.chunk.js → Admin-authenticatedApp.e39f36c9.chunk.js} +3 -3
  90. package/build/{Admin_homePage.6d5e3236.chunk.js → Admin_homePage.118926e0.chunk.js} +1 -1
  91. package/build/{Admin_profilePage.da32abbc.chunk.js → Admin_profilePage.9d50ac44.chunk.js} +1 -1
  92. package/build/{Admin_settingsPage.98e2a62b.chunk.js → Admin_settingsPage.98a711e5.chunk.js} +16 -16
  93. package/build/admin-app.4f7618a9.chunk.js +112 -0
  94. package/build/admin-edit-roles-page.554ba3fa.chunk.js +1 -0
  95. package/build/api-tokens-create-page.4c262d6e.chunk.js +1 -0
  96. package/build/api-tokens-edit-page.10a9d368.chunk.js +1 -0
  97. package/build/api-tokens-list-page.442c9f3c.chunk.js +15 -0
  98. package/build/{ar-json.d4cb26d9.chunk.js → ar-json.3489463d.chunk.js} +1 -1
  99. package/build/{ca-json.d16c1d28.chunk.js → ca-json.a16899ae.chunk.js} +1 -1
  100. package/build/content-manager.7d57c9d1.chunk.js +1200 -0
  101. package/build/content-type-builder-list-view.8cc534e0.chunk.js +194 -0
  102. package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +1 -0
  103. package/build/content-type-builder.684df7a4.chunk.js +142 -0
  104. package/build/{cs-json.c8f28ba8.chunk.js → cs-json.ce49da5c.chunk.js} +1 -1
  105. package/build/{de-json.a9b514dc.chunk.js → de-json.aa6026b3.chunk.js} +1 -1
  106. package/build/{dk-json.09e8d145.chunk.js → dk-json.fac2bcfb.chunk.js} +1 -1
  107. package/build/en-json.0c69c7d7.chunk.js +1 -0
  108. package/build/{es-json.3a9c7c09.chunk.js → es-json.d672e181.chunk.js} +1 -1
  109. package/build/{fr-json.4ed1fc2c.chunk.js → fr-json.71a16175.chunk.js} +1 -1
  110. package/build/{gu-json.d8311297.chunk.js → gu-json.ca345cd1.chunk.js} +1 -1
  111. package/build/{hi-json.0edb8d29.chunk.js → hi-json.50c7e6d4.chunk.js} +1 -1
  112. package/build/{hu-json.7855529a.chunk.js → hu-json.e0521dcc.chunk.js} +1 -1
  113. package/build/{id-json.df9618f2.chunk.js → id-json.4b1ff8d6.chunk.js} +1 -1
  114. package/build/index.html +1 -1
  115. package/build/{it-json.a21bf078.chunk.js → it-json.86bac220.chunk.js} +1 -1
  116. package/build/{ja-json.7b0d9067.chunk.js → ja-json.4e44e36b.chunk.js} +1 -1
  117. package/build/{ko-json.983c1f8f.chunk.js → ko-json.1003756e.chunk.js} +1 -1
  118. package/build/main.b47db1a3.js +9337 -0
  119. package/build/{ml-json.8dd021c8.chunk.js → ml-json.c7774425.chunk.js} +1 -1
  120. package/build/{ms-json.836ed013.chunk.js → ms-json.ed51e902.chunk.js} +1 -1
  121. package/build/{nl-json.29d2eb37.chunk.js → nl-json.f58ea235.chunk.js} +1 -1
  122. package/build/{pl-json.1f04f00c.chunk.js → pl-json.fed96aba.chunk.js} +1 -1
  123. package/build/{pt-BR-json.b4bc8efe.chunk.js → pt-BR-json.073799ab.chunk.js} +1 -1
  124. package/build/{pt-json.c23020ab.chunk.js → pt-json.3161ca22.chunk.js} +1 -1
  125. package/build/{ru-json.7ab40ccf.chunk.js → ru-json.7ad2cbbf.chunk.js} +1 -1
  126. package/build/{runtime~main.4204f341.js → runtime~main.feeac6d3.js} +1 -1
  127. package/build/{sa-json.c5a9f4ea.chunk.js → sa-json.f0f704f0.chunk.js} +1 -1
  128. package/build/{sk-json.e4c24c4e.chunk.js → sk-json.a848961b.chunk.js} +1 -1
  129. package/build/sso-settings-page.445184e0.chunk.js +1 -0
  130. package/build/{sv-json.c3f471ae.chunk.js → sv-json.b038acbe.chunk.js} +1 -1
  131. package/build/{th-json.a59ffb32.chunk.js → th-json.72e8de3d.chunk.js} +1 -1
  132. package/build/{tr-json.276e59fe.chunk.js → tr-json.9c44ea0c.chunk.js} +1 -1
  133. package/build/{uk-json.5b5b9c27.chunk.js → uk-json.c4cd2e24.chunk.js} +1 -1
  134. package/build/{vi-json.bf3424be.chunk.js → vi-json.f7890025.chunk.js} +1 -1
  135. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.d2ea3351.chunk.js} +1 -1
  136. package/build/{zh-Hans-json.9c99f8d4.chunk.js → zh-Hans-json.03d2bda1.chunk.js} +1 -1
  137. package/build/{zh-json.451a0271.chunk.js → zh-json.3d0cc664.chunk.js} +1 -1
  138. package/package.json +7 -8
  139. package/server/bootstrap.js +1 -19
  140. package/server/config/admin-actions.js +0 -20
  141. package/server/content-types/api-token.js +1 -25
  142. package/server/content-types/index.js +0 -1
  143. package/server/controllers/api-token.js +1 -24
  144. package/server/controllers/index.js +0 -1
  145. package/server/routes/api-tokens.js +0 -11
  146. package/server/routes/index.js +0 -2
  147. package/server/services/api-token.js +29 -310
  148. package/server/services/constants.js +0 -10
  149. package/server/services/permission/engine-hooks.js +82 -0
  150. package/server/services/permission/engine.js +226 -36
  151. package/server/services/permission.js +1 -4
  152. package/server/strategies/admin.js +1 -7
  153. package/server/strategies/api-token.js +11 -71
  154. package/server/validation/api-tokens.js +2 -12
  155. package/admin/src/content-manager/components/SelectMany/ListItem.js +0 -102
  156. package/admin/src/content-manager/components/SelectMany/index.js +0 -148
  157. package/admin/src/content-manager/components/SelectOne/SingleValue.js +0 -67
  158. package/admin/src/content-manager/components/SelectOne/index.js +0 -97
  159. package/admin/src/content-manager/components/SelectWrapper/Label.js +0 -60
  160. package/admin/src/content-manager/components/SelectWrapper/index.js +0 -356
  161. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFieldButton.js +0 -135
  162. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +0 -103
  163. package/admin/src/content-manager/pages/EditView/Header/utils/getDraftRelations.js +0 -62
  164. package/admin/src/contexts/ApiTokenPermissions/index.js +0 -24
  165. package/admin/src/core/apis/CustomFields.js +0 -80
  166. package/admin/src/hooks/useRegenerate/index.js +0 -34
  167. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +0 -56
  168. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +0 -41
  169. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +0 -72
  170. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +0 -30
  171. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +0 -150
  172. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +0 -37
  173. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +0 -254
  174. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +0 -77
  175. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +0 -85
  176. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +0 -40
  177. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +0 -68
  178. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +0 -13
  179. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +0 -72
  180. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +0 -16
  181. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +0 -5
  182. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +0 -36
  183. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +0 -63
  184. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +0 -19
  185. package/build/1669.d1b29c28.chunk.js +0 -1
  186. package/build/4318.7d167b58.chunk.js +0 -30
  187. package/build/524.40377968.chunk.js +0 -644
  188. package/build/7379.d246dd38.chunk.js +0 -1
  189. package/build/admin-app.a61d5c2e.chunk.js +0 -112
  190. package/build/admin-edit-roles-page.4dd6bcb9.chunk.js +0 -1
  191. package/build/api-tokens-create-page.93dd0689.chunk.js +0 -1
  192. package/build/api-tokens-edit-page.b0adac81.chunk.js +0 -1
  193. package/build/api-tokens-list-page.bb36535f.chunk.js +0 -16
  194. package/build/content-manager.feb0d540.chunk.js +0 -1178
  195. package/build/content-type-builder-list-view.5b3cd768.chunk.js +0 -194
  196. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +0 -1
  197. package/build/content-type-builder.a684b2e8.chunk.js +0 -145
  198. package/build/en-json.a9918c93.chunk.js +0 -1
  199. package/build/main.e4065f58.js +0 -9337
  200. package/build/sso-settings-page.9ceb0140.chunk.js +0 -1
  201. package/server/content-types/api-token-permission.js +0 -36
  202. package/server/controllers/content-api.js +0 -15
  203. package/server/routes/content-api.js +0 -20
@@ -1,13 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
- const { isNil } = require('lodash/fp');
5
- const { omit, difference, isEmpty, map, isArray, uniq } = require('lodash/fp');
6
- const { ValidationError, NotFoundError } = require('@strapi/utils').errors;
7
- const constants = require('./constants');
8
4
 
9
5
  /**
10
- * @typedef {'read-only'|'full-access'|'custom'} TokenType
6
+ * @typedef {'read-only'|'full-access'} TokenType
11
7
  */
12
8
 
13
9
  /**
@@ -15,135 +11,20 @@ const constants = require('./constants');
15
11
  *
16
12
  * @property {number|string} id
17
13
  * @property {string} name
18
- * @property {string} description
14
+ * @property {string} [description]
19
15
  * @property {string} accessKey
20
- * @property {number} lastUsedAt
21
- * @property {number} lifespan
22
- * @property {number} expiresAt
23
16
  * @property {TokenType} type
24
- * @property {(number|ApiTokenPermission)[]} permissions
25
17
  */
26
18
 
27
- /**
28
- * @typedef ApiTokenPermission
29
- *
30
- * @property {number|string} id
31
- * @property {string} action
32
- * @property {ApiToken|number} token
33
- */
34
-
35
- /** @constant {Array<string>} */
36
- const SELECT_FIELDS = [
37
- 'id',
38
- 'name',
39
- 'description',
40
- 'lastUsedAt',
41
- 'type',
42
- 'lifespan',
43
- 'expiresAt',
44
- 'createdAt',
45
- 'updatedAt',
46
- ];
47
-
48
19
  /** @constant {Array<string>} */
49
- const POPULATE_FIELDS = ['permissions'];
50
-
51
- // TODO: we need to ensure the permissions are actually valid registered permissions!
52
-
53
- /**
54
- * Assert that a token's permissions attribute is valid for its type
55
- *
56
- * @param {ApiToken} token
57
- */
58
- const assertCustomTokenPermissionsValidity = (attributes) => {
59
- // Ensure non-custom tokens doesn't have permissions
60
- if (attributes.type !== constants.API_TOKEN_TYPE.CUSTOM && !isEmpty(attributes.permissions)) {
61
- throw new ValidationError('Non-custom tokens should not reference permissions');
62
- }
63
-
64
- // Custom type tokens should always have permissions attached to them
65
- if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM && !isArray(attributes.permissions)) {
66
- throw new ValidationError('Missing permissions attribute for custom token');
67
- }
68
-
69
- // Permissions provided for a custom type token should be valid/registered permissions UID
70
- if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {
71
- const validPermissions = strapi.contentAPI.permissions.providers.action.keys();
72
- const invalidPermissions = difference(attributes.permissions, validPermissions);
73
-
74
- if (!isEmpty(invalidPermissions)) {
75
- throw new ValidationError(`Unknown permissions provided: ${invalidPermissions.join(', ')}`);
76
- }
77
- }
78
- };
79
-
80
- /**
81
- * Assert that a token's permissions attribute is valid for its type
82
- *
83
- * @param {ApiToken} token
84
- */
85
- const assertValidLifespan = ({ lifespan }) => {
86
- if (isNil(lifespan)) {
87
- return;
88
- }
89
-
90
- if (!Object.values(constants.API_TOKEN_LIFESPANS).includes(lifespan)) {
91
- throw new ValidationError(
92
- `lifespan must be one of the following values:
93
- ${Object.values(constants.API_TOKEN_LIFESPANS).join(', ')}`
94
- );
95
- }
96
- };
97
-
98
- /**
99
- * Flatten a token's database permissions objects to an array of strings
100
- *
101
- * @param {ApiToken} token
102
- *
103
- * @returns {ApiToken}
104
- */
105
- const flattenTokenPermissions = (token) => {
106
- if (!token) return token;
107
- return {
108
- ...token,
109
- permissions: isArray(token.permissions) ? map('action', token.permissions) : token.permissions,
110
- };
111
- };
20
+ const SELECT_FIELDS = ['id', 'name', 'description', 'type', 'createdAt'];
112
21
 
113
22
  /**
114
- * Get a token
115
- *
116
23
  * @param {Object} whereParams
117
- * @param {string|number} whereParams.id
118
- * @param {string} whereParams.name
119
- * @param {number} whereParams.lastUsedAt
120
- * @param {string} whereParams.description
121
- * @param {string} whereParams.accessKey
122
- *
123
- * @returns {Promise<Omit<ApiToken, 'accessKey'> | null>}
124
- */
125
- const getBy = async (whereParams = {}) => {
126
- if (Object.keys(whereParams).length === 0) {
127
- return null;
128
- }
129
-
130
- const token = await strapi
131
- .query('admin::api-token')
132
- .findOne({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: whereParams });
133
-
134
- if (!token) return token;
135
- return flattenTokenPermissions(token);
136
- };
137
-
138
- /**
139
- * Check if token exists
140
- *
141
- * @param {Object} whereParams
142
- * @param {string|number} whereParams.id
143
- * @param {string} whereParams.name
144
- * @param {number} whereParams.lastUsedAt
145
- * @param {string} whereParams.description
146
- * @param {string} whereParams.accessKey
24
+ * @param {string|number} [whereParams.id]
25
+ * @param {string} [whereParams.name]
26
+ * @param {string} [whereParams.description]
27
+ * @param {string} [whereParams.accessKey]
147
28
  *
148
29
  * @returns {Promise<boolean>}
149
30
  */
@@ -154,8 +35,6 @@ const exists = async (whereParams = {}) => {
154
35
  };
155
36
 
156
37
  /**
157
- * Return a secure sha512 hash of an accessKey
158
- *
159
38
  * @param {string} accessKey
160
39
  *
161
40
  * @returns {string}
@@ -168,103 +47,24 @@ const hash = (accessKey) => {
168
47
  };
169
48
 
170
49
  /**
171
- * @param {number} lifespan
172
- *
173
- * @returns { { lifespan: null | number, expiresAt: null | number } }
174
- */
175
- const getExpirationFields = (lifespan) => {
176
- // it must be nil or a finite number >= 0
177
- const isValidNumber = Number.isFinite(lifespan) && lifespan > 0;
178
- if (!isValidNumber && !isNil(lifespan)) {
179
- throw new ValidationError('lifespan must be a positive number or null');
180
- }
181
-
182
- return {
183
- lifespan: lifespan || null,
184
- expiresAt: lifespan ? Date.now() + lifespan : null,
185
- };
186
- };
187
-
188
- /**
189
- * Create a token and its permissions
190
- *
191
50
  * @param {Object} attributes
192
51
  * @param {TokenType} attributes.type
193
52
  * @param {string} attributes.name
194
- * @param {number} attributes.lifespan
195
- * @param {string[]} attributes.permissions
196
- * @param {string} attributes.description
53
+ * @param {string} [attributes.description]
197
54
  *
198
55
  * @returns {Promise<ApiToken>}
199
56
  */
200
57
  const create = async (attributes) => {
201
58
  const accessKey = crypto.randomBytes(128).toString('hex');
202
59
 
203
- assertCustomTokenPermissionsValidity(attributes);
204
- assertValidLifespan(attributes);
205
-
206
- // Create the token
207
60
  const apiToken = await strapi.query('admin::api-token').create({
208
61
  select: SELECT_FIELDS,
209
- populate: POPULATE_FIELDS,
210
- data: {
211
- ...omit('permissions', attributes),
212
- accessKey: hash(accessKey),
213
- ...getExpirationFields(attributes.lifespan),
214
- },
215
- });
216
-
217
- const result = { ...apiToken, accessKey };
218
-
219
- // If this is a custom type token, create and the related permissions
220
- if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {
221
- // TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries
222
- // const permissionsCount = await strapi.query('admin::api-token-permission').createMany({
223
- // populate: POPULATE_FIELDS,
224
- // data: attributes.permissions.map(action => ({ action, token: apiToken })),
225
- // });
226
- await Promise.all(
227
- uniq(attributes.permissions).map((action) =>
228
- strapi.query('admin::api-token-permission').create({
229
- data: { action, token: apiToken },
230
- })
231
- )
232
- );
233
-
234
- const currentPermissions = await strapi.entityService.load(
235
- 'admin::api-token',
236
- apiToken,
237
- 'permissions'
238
- );
239
-
240
- if (currentPermissions) {
241
- Object.assign(result, { permissions: map('action', currentPermissions) });
242
- }
243
- }
244
-
245
- return result;
246
- };
247
-
248
- /**
249
- * @param {string|number} id
250
- *
251
- * @returns {Promise<ApiToken>}
252
- */
253
- const regenerate = async (id) => {
254
- const accessKey = crypto.randomBytes(128).toString('hex');
255
-
256
- const apiToken = await strapi.query('admin::api-token').update({
257
- select: ['id', 'accessKey'],
258
- where: { id },
259
62
  data: {
63
+ ...attributes,
260
64
  accessKey: hash(accessKey),
261
65
  },
262
66
  });
263
67
 
264
- if (!apiToken) {
265
- throw new NotFoundError('The provided token id does not exist');
266
- }
267
-
268
68
  return {
269
69
  ...apiToken,
270
70
  accessKey,
@@ -292,37 +92,25 @@ For security reasons, prefer storing the secret in an environment variable and r
292
92
  };
293
93
 
294
94
  /**
295
- * Return a list of all tokens and their permissions
296
- *
297
95
  * @returns {Promise<Omit<ApiToken, 'accessKey'>>}
298
96
  */
299
97
  const list = async () => {
300
- const tokens = await strapi.query('admin::api-token').findMany({
98
+ return strapi.query('admin::api-token').findMany({
301
99
  select: SELECT_FIELDS,
302
- populate: POPULATE_FIELDS,
303
100
  orderBy: { name: 'ASC' },
304
101
  });
305
-
306
- if (!tokens) return tokens;
307
- return tokens.map((token) => flattenTokenPermissions(token));
308
102
  };
309
103
 
310
104
  /**
311
- * Revoke (delete) a token
312
- *
313
105
  * @param {string|number} id
314
106
  *
315
107
  * @returns {Promise<Omit<ApiToken, 'accessKey'>>}
316
108
  */
317
109
  const revoke = async (id) => {
318
- return strapi
319
- .query('admin::api-token')
320
- .delete({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: { id } });
110
+ return strapi.query('admin::api-token').delete({ select: SELECT_FIELDS, where: { id } });
321
111
  };
322
112
 
323
113
  /**
324
- * Retrieve a token by id
325
- *
326
114
  * @param {string|number} id
327
115
  *
328
116
  * @returns {Promise<Omit<ApiToken, 'accessKey'>>}
@@ -332,8 +120,6 @@ const getById = async (id) => {
332
120
  };
333
121
 
334
122
  /**
335
- * Retrieve a token by name
336
- *
337
123
  * @param {string} name
338
124
  *
339
125
  * @returns {Promise<Omit<ApiToken, 'accessKey'>>}
@@ -343,106 +129,39 @@ const getByName = async (name) => {
343
129
  };
344
130
 
345
131
  /**
346
- * Update a token and its permissions
347
- *
348
132
  * @param {string|number} id
349
133
  * @param {Object} attributes
350
134
  * @param {TokenType} attributes.type
351
135
  * @param {string} attributes.name
352
- * @param {number} attributes.lastUsedAt
353
- * @param {string[]} attributes.permissions
354
- * @param {string} attributes.description
136
+ * @param {string} [attributes.description]
355
137
  *
356
138
  * @returns {Promise<Omit<ApiToken, 'accessKey'>>}
357
139
  */
358
140
  const update = async (id, attributes) => {
359
- // retrieve token without permissions
360
- const originalToken = await strapi.query('admin::api-token').findOne({ where: { id } });
361
-
362
- if (!originalToken) {
363
- throw new NotFoundError('Token not found');
364
- }
365
-
366
- const changingTypeToCustom =
367
- attributes.type === constants.API_TOKEN_TYPE.CUSTOM &&
368
- originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;
369
-
370
- // if we're updating the permissions on any token type, or changing from non-custom to custom, ensure they're still valid
371
- // if neither type nor permissions are changing, we don't need to validate again or else we can't allow partial update
372
- if (attributes.permissions || changingTypeToCustom) {
373
- assertCustomTokenPermissionsValidity({
374
- ...originalToken,
375
- ...attributes,
376
- type: attributes.type || originalToken.type,
377
- });
378
- }
379
-
380
- assertValidLifespan(attributes);
381
-
382
- const updatedToken = await strapi.query('admin::api-token').update({
383
- select: SELECT_FIELDS,
384
- populate: POPULATE_FIELDS,
385
- where: { id },
386
- data: omit('permissions', attributes),
387
- });
388
-
389
- // custom tokens need to have their permissions updated as well
390
- if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && attributes.permissions) {
391
- const currentPermissionsResult = await strapi.entityService.load(
392
- 'admin::api-token',
393
- updatedToken,
394
- 'permissions'
395
- );
396
-
397
- const currentPermissions = map('action', currentPermissionsResult || []);
398
- const newPermissions = uniq(attributes.permissions);
399
-
400
- const actionsToDelete = difference(currentPermissions, newPermissions);
401
- const actionsToAdd = difference(newPermissions, currentPermissions);
402
-
403
- // TODO: improve efficiency here
404
- // method using a loop -- works but very inefficient
405
- await Promise.all(
406
- actionsToDelete.map((action) =>
407
- strapi.query('admin::api-token-permission').delete({
408
- where: { action, token: id },
409
- })
410
- )
411
- );
141
+ return strapi
142
+ .query('admin::api-token')
143
+ .update({ where: { id }, data: attributes, select: SELECT_FIELDS });
144
+ };
412
145
 
413
- // TODO: improve efficiency here
414
- // using a loop -- works but very inefficient
415
- await Promise.all(
416
- actionsToAdd.map((action) =>
417
- strapi.query('admin::api-token-permission').create({
418
- data: { action, token: id },
419
- })
420
- )
421
- );
422
- }
423
- // if type is not custom, make sure any old permissions get removed
424
- else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {
425
- await strapi.query('admin::api-token-permission').delete({
426
- where: { token: id },
427
- });
146
+ /**
147
+ * @param {Object} whereParams
148
+ * @param {string|number} [whereParams.id]
149
+ * @param {string} [whereParams.name]
150
+ * @param {string} [whereParams.description]
151
+ * @param {string} [whereParams.accessKey]
152
+ *
153
+ * @returns {Promise<Omit<ApiToken, 'accessKey'> | null>}
154
+ */
155
+ const getBy = async (whereParams = {}) => {
156
+ if (Object.keys(whereParams).length === 0) {
157
+ return null;
428
158
  }
429
159
 
430
- // retrieve permissions
431
- const permissionsFromDb = await strapi.entityService.load(
432
- 'admin::api-token',
433
- updatedToken,
434
- 'permissions'
435
- );
436
-
437
- return {
438
- ...updatedToken,
439
- permissions: permissionsFromDb ? permissionsFromDb.map((p) => p.action) : undefined,
440
- };
160
+ return strapi.query('admin::api-token').findOne({ select: SELECT_FIELDS, where: whereParams });
441
161
  };
442
162
 
443
163
  module.exports = {
444
164
  create,
445
- regenerate,
446
165
  exists,
447
166
  checkSaltIsDefined,
448
167
  hash,
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const DAY_IN_MS = 24 * 60 * 60 * 1000;
4
-
5
3
  module.exports = {
6
4
  CONTENT_TYPE_SECTION: 'contentTypes',
7
5
  SUPER_ADMIN_CODE: 'strapi-super-admin',
@@ -15,13 +13,5 @@ module.exports = {
15
13
  API_TOKEN_TYPE: {
16
14
  READ_ONLY: 'read-only',
17
15
  FULL_ACCESS: 'full-access',
18
- CUSTOM: 'custom',
19
- },
20
- // The front-end only displays these values
21
- API_TOKEN_LIFESPANS: {
22
- UNLIMITED: null,
23
- DAYS_7: 7 * DAY_IN_MS,
24
- DAYS_30: 30 * DAY_IN_MS,
25
- DAYS_90: 90 * DAY_IN_MS,
26
16
  },
27
17
  };
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const { cloneDeep, has } = require('lodash/fp');
4
+ const { hooks } = require('@strapi/utils');
5
+
6
+ const permissionDomain = require('../../domain/permission');
7
+
8
+ /**
9
+ * Create a hook map used by the permission Engine
10
+ */
11
+ const createEngineHooks = () => ({
12
+ willEvaluatePermission: hooks.createAsyncSeriesHook(),
13
+ willRegisterPermission: hooks.createAsyncSeriesHook(),
14
+ });
15
+
16
+ /**
17
+ * Create a context from a domain {@link Permission} used by the WillEvaluate hook
18
+ * @param {Permission} permission
19
+ * @return {{readonly permission: Permission, addCondition(string): this}}
20
+ */
21
+ const createWillEvaluateContext = (permission) => ({
22
+ get permission() {
23
+ return cloneDeep(permission);
24
+ },
25
+
26
+ addCondition(condition) {
27
+ Object.assign(permission, permissionDomain.addCondition(condition, permission));
28
+
29
+ return this;
30
+ },
31
+ });
32
+
33
+ /**
34
+ * Create a context from a casl Permission & some options
35
+ * @param caslPermission
36
+ * @param {object} options
37
+ * @param {Permission} options.permission
38
+ * @param {object} options.user
39
+ */
40
+ const createWillRegisterContext = (caslPermission, { permission, user }) => ({
41
+ get permission() {
42
+ return cloneDeep(permission);
43
+ },
44
+
45
+ get user() {
46
+ return cloneDeep(user);
47
+ },
48
+
49
+ condition: {
50
+ and(rawConditionObject) {
51
+ if (!caslPermission.condition) {
52
+ Object.assign(caslPermission, { condition: { $and: [] } });
53
+ }
54
+
55
+ caslPermission.condition.$and.push(rawConditionObject);
56
+
57
+ return this;
58
+ },
59
+
60
+ or(rawConditionObject) {
61
+ if (!caslPermission.condition) {
62
+ Object.assign(caslPermission, { condition: { $and: [] } });
63
+ }
64
+
65
+ const orClause = caslPermission.condition.$and.find(has('$or'));
66
+
67
+ if (orClause) {
68
+ orClause.$or.push(rawConditionObject);
69
+ } else {
70
+ caslPermission.condition.$and.push({ $or: [rawConditionObject] });
71
+ }
72
+
73
+ return this;
74
+ },
75
+ },
76
+ });
77
+
78
+ module.exports = {
79
+ createEngineHooks,
80
+ createWillEvaluateContext,
81
+ createWillRegisterContext,
82
+ };