@strapi/admin 4.7.0-exp.3d6a31eb083e9d44afcf98f68c107fb7567e5720 → 4.7.1
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.
- package/admin/src/components/Notifications/Notification/index.js +8 -1
- package/admin/src/hooks/index.js +2 -0
- package/admin/src/hooks/useLicenseLimitNotification/index.js +5 -0
- package/admin/src/hooks/useLicenseLimits/index.js +3 -0
- package/admin/src/pages/HomePage/index.js +2 -0
- package/admin/src/pages/SettingsPage/components/Tokens/LifeSpanInput/index.js +1 -1
- package/admin/src/pages/SettingsPage/components/Tokens/Regenerate/index.js +5 -5
- package/admin/src/pages/SettingsPage/components/Tokens/Table/index.js +1 -1
- package/admin/src/pages/SettingsPage/components/Tokens/TokenBox/index.js +1 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +4 -4
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +5 -5
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +2 -2
- package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +5 -0
- package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +2 -0
- package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/utils/tableHeaders.js +4 -4
- package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +1 -4
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +24 -0
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +4 -1
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +25 -31
- package/admin/src/translations/ca.json +6 -6
- package/admin/src/translations/de.json +6 -6
- package/admin/src/translations/dk.json +6 -6
- package/admin/src/translations/en.json +209 -184
- package/admin/src/translations/es.json +6 -6
- package/admin/src/translations/eu.json +19 -19
- package/admin/src/translations/fr.json +6 -6
- package/admin/src/translations/hi.json +6 -6
- package/admin/src/translations/hu.json +19 -19
- package/admin/src/translations/ja.json +6 -6
- package/admin/src/translations/ko.json +6 -6
- package/admin/src/translations/ml.json +6 -6
- package/admin/src/translations/nl.json +19 -19
- package/admin/src/translations/pl.json +6 -6
- package/admin/src/translations/pt-BR.json +6 -6
- package/admin/src/translations/ru.json +865 -785
- package/admin/src/translations/sa.json +6 -6
- package/admin/src/translations/sk.json +2 -2
- package/admin/src/translations/sv.json +19 -19
- package/admin/src/translations/tr.json +19 -19
- package/admin/src/translations/zh-Hans.json +6 -6
- package/admin/src/translations/zh.json +19 -19
- package/build/4649.ffa2f59a.chunk.js +30 -0
- package/build/{6487.83e219bf.chunk.js → 6891.ef7464be.chunk.js} +6 -6
- package/build/7259.cd2f7bad.chunk.js +1 -0
- package/build/{4849.7abafe52.chunk.js → 8252.ad0478da.chunk.js} +2 -2
- package/build/{Admin-authenticatedApp.368164a1.chunk.js → Admin-authenticatedApp.09978a81.chunk.js} +6 -6
- package/build/Admin_homePage.cec3f510.chunk.js +70 -0
- package/build/Admin_settingsPage.f6d02df6.chunk.js +178 -0
- package/build/admin-app.fdab907e.chunk.js +112 -0
- package/build/admin-edit-users.f06c4a53.chunk.js +10 -0
- package/build/admin-users.085fd03f.chunk.js +11 -0
- package/build/audit-logs-settings-page.7be97e82.chunk.js +1 -0
- package/build/ca-json.59c4502c.chunk.js +1 -0
- package/build/de-json.dbc2cf1b.chunk.js +1 -0
- package/build/dk-json.52f67b15.chunk.js +1 -0
- package/build/en-json.e688dfe2.chunk.js +1 -0
- package/build/es-json.c40c57dd.chunk.js +1 -0
- package/build/eu-json.6702a0d2.chunk.js +1 -0
- package/build/fr-json.ea9ec573.chunk.js +1 -0
- package/build/hi-json.14a17920.chunk.js +1 -0
- package/build/hu-json.33172d09.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/ja-json.3008b720.chunk.js +1 -0
- package/build/ko-json.7d2f95b1.chunk.js +1 -0
- package/build/{main.8009bfe8.js → main.3d8a17d5.js} +1 -1
- package/build/ml-json.3e69969b.chunk.js +1 -0
- package/build/nl-json.641782d5.chunk.js +1 -0
- package/build/pl-json.05814145.chunk.js +1 -0
- package/build/pt-BR-json.d72350de.chunk.js +1 -0
- package/build/ru-json.c4a4f50b.chunk.js +1 -0
- package/build/{runtime~main.725b20df.js → runtime~main.f7bcdaa0.js} +2 -2
- package/build/sa-json.e5e7ccaf.chunk.js +1 -0
- package/build/{sk-json.7bbeb0af.chunk.js → sk-json.3529b8aa.chunk.js} +1 -1
- package/build/sv-json.207afc0d.chunk.js +1 -0
- package/build/tr-json.f1a0d19d.chunk.js +1 -0
- package/build/{transfer-tokens-list-page.1e15926d.chunk.js → transfer-tokens-list-page.c6f8039a.chunk.js} +1 -1
- package/build/zh-Hans-json.6e26e359.chunk.js +1 -0
- package/build/zh-json.085a34f4.chunk.js +1 -0
- package/ee/admin/hooks/index.js +2 -0
- package/ee/admin/hooks/useLicenseLimitNotification/index.js +88 -0
- package/ee/admin/hooks/useLicenseLimits/index.js +31 -0
- package/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +88 -0
- package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/Modal/ActionBody.js +1 -1
- package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/utils/getDisplayedFilters.js +21 -10
- package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/utils/tableHeaders.js +1 -1
- package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +52 -0
- package/ee/server/bootstrap.js +1 -1
- package/ee/server/controllers/admin.js +49 -0
- package/ee/server/controllers/index.js +1 -0
- package/ee/server/controllers/user.js +96 -4
- package/ee/server/routes/index.js +23 -0
- package/ee/server/services/audit-logs.js +15 -5
- package/ee/server/services/index.js +2 -0
- package/ee/server/services/seat-enforcement.js +114 -0
- package/ee/server/services/user.js +234 -0
- package/package.json +9 -9
- package/server/middlewares/data-transfer.js +26 -0
- package/server/middlewares/index.js +1 -0
- package/server/routes/transfer.js +7 -9
- package/server/services/transfer/index.js +1 -0
- package/server/services/transfer/token.js +18 -3
- package/server/services/transfer/utils.js +38 -0
- package/admin/src/pages/SettingsPage/components/Tokens/FormiTokenContainer/LifeSpanInput.js +0 -95
- package/build/4649.b7e84a29.chunk.js +0 -30
- package/build/7259.3f04094f.chunk.js +0 -1
- package/build/Admin_homePage.1f10437f.chunk.js +0 -78
- package/build/Admin_settingsPage.5a329b58.chunk.js +0 -178
- package/build/admin-app.df9adf93.chunk.js +0 -112
- package/build/admin-edit-users.08a60ea2.chunk.js +0 -10
- package/build/admin-users.74f5629d.chunk.js +0 -11
- package/build/audit-logs-settings-page.bc1784fe.chunk.js +0 -1
- package/build/ca-json.4d999055.chunk.js +0 -1
- package/build/de-json.866f8a28.chunk.js +0 -1
- package/build/dk-json.10f7b1d1.chunk.js +0 -1
- package/build/en-json.8e5451b1.chunk.js +0 -1
- package/build/es-json.ea15c957.chunk.js +0 -1
- package/build/eu-json.3bc24d60.chunk.js +0 -1
- package/build/fr-json.e88fbdfd.chunk.js +0 -1
- package/build/hi-json.df3a7be2.chunk.js +0 -1
- package/build/hu-json.680e6eef.chunk.js +0 -1
- package/build/ja-json.97ee41ba.chunk.js +0 -1
- package/build/ko-json.4cbbf4f2.chunk.js +0 -1
- package/build/ml-json.e3747091.chunk.js +0 -1
- package/build/nl-json.371a15ee.chunk.js +0 -1
- package/build/pl-json.e535cbce.chunk.js +0 -1
- package/build/pt-BR-json.e5fafa46.chunk.js +0 -1
- package/build/ru-json.866f0ff1.chunk.js +0 -1
- package/build/sa-json.7efeb257.chunk.js +0 -1
- package/build/sv-json.dc40951f.chunk.js +0 -1
- package/build/tr-json.b79eae31.chunk.js +0 -1
- package/build/zh-Hans-json.30a18940.chunk.js +0 -1
- package/build/zh-json.49d84433.chunk.js +0 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { pipe, map, castArray, toNumber } = require('lodash/fp');
|
|
5
|
+
const { stringIncludes } = require('@strapi/utils');
|
|
6
|
+
const { ValidationError } = require('@strapi/utils').errors;
|
|
7
|
+
const { hasSuperAdminRole } = require('../../../server/domain/user');
|
|
8
|
+
const { getService } = require('../../../server/utils');
|
|
9
|
+
const { SUPER_ADMIN_CODE } = require('../../../server/services/constants');
|
|
10
|
+
|
|
11
|
+
/** Checks if ee disabled users list needs to be updated
|
|
12
|
+
* @param {string} id
|
|
13
|
+
* @param {object} input
|
|
14
|
+
*/
|
|
15
|
+
const updateEEDisabledUsersList = async (id, input) => {
|
|
16
|
+
const disabledUsers = await getService('seat-enforcement').getDisabledUserList();
|
|
17
|
+
|
|
18
|
+
if (!disabledUsers) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const user = disabledUsers.find((user) => user.id === Number(id));
|
|
23
|
+
if (!user) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (user.isActive !== input.isActive) {
|
|
28
|
+
const newDisabledUsersList = disabledUsers.filter((user) => user.id !== Number(id));
|
|
29
|
+
await strapi.store.set({
|
|
30
|
+
type: 'ee',
|
|
31
|
+
key: 'disabled_users',
|
|
32
|
+
value: newDisabledUsersList,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const castNumberArray = pipe(castArray, map(toNumber));
|
|
38
|
+
|
|
39
|
+
const removeFromEEDisabledUsersList = async (ids) => {
|
|
40
|
+
let idsToCheck;
|
|
41
|
+
if (typeof ids === 'object') {
|
|
42
|
+
idsToCheck = castNumberArray(ids);
|
|
43
|
+
} else {
|
|
44
|
+
idsToCheck = [Number(ids)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const disabledUsers = await getService('seat-enforcement').getDisabledUserList();
|
|
48
|
+
|
|
49
|
+
if (!disabledUsers) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const newDisabledUsersList = disabledUsers.filter((user) => !idsToCheck.includes(user.id));
|
|
54
|
+
await strapi.store.set({
|
|
55
|
+
type: 'ee',
|
|
56
|
+
key: 'disabled_users',
|
|
57
|
+
value: newDisabledUsersList,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Update a user in database
|
|
63
|
+
* @param id query params to find the user to update
|
|
64
|
+
* @param attributes A partial user object
|
|
65
|
+
* @returns {Promise<user>}
|
|
66
|
+
*/
|
|
67
|
+
const updateById = async (id, attributes) => {
|
|
68
|
+
// Check at least one super admin remains
|
|
69
|
+
if (_.has(attributes, 'roles')) {
|
|
70
|
+
const lastAdminUser = await isLastSuperAdminUser(id);
|
|
71
|
+
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
|
|
72
|
+
const willRemoveSuperAdminRole = !stringIncludes(attributes.roles, superAdminRole.id);
|
|
73
|
+
|
|
74
|
+
if (lastAdminUser && willRemoveSuperAdminRole) {
|
|
75
|
+
throw new ValidationError('You must have at least one user with super admin role.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// cannot disable last super admin
|
|
80
|
+
if (attributes.isActive === false) {
|
|
81
|
+
const lastAdminUser = await isLastSuperAdminUser(id);
|
|
82
|
+
if (lastAdminUser) {
|
|
83
|
+
throw new ValidationError('You must have at least one user with super admin role.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// hash password if a new one is sent
|
|
88
|
+
if (_.has(attributes, 'password')) {
|
|
89
|
+
const hashedPassword = await getService('auth').hashPassword(attributes.password);
|
|
90
|
+
|
|
91
|
+
const updatedUser = await strapi.query('admin::user').update({
|
|
92
|
+
where: { id },
|
|
93
|
+
data: {
|
|
94
|
+
...attributes,
|
|
95
|
+
password: hashedPassword,
|
|
96
|
+
},
|
|
97
|
+
populate: ['roles'],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
strapi.eventHub.emit('user.update', { user: sanitizeUser(updatedUser) });
|
|
101
|
+
|
|
102
|
+
return updatedUser;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const updatedUser = await strapi.query('admin::user').update({
|
|
106
|
+
where: { id },
|
|
107
|
+
data: attributes,
|
|
108
|
+
populate: ['roles'],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await updateEEDisabledUsersList(id, attributes);
|
|
112
|
+
|
|
113
|
+
if (updatedUser) {
|
|
114
|
+
strapi.eventHub.emit('user.update', { user: sanitizeUser(updatedUser) });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return updatedUser;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** Delete a user
|
|
121
|
+
* @param id id of the user to delete
|
|
122
|
+
* @returns {Promise<user>}
|
|
123
|
+
*/
|
|
124
|
+
const deleteById = async (id) => {
|
|
125
|
+
// Check at least one super admin remains
|
|
126
|
+
const userToDelete = await strapi.query('admin::user').findOne({
|
|
127
|
+
where: { id },
|
|
128
|
+
populate: ['roles'],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!userToDelete) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (userToDelete) {
|
|
136
|
+
if (userToDelete.roles.some((r) => r.code === SUPER_ADMIN_CODE)) {
|
|
137
|
+
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
|
|
138
|
+
if (superAdminRole.usersCount === 1) {
|
|
139
|
+
throw new ValidationError('You must have at least one user with super admin role.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const deletedUser = await strapi
|
|
145
|
+
.query('admin::user')
|
|
146
|
+
.delete({ where: { id }, populate: ['roles'] });
|
|
147
|
+
|
|
148
|
+
await removeFromEEDisabledUsersList(id);
|
|
149
|
+
|
|
150
|
+
strapi.eventHub.emit('user.delete', { user: sanitizeUser(deletedUser) });
|
|
151
|
+
|
|
152
|
+
return deletedUser;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/** Delete a user
|
|
156
|
+
* @param ids ids of the users to delete
|
|
157
|
+
* @returns {Promise<user>}
|
|
158
|
+
*/
|
|
159
|
+
const deleteByIds = async (ids) => {
|
|
160
|
+
// Check at least one super admin remains
|
|
161
|
+
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
|
|
162
|
+
const nbOfSuperAdminToDelete = await strapi.query('admin::user').count({
|
|
163
|
+
where: {
|
|
164
|
+
id: ids,
|
|
165
|
+
roles: { id: superAdminRole.id },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (superAdminRole.usersCount === nbOfSuperAdminToDelete) {
|
|
170
|
+
throw new ValidationError('You must have at least one user with super admin role.');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const deletedUsers = [];
|
|
174
|
+
for (const id of ids) {
|
|
175
|
+
const deletedUser = await strapi.query('admin::user').delete({
|
|
176
|
+
where: { id },
|
|
177
|
+
populate: ['roles'],
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
deletedUsers.push(deletedUser);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await removeFromEEDisabledUsersList(ids);
|
|
184
|
+
|
|
185
|
+
strapi.eventHub.emit('user.delete', {
|
|
186
|
+
users: deletedUsers.map((deletedUser) => sanitizeUser(deletedUser)),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return deletedUsers;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const sanitizeUserRoles = (role) => _.pick(role, ['id', 'name', 'description', 'code']);
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check if a user is the last super admin
|
|
196
|
+
* @param {int|string} userId user's id to look for
|
|
197
|
+
*/
|
|
198
|
+
const isLastSuperAdminUser = async (userId) => {
|
|
199
|
+
const user = await findOne(userId);
|
|
200
|
+
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
|
|
201
|
+
|
|
202
|
+
return superAdminRole.usersCount === 1 && hasSuperAdminRole(user);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Remove private user fields
|
|
207
|
+
* @param {Object} user - user to sanitize
|
|
208
|
+
*/
|
|
209
|
+
const sanitizeUser = (user) => {
|
|
210
|
+
return {
|
|
211
|
+
..._.omit(user, ['password', 'resetPasswordToken', 'registrationToken', 'roles']),
|
|
212
|
+
roles: user.roles && user.roles.map(sanitizeUserRoles),
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Find one user
|
|
218
|
+
*/
|
|
219
|
+
const findOne = async (id, populate = ['roles']) => {
|
|
220
|
+
return strapi.entityService.findOne('admin::user', id, { populate });
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const getCurrentActiveUserCount = async () => {
|
|
224
|
+
return strapi.db.query('admin::user').count({ where: { isActive: true } });
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
updateEEDisabledUsersList,
|
|
229
|
+
removeFromEEDisabledUsersList,
|
|
230
|
+
getCurrentActiveUserCount,
|
|
231
|
+
deleteByIds,
|
|
232
|
+
deleteById,
|
|
233
|
+
updateById,
|
|
234
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/admin",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "Strapi Admin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,15 +46,15 @@
|
|
|
46
46
|
"@casl/ability": "^5.4.3",
|
|
47
47
|
"@fingerprintjs/fingerprintjs": "3.3.6",
|
|
48
48
|
"@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
|
|
49
|
-
"@strapi/babel-plugin-switch-ee-ce": "4.7.
|
|
50
|
-
"@strapi/data-transfer": "4.7.
|
|
49
|
+
"@strapi/babel-plugin-switch-ee-ce": "4.7.1",
|
|
50
|
+
"@strapi/data-transfer": "4.7.1",
|
|
51
51
|
"@strapi/design-system": "1.6.3",
|
|
52
|
-
"@strapi/helper-plugin": "4.7.
|
|
52
|
+
"@strapi/helper-plugin": "4.7.1",
|
|
53
53
|
"@strapi/icons": "1.6.3",
|
|
54
|
-
"@strapi/permissions": "4.7.
|
|
55
|
-
"@strapi/provider-audit-logs-local": "4.7.
|
|
56
|
-
"@strapi/typescript-utils": "4.7.
|
|
57
|
-
"@strapi/utils": "4.7.
|
|
54
|
+
"@strapi/permissions": "4.7.1",
|
|
55
|
+
"@strapi/provider-audit-logs-local": "4.7.1",
|
|
56
|
+
"@strapi/typescript-utils": "4.7.1",
|
|
57
|
+
"@strapi/utils": "4.7.1",
|
|
58
58
|
"axios": "1.2.2",
|
|
59
59
|
"babel-loader": "^9.1.2",
|
|
60
60
|
"babel-plugin-styled-components": "2.0.2",
|
|
@@ -165,5 +165,5 @@
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
},
|
|
168
|
-
"gitHead": "
|
|
168
|
+
"gitHead": "0307fb4bf7b006c4cd902412967d3987d7810ed8"
|
|
169
169
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getService } = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = () => async (ctx, next) => {
|
|
6
|
+
const transferUtils = getService('transfer').utils;
|
|
7
|
+
|
|
8
|
+
const { hasValidTokenSalt, isDataTransferEnabled, isDisabledFromEnv } = transferUtils;
|
|
9
|
+
|
|
10
|
+
if (isDataTransferEnabled()) {
|
|
11
|
+
return next();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!hasValidTokenSalt()) {
|
|
15
|
+
return ctx.notImplemented(
|
|
16
|
+
'The server configuration for data transfer is invalid. Please contact your server administrator.'
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isDisabledFromEnv()) {
|
|
21
|
+
return ctx.notFound();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// This should never happen as long as we're handling individual scenarios above
|
|
25
|
+
throw new Error('Unexpected error while trying to access a data transfer route');
|
|
26
|
+
};
|
|
@@ -9,15 +9,7 @@ module.exports = [
|
|
|
9
9
|
path: '/transfer/runner/connect',
|
|
10
10
|
handler: 'transfer.runner-connect',
|
|
11
11
|
config: {
|
|
12
|
-
middlewares: [
|
|
13
|
-
(ctx, next) => {
|
|
14
|
-
if (process.env.STRAPI_DISABLE_REMOTE_DATA_TRANSFER === 'true') {
|
|
15
|
-
return ctx.notFound();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return next();
|
|
19
|
-
},
|
|
20
|
-
],
|
|
12
|
+
middlewares: ['admin::data-transfer'],
|
|
21
13
|
// TODO: Allow not passing any scope <> Add a way to prevent assigning one by default
|
|
22
14
|
auth: { strategies: [dataTransferAuthStrategy], scope: ['push'] },
|
|
23
15
|
},
|
|
@@ -28,6 +20,7 @@ module.exports = [
|
|
|
28
20
|
path: '/transfer/tokens',
|
|
29
21
|
handler: 'transfer.token-create',
|
|
30
22
|
config: {
|
|
23
|
+
middlewares: ['admin::data-transfer'],
|
|
31
24
|
policies: [
|
|
32
25
|
'admin::isAuthenticatedAdmin',
|
|
33
26
|
{ name: 'admin::hasPermissions', config: { actions: ['admin::transfer.tokens.create'] } },
|
|
@@ -39,6 +32,7 @@ module.exports = [
|
|
|
39
32
|
path: '/transfer/tokens',
|
|
40
33
|
handler: 'transfer.token-list',
|
|
41
34
|
config: {
|
|
35
|
+
middlewares: ['admin::data-transfer'],
|
|
42
36
|
policies: [
|
|
43
37
|
'admin::isAuthenticatedAdmin',
|
|
44
38
|
{ name: 'admin::hasPermissions', config: { actions: ['admin::transfer.tokens.read'] } },
|
|
@@ -50,6 +44,7 @@ module.exports = [
|
|
|
50
44
|
path: '/transfer/tokens/:id',
|
|
51
45
|
handler: 'transfer.token-revoke',
|
|
52
46
|
config: {
|
|
47
|
+
middlewares: ['admin::data-transfer'],
|
|
53
48
|
policies: [
|
|
54
49
|
'admin::isAuthenticatedAdmin',
|
|
55
50
|
{ name: 'admin::hasPermissions', config: { actions: ['admin::transfer.tokens.delete'] } },
|
|
@@ -61,6 +56,7 @@ module.exports = [
|
|
|
61
56
|
path: '/transfer/tokens/:id',
|
|
62
57
|
handler: 'transfer.token-getById',
|
|
63
58
|
config: {
|
|
59
|
+
middlewares: ['admin::data-transfer'],
|
|
64
60
|
policies: [
|
|
65
61
|
'admin::isAuthenticatedAdmin',
|
|
66
62
|
{ name: 'admin::hasPermissions', config: { actions: ['admin::transfer.tokens.read'] } },
|
|
@@ -72,6 +68,7 @@ module.exports = [
|
|
|
72
68
|
path: '/transfer/tokens/:id',
|
|
73
69
|
handler: 'transfer.token-update',
|
|
74
70
|
config: {
|
|
71
|
+
middlewares: ['admin::data-transfer'],
|
|
75
72
|
policies: [
|
|
76
73
|
'admin::isAuthenticatedAdmin',
|
|
77
74
|
{ name: 'admin::hasPermissions', config: { actions: ['admin::transfer.tokens.update'] } },
|
|
@@ -83,6 +80,7 @@ module.exports = [
|
|
|
83
80
|
path: '/transfer/tokens/:id/regenerate',
|
|
84
81
|
handler: 'transfer.token-regenerate',
|
|
85
82
|
config: {
|
|
83
|
+
middlewares: ['admin::data-transfer'],
|
|
86
84
|
policies: [
|
|
87
85
|
'admin::isAuthenticatedAdmin',
|
|
88
86
|
{
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
} = require('@strapi/utils');
|
|
9
9
|
|
|
10
10
|
const constants = require('../constants');
|
|
11
|
+
const { getService } = require('../../utils');
|
|
11
12
|
|
|
12
13
|
const TRANSFER_TOKEN_UID = 'admin::transfer-token';
|
|
13
14
|
const TRANSFER_TOKEN_PERMISSION_UID = 'admin::transfer-token-permission';
|
|
@@ -327,6 +328,12 @@ const getExpirationFields = (lifespan) => {
|
|
|
327
328
|
* @returns {string}
|
|
328
329
|
*/
|
|
329
330
|
const hash = (accessKey) => {
|
|
331
|
+
const { hasValidTokenSalt } = getService('transfer').utils;
|
|
332
|
+
|
|
333
|
+
if (!hasValidTokenSalt()) {
|
|
334
|
+
throw new TypeError('Required token salt is not defined');
|
|
335
|
+
}
|
|
336
|
+
|
|
330
337
|
return crypto
|
|
331
338
|
.createHmac('sha512', strapi.config.get('admin.transfer.token.salt'))
|
|
332
339
|
.update(accessKey)
|
|
@@ -337,9 +344,17 @@ const hash = (accessKey) => {
|
|
|
337
344
|
* @returns {void}
|
|
338
345
|
*/
|
|
339
346
|
const checkSaltIsDefined = () => {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
347
|
+
const { hasValidTokenSalt, isDisabledFromEnv } = getService('transfer').utils;
|
|
348
|
+
|
|
349
|
+
// Ignore the check if the data-transfer feature is manually disabled
|
|
350
|
+
if (isDisabledFromEnv()) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!hasValidTokenSalt()) {
|
|
355
|
+
process.emitWarning(
|
|
356
|
+
`Missing transfer.token.salt: Data transfer features have been disabled.
|
|
357
|
+
Please set transfer.token.salt in config/admin.js (ex: you can generate one using Node with \`crypto.randomBytes(16).toString('base64')\`)
|
|
343
358
|
For security reasons, prefer storing the secret in an environment variable and read it in config/admin.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`
|
|
344
359
|
);
|
|
345
360
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { env } = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
const { getService } = require('../../utils');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns whether the data transfer features have been disabled from the env configuration
|
|
9
|
+
*
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
const isDisabledFromEnv = () => {
|
|
13
|
+
return env.bool('STRAPI_DISABLE_REMOTE_DATA_TRANSFER', false);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A valid transfer token salt must be a non-empty string defined in the Strapi config
|
|
18
|
+
*
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
const hasValidTokenSalt = () => {
|
|
22
|
+
const salt = strapi.config.get('admin.transfer.token.salt', null);
|
|
23
|
+
|
|
24
|
+
return typeof salt === 'string' && salt.length > 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether data transfer features are enabled
|
|
29
|
+
*
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
const isDataTransferEnabled = () => {
|
|
33
|
+
const { utils } = getService('transfer');
|
|
34
|
+
|
|
35
|
+
return !utils.isDisabledFromEnv() && utils.hasValidTokenSalt();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
module.exports = { isDataTransferEnabled, isDisabledFromEnv, hasValidTokenSalt };
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { useIntl } from 'react-intl';
|
|
4
|
-
import { usePersistentState } from '@strapi/helper-plugin';
|
|
5
|
-
import { Select, Option, Typography } from '@strapi/design-system';
|
|
6
|
-
import { getDateOfExpiration } from '../../../pages/ApiTokens/EditView/utils';
|
|
7
|
-
|
|
8
|
-
const LifeSpanInput = ({ token, errors, values, onChange, disabled }) => {
|
|
9
|
-
const { formatMessage } = useIntl();
|
|
10
|
-
const [lang] = usePersistentState('strapi-admin-language', 'en');
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<>
|
|
14
|
-
<Select
|
|
15
|
-
name="lifespan"
|
|
16
|
-
label={formatMessage({
|
|
17
|
-
id: 'Settings.apiTokens.form.duration',
|
|
18
|
-
defaultMessage: 'Token duration',
|
|
19
|
-
})}
|
|
20
|
-
value={values.lifespan !== null ? values.lifespan : '0'}
|
|
21
|
-
error={
|
|
22
|
-
errors.lifespan
|
|
23
|
-
? formatMessage(
|
|
24
|
-
errors.lifespan?.id
|
|
25
|
-
? errors.lifespan
|
|
26
|
-
: { id: errors.lifespan, defaultMessage: errors.lifespan }
|
|
27
|
-
)
|
|
28
|
-
: null
|
|
29
|
-
}
|
|
30
|
-
onChange={(value) => {
|
|
31
|
-
onChange({ target: { name: 'lifespan', value } });
|
|
32
|
-
}}
|
|
33
|
-
required
|
|
34
|
-
disabled={disabled}
|
|
35
|
-
placeholder="Select"
|
|
36
|
-
>
|
|
37
|
-
<Option value="604800000">
|
|
38
|
-
{formatMessage({
|
|
39
|
-
id: 'Settings.apiTokens.duration.7-days',
|
|
40
|
-
defaultMessage: '7 days',
|
|
41
|
-
})}
|
|
42
|
-
</Option>
|
|
43
|
-
<Option value="2592000000">
|
|
44
|
-
{formatMessage({
|
|
45
|
-
id: 'Settings.apiTokens.duration.30-days',
|
|
46
|
-
defaultMessage: '30 days',
|
|
47
|
-
})}
|
|
48
|
-
</Option>
|
|
49
|
-
<Option value="7776000000">
|
|
50
|
-
{formatMessage({
|
|
51
|
-
id: 'Settings.apiTokens.duration.90-days',
|
|
52
|
-
defaultMessage: '90 days',
|
|
53
|
-
})}
|
|
54
|
-
</Option>
|
|
55
|
-
<Option value="0">
|
|
56
|
-
{formatMessage({
|
|
57
|
-
id: 'Settings.apiTokens.duration.unlimited',
|
|
58
|
-
defaultMessage: 'Unlimited',
|
|
59
|
-
})}
|
|
60
|
-
</Option>
|
|
61
|
-
</Select>
|
|
62
|
-
<Typography variant="pi" textColor="neutral600">
|
|
63
|
-
{disabled &&
|
|
64
|
-
`${formatMessage({
|
|
65
|
-
id: 'Settings.apiTokens.duration.expiration-date',
|
|
66
|
-
defaultMessage: 'Expiration date',
|
|
67
|
-
})}: ${getDateOfExpiration(token?.createdAt, parseInt(values.lifespan, 10, lang))}`}
|
|
68
|
-
</Typography>
|
|
69
|
-
</>
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
LifeSpanInput.propTypes = {
|
|
74
|
-
errors: PropTypes.string,
|
|
75
|
-
onChange: PropTypes.func.isRequired,
|
|
76
|
-
values: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
|
77
|
-
disabled: PropTypes.bool.isRequired,
|
|
78
|
-
token: PropTypes.shape({
|
|
79
|
-
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
80
|
-
type: PropTypes.string,
|
|
81
|
-
lifespan: PropTypes.string,
|
|
82
|
-
name: PropTypes.string,
|
|
83
|
-
accessKey: PropTypes.string,
|
|
84
|
-
permissions: PropTypes.array,
|
|
85
|
-
description: PropTypes.string,
|
|
86
|
-
createdAt: PropTypes.string,
|
|
87
|
-
}),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
LifeSpanInput.defaultProps = {
|
|
91
|
-
errors: {},
|
|
92
|
-
token: {},
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export default LifeSpanInput;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_strapi_admin=self.webpackChunk_strapi_admin||[]).push([[4649],{30493:function(b,M,n){"use strict";n.d(M,{Z:function(){return s}});var e=n(32735),g=n(5636),a=n(60216),t=n.n(a),u=n(13478),A=n(57269),E=n(39161),f=n(8888);const C=({onRegenerate:y,idToRegenerate:c,backUrl:R})=>{const{formatMessage:I}=(0,g.useIntl)(),[_,L]=(0,e.useState)(!1),{regenerateData:W,isLoadingConfirmation:G}=(0,f.rW)(R,c,y),N=async()=>{W(),L(!1)};return e.createElement(e.Fragment,null,e.createElement(E.Button,{startIcon:e.createElement(A.Refresh,null),type:"button",size:"S",variant:"tertiary",onClick:()=>L(!0),name:"regenerate"},I({id:"Settings.apiTokens.regenerate",defaultMessage:"Regenerate"})),e.createElement(u.ConfirmDialog,{bodyText:{id:"Settings.apiTokens.popUpWarning.message",defaultMessage:"Are you sure you want to regenerate this token?"},iconRightButton:e.createElement(A.Refresh,null),isConfirmButtonLoading:G,isOpen:_,onToggleDialog:()=>L(!1),onConfirm:N,leftButtonText:{id:"Settings.apiTokens.Button.cancel",defaultMessage:"Cancel"},rightButtonText:{id:"Settings.apiTokens.Button.regenerate",defaultMessage:"Regenerate"},title:{id:"Settings.apiTokens.RegenerateDialog.title",defaultMessage:"Regenerate token"}}))};C.defaultProps={onRegenerate(){}},C.propTypes={onRegenerate:t().func,idToRegenerate:t().oneOfType([t().number,t().string]).isRequired,backUrl:t().string.isRequired};var h=C;const v=({title:y,token:c,setToken:R,canEditInputs:I,canRegenerate:_,isSubmitting:L,backUrl:W,regenerateUrl:G})=>{const{formatMessage:N}=(0,g.useIntl)(),Z=X=>{R({...c,accessKey:X})};return e.createElement(E.HeaderLayout,{title:c?.name||N(y),primaryAction:I?e.createElement(E.Stack,{horizontal:!0,spacing:2},_&&c?.id&&e.createElement(h,{backUrl:G,onRegenerate:Z,idToRegenerate:c?.id}),e.createElement(E.Button,{disabled:L,loading:L,startIcon:e.createElement(A.Check,null),type:"submit",size:"S"},N({id:"global.save",defaultMessage:"Save"}))):_&&c?.id&&e.createElement(h,{onRegenerate:Z,idToRegenerate:c?.id,backUrl:G}),navigationAction:e.createElement(u.Link,{startIcon:e.createElement(A.ArrowLeft,null),to:W},N({id:"global.back",defaultMessage:"Back"}))})};v.propTypes={token:t().shape({id:t().oneOfType([t().number,t().string]),type:t().string,lifespan:t().oneOfType([t().number,t().string]),name:t().string,accessKey:t().string,permissions:t().array,description:t().string,createdAt:t().string}),canEditInputs:t().bool.isRequired,canRegenerate:t().bool.isRequired,setToken:t().func.isRequired,isSubmitting:t().bool.isRequired,backUrl:t().string.isRequired,title:t().shape({id:t().string,label:t().string}).isRequired,regenerateUrl:t().string.isRequired},v.defaultProps={token:void 0};var s=v},4321:function(b,M,n){"use strict";var e=n(32735),g=n(60216),a=n.n(g),t=n(5636),u=n(39161),A=n.n(u),E=n(37944);const f=({token:C,errors:h,values:v,onChange:s,isCreating:y})=>{const{formatMessage:c}=(0,t.useIntl)();return e.createElement(e.Fragment,null,e.createElement(u.Select,{name:"lifespan",label:c({id:"Settings.apiTokens.form.duration",defaultMessage:"Token duration"}),value:v.lifespan!==null?v.lifespan:"0",error:h.lifespan?c(h.lifespan?.id?h.lifespan:{id:h.lifespan,defaultMessage:h.lifespan}):null,onChange:R=>{s({target:{name:"lifespan",value:R}})},required:!0,disabled:!y,placeholder:"Select"},e.createElement(u.Option,{value:"604800000"},c({id:"Settings.tokens.duration.7-days",defaultMessage:"7 days"})),e.createElement(u.Option,{value:"2592000000"},c({id:"Settings.tokens.duration.30-days",defaultMessage:"30 days"})),e.createElement(u.Option,{value:"7776000000"},c({id:"Settings.tokens.duration.90-days",defaultMessage:"90 days"})),e.createElement(u.Option,{value:"0"},c({id:"Settings.tokens.duration.unlimited",defaultMessage:"Unlimited"}))),e.createElement(u.Typography,{variant:"pi",textColor:"neutral600"},!y&&`${c({id:"Settings.tokens.duration.expiration-date",defaultMessage:"Expiration date"})}: ${(0,E.IX)(C?.createdAt,parseInt(v.lifespan,10))}`))};f.propTypes={errors:a().shape({lifespan:a().string}),onChange:a().func.isRequired,values:a().shape({lifespan:a().oneOfType([a().number,a().string])}).isRequired,isCreating:a().bool.isRequired,token:a().shape({id:a().oneOfType([a().number,a().string]),type:a().string,lifespan:a().string,name:a().string,accessKey:a().string,permissions:a().array,description:a().string,createdAt:a().string})},f.defaultProps={errors:{},token:{}},M.Z=f},93682:function(b,M,n){"use strict";var e=n(32735),g=n(5636),a=n(13478),t=n.n(a),u=n(39161),A=n.n(u),E=n(57269),f=n.n(E),C=n(60216),h=n.n(C),v=n(59087),s=n.n(v);const y=({token:c,tokenType:R})=>{const{formatMessage:I}=(0,g.useIntl)(),_=(0,a.useNotification)(),{trackUsage:L}=(0,a.useTracking)(),W=(0,e.useRef)(L);return e.createElement(a.ContentBox,{endAction:c&&e.createElement("span",{style:{alignSelf:"start"}},e.createElement(v.CopyToClipboard,{onCopy:()=>{W.current("didCopyTokenKey",{tokenType:R}),_({type:"success",message:{id:"Settings.tokens.notification.copied"}})},text:c},e.createElement(u.IconButton,{label:I({id:"app.component.CopyToClipboard.label",defaultMessage:"Copy to clipboard"}),noBorder:!0,icon:e.createElement(E.Duplicate,null),style:{padding:0,height:"1rem"}}))),title:c||I({id:"Settings.tokens.copy.editTitle",defaultMessage:"This token isn\u2019t accessible anymore."}),subtitle:I(c?{id:"Settings.tokens.copy.lastWarning",defaultMessage:"Make sure to copy this token, you won\u2019t be able to see it again!"}:{id:"Settings.tokens.copy.editMessage",defaultMessage:"For security reasons, you can only see your token once."}),icon:e.createElement(E.Key,null),iconBackground:"neutral100"})};y.defaultProps={token:null},y.propTypes={token:h().string,tokenType:h().string.isRequired},M.Z=y},42789:function(b,M,n){"use strict";var e=n(32735),g=n(60216),a=n.n(g),t=n(5636),u=n(39161),A=n.n(u);const E=({errors:f,values:C,onChange:h,canEditInputs:v})=>{const{formatMessage:s}=(0,t.useIntl)();return e.createElement(u.Textarea,{label:s({id:"Settings.tokens.form.description",defaultMessage:"Description"}),name:"description",error:f.description?s(f.description?.id?f.description:{id:f.description,defaultMessage:f.description}):null,onChange:h,disabled:!v},C.description)};E.propTypes={errors:a().shape({description:a().string}),onChange:a().func.isRequired,canEditInputs:a().bool.isRequired,values:a().shape({description:a().string}).isRequired},E.defaultProps={errors:{}},M.Z=E},8377:function(b,M,n){"use strict";var e=n(32735),g=n(60216),a=n.n(g),t=n(5636),u=n(39161),A=n.n(u);const E=({errors:f,values:C,onChange:h,canEditInputs:v})=>{const{formatMessage:s}=(0,t.useIntl)();return e.createElement(u.TextInput,{name:"name",error:f.name?s(f.name?.id?f.name:{id:f.name,defaultMessage:f.name}):null,label:s({id:"Settings.tokens.form.name",defaultMessage:"Name"}),onChange:h,value:C.name,disabled:!v,required:!0})};E.propTypes={errors:a().shape({name:a().string}),onChange:a().func.isRequired,canEditInputs:a().bool.isRequired,values:a().shape({name:a().string}).isRequired},E.defaultProps={errors:{}},M.Z=E},68774:function(b,M,n){"use strict";n.d(M,{Z:function(){return e},f:function(){return g}});const e="api-token",g="transfer-token"},24649:function(b,M,n){"use strict";n.d(M,{Z:function(){return De}});var e=n(32735),g=n(5636),a=n(13478),t=n(39161),u=n(83281),A=n(5141),E=n(84968),f=n(64421),C=n(37944),h=n(57269),v=n(60216),s=n.n(v);const y=({apiTokenName:o})=>{const{formatMessage:i}=(0,g.useIntl)();return(0,a.useFocusWhenNavigate)(),e.createElement(t.Main,{"aria-busy":"true"},e.createElement(a.SettingsPageTitle,{name:"API Tokens"}),e.createElement(t.HeaderLayout,{primaryAction:e.createElement(t.Button,{disabled:!0,startIcon:e.createElement(h.Check,null),type:"button",size:"L"},i({id:"global.save",defaultMessage:"Save"})),title:o||i({id:"Settings.apiTokens.createPage.title",defaultMessage:"Create API Token"})}),e.createElement(t.ContentLayout,null,e.createElement(a.LoadingIndicatorPage,null)))};y.defaultProps={apiTokenName:null},y.propTypes={apiTokenName:s().string};var c=y,R=n(72041);const I=(0,e.createContext)({}),_=({children:o,...i})=>e.createElement(I.Provider,{value:i},o),L=()=>(0,e.useContext)(I);_.propTypes={children:s().node.isRequired};var G=(o,i=[])=>({...o,selectedAction:null,routes:[],selectedActions:[],data:(0,C.mk)(i)}),N=n(97889),Z=n(92891);const X={data:{},selectedActions:[]};var re=(o,i)=>(0,N.default)(o,r=>{switch(i.type){case"ON_CHANGE":{r.selectedActions.includes(i.value)?(0,Z.pull)(r.selectedActions,i.value):r.selectedActions.push(i.value);break}case"SELECT_ALL_IN_PERMISSION":{i.value.every(d=>r.selectedActions.includes(d.actionId))?i.value.forEach(d=>{(0,Z.pull)(r.selectedActions,d.actionId)}):i.value.forEach(d=>{r.selectedActions.push(d.actionId)});break}case"SELECT_ALL_ACTIONS":{r.selectedActions=[...r.data.allActionsIds];break}case"ON_CHANGE_READ_ONLY":{const T=r.data.allActionsIds.filter(d=>d.includes("find")||d.includes("findOne"));r.selectedActions=[...T];break}case"UPDATE_PERMISSIONS_LAYOUT":{r.data=(0,C.mk)(i.value);break}case"UPDATE_ROUTES":{r.routes={...i.value};break}case"UPDATE_PERMISSIONS":{r.selectedActions=[...i.value];break}case"SET_SELECTED_ACTION":{r.selectedAction=i.value;break}default:return r}}),z=n(83292);const ie=z.css`
|
|
2
|
-
background: ${o=>o.theme.colors.primary100};
|
|
3
|
-
svg {
|
|
4
|
-
opacity: 1;
|
|
5
|
-
}
|
|
6
|
-
`;var le=(0,z.default)(t.Box)`
|
|
7
|
-
display: flex;
|
|
8
|
-
justify-content: space-between;
|
|
9
|
-
align-items: center;
|
|
10
|
-
|
|
11
|
-
svg {
|
|
12
|
-
opacity: 0;
|
|
13
|
-
path {
|
|
14
|
-
fill: ${o=>o.theme.colors.primary600};
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/* Show active style both on hover and when the action is selected */
|
|
19
|
-
${o=>o.isActive&&ie}
|
|
20
|
-
&:hover {
|
|
21
|
-
${ie}
|
|
22
|
-
}
|
|
23
|
-
`;const ce=z.default.div`
|
|
24
|
-
flex: 1;
|
|
25
|
-
align-self: center;
|
|
26
|
-
border-top: 1px solid ${({theme:o})=>o.colors.neutral150};
|
|
27
|
-
`,J=({controllers:o,label:i,orderNumber:r,disabled:T,onExpanded:d,indexExpandendCollapsedContent:l})=>{const{value:{onChangeSelectAll:k,onChange:B,selectedActions:P,setSelectedAction:U,selectedAction:$}}=L(),[x,j]=(0,e.useState)(!1),{formatMessage:H}=(0,g.useIntl)(),S=()=>{j(D=>!D),d(r)};(0,e.useEffect)(()=>{l!==null&&l!==r&&x&&j(!1)},[l,r,x]);const F=D=>D===$;return e.createElement(t.Accordion,{expanded:x,onToggle:S,variant:r%2?"primary":"secondary"},e.createElement(t.AccordionToggle,{title:(0,Z.capitalize)(i)}),e.createElement(t.AccordionContent,null,o?.map(D=>{const Q=D.actions.every(m=>P.includes(m.actionId)),ne=D.actions.some(m=>P.includes(m.actionId));return e.createElement(t.Box,{key:`${i}.${D?.controller}`},e.createElement(t.Flex,{justifyContent:"space-between",alignItems:"center",padding:4},e.createElement(t.Box,{paddingRight:4},e.createElement(t.Typography,{variant:"sigma",textColor:"neutral600"},D?.controller)),e.createElement(ce,null),e.createElement(t.Box,{paddingLeft:4},e.createElement(t.Checkbox,{value:Q,indeterminate:!Q&&ne,onValueChange:()=>{k({target:{value:[...D.actions]}})},disabled:T},H({id:"app.utils.select-all",defaultMessage:"Select all"})))),e.createElement(t.Grid,{gap:4,padding:4},D?.actions&&D?.actions.map(m=>e.createElement(t.GridItem,{col:6,key:m.actionId},e.createElement(le,{isActive:F(m.actionId),padding:2,hasRadius:!0},e.createElement(t.Checkbox,{value:P.includes(m.actionId),name:m.actionId,onValueChange:()=>{B({target:{value:m.actionId}})},disabled:T},m.action),e.createElement("button",{type:"button","data-testid":"action-cog",onClick:()=>U({target:{value:m.actionId}}),style:{display:"inline-flex",alignItems:"center"}},e.createElement(h.Cog,null)))))))})))};J.defaultProps={controllers:[],orderNumber:0,disabled:!1,onExpanded:()=>null,indexExpandendCollapsedContent:null},J.propTypes={controllers:s().array,orderNumber:s().number,label:s().string.isRequired,disabled:s().bool,onExpanded:s().func,indexExpandendCollapsedContent:s().number};var de=J;const w=({section:o,...i})=>{const[r,T]=(0,e.useState)(null),d=l=>T(l);return e.createElement(t.Box,{padding:4,background:"neutral0"},o&&o.map((l,k)=>e.createElement(de,{key:l.apiId,label:l.label,controllers:l.controllers,orderNumber:k,indexExpandendCollapsedContent:r,onExpanded:d,name:l.apiId,...i})))};w.defaultProps={section:null},w.propTypes={section:s().arrayOf(s().object)};var pe=w,ue=n(37213),ge=n.n(ue),me=n(15738),Ee=n.n(me),fe=o=>{switch(o){case"POST":return{text:"success600",border:"success200",background:"success100"};case"GET":return{text:"secondary600",border:"secondary200",background:"secondary100"};case"PUT":return{text:"warning600",border:"warning200",background:"warning100"};case"DELETE":return{text:"danger600",border:"danger200",background:"danger100"};default:return{text:"neutral600",border:"neutral200",background:"neutral100"}}};const ye=(0,z.default)(t.Box)`
|
|
28
|
-
margin: -1px;
|
|
29
|
-
border-radius: ${({theme:o})=>o.spaces[1]} 0 0 ${({theme:o})=>o.spaces[1]};
|
|
30
|
-
`;function q({route:o}){const{formatMessage:i}=(0,g.useIntl)(),{method:r,handler:T,path:d}=o,l=d?Ee()(d.split("/")):[],[k="",B=""]=T?T.split("."):[],P=fe(o.method);return e.createElement(t.Stack,{spacing:2},e.createElement(t.Typography,{variant:"delta",as:"h3"},i({id:"Settings.apiTokens.createPage.BoundRoute.title",defaultMessage:"Bound route to"}),"\xA0",e.createElement("span",null,k),e.createElement(t.Typography,{variant:"delta",textColor:"primary600"},".",B)),e.createElement(t.Stack,{horizontal:!0,hasRadius:!0,background:"neutral0",borderColor:"neutral200",spacing:0},e.createElement(ye,{background:P.background,borderColor:P.border,padding:2},e.createElement(t.Typography,{fontWeight:"bold",textColor:P.text},r)),e.createElement(t.Box,{paddingLeft:2,paddingRight:2},ge()(l,U=>e.createElement(t.Typography,{key:U,textColor:U.includes(":")?"neutral600":"neutral900"},"/",U)))))}q.defaultProps={route:{handler:"Nocontroller.error",method:"GET",path:"/there-is-no-path"}},q.propTypes={route:s().shape({handler:s().string,method:s().string,path:s().string})};var Te=q,he=()=>{const{value:{selectedAction:o,routes:i}}=L(),{formatMessage:r}=(0,g.useIntl)(),T=o?.split(".")[0];return e.createElement(t.GridItem,{col:5,background:"neutral150",paddingTop:6,paddingBottom:6,paddingLeft:7,paddingRight:7,style:{minHeight:"100%"}},o?e.createElement(t.Stack,{spacing:2},i[T]?.map(d=>d.config.auth?.scope?.includes(o)||d.handler===o?e.createElement(Te,{key:d.handler,route:d}):null)):e.createElement(t.Stack,{spacing:2},e.createElement(t.Typography,{variant:"delta",as:"h3"},r({id:"Settings.apiTokens.createPage.permissions.header.title",defaultMessage:"Advanced settings"})),e.createElement(t.Typography,{as:"p",textColor:"neutral600"},r({id:"Settings.apiTokens.createPage.permissions.header.hint",defaultMessage:"Select the application's actions or the plugin's actions and click on the cog icon to display the bound route"}))))};const Ce=({...o})=>{const{value:{data:i}}=L(),{formatMessage:r}=(0,g.useIntl)();return e.createElement(t.Grid,{gap:0,shadow:"filterShadow",hasRadius:!0,background:"neutral0"},e.createElement(t.GridItem,{col:7,paddingTop:6,paddingBottom:6,paddingLeft:7,paddingRight:7},e.createElement(t.Stack,{spacing:2},e.createElement(t.Typography,{variant:"delta",as:"h2"},r({id:"Settings.apiTokens.createPage.permissions.title",defaultMessage:"Permissions"})),e.createElement(t.Typography,{as:"p",textColor:"neutral600"},r({id:"Settings.apiTokens.createPage.permissions.description",defaultMessage:"Only actions bound by a route are listed below."}))),i?.permissions&&e.createElement(pe,{section:i?.permissions,...o})),e.createElement(he,null))};var ve=(0,e.memo)(Ce),Ae=n(4321),Me=n(8377),ke=n(42789);const ee=({errors:o,values:i,onChange:r,canEditInputs:T,options:d,label:l})=>{const{formatMessage:k}=(0,g.useIntl)();return e.createElement(t.Select,{name:"type",label:k({id:l.id,defaultMessage:l.defaultMessage}),value:i?.type,error:o.type?k(o.type?.id?o.type:{id:o.type,defaultMessage:o.type}):null,onChange:r,placeholder:"Select",required:!0,disabled:!T},d&&d.map(({value:B,label:P})=>e.createElement(t.Option,{key:B,value:B},k(P))))};ee.propTypes={options:s().arrayOf(s().shape({label:s().shape({id:s().string,defaultMessage:s().string}),value:s().string})),errors:s().shape({type:s().string}),onChange:s().func.isRequired,canEditInputs:s().bool.isRequired,values:s().shape({type:s().string}).isRequired,label:s().shape({id:s().string,defaultMessage:s().string}).isRequired},ee.defaultProps={errors:{},options:[]};var Re=ee;const te=({errors:o,onChange:i,canEditInputs:r,isCreating:T,values:d,apiToken:l,onDispatch:k,setHasChangedPermissions:B})=>{const{formatMessage:P}=(0,g.useIntl)(),U=({target:{value:x}})=>{B(!1),x==="full-access"&&k({type:"SELECT_ALL_ACTIONS"}),x==="read-only"&&k({type:"ON_CHANGE_READ_ONLY"})},$=[{value:"read-only",label:{id:"Settings.apiTokens.types.read-only",defaultMessage:"Read-only"}},{value:"full-access",label:{id:"Settings.apiTokens.types.full-access",defaultMessage:"Full access"}},{value:"custom",label:{id:"Settings.apiTokens.types.custom",defaultMessage:"Custom"}}];return e.createElement(t.Box,{background:"neutral0",hasRadius:!0,shadow:"filterShadow",paddingTop:6,paddingBottom:6,paddingLeft:7,paddingRight:7},e.createElement(t.Stack,{spacing:4},e.createElement(t.Typography,{variant:"delta",as:"h2"},P({id:"global.details",defaultMessage:"Details"})),e.createElement(t.Grid,{gap:5},e.createElement(t.GridItem,{key:"name",col:6,xs:12},e.createElement(Me.Z,{errors:o,values:d,canEditInputs:r,onChange:i})),e.createElement(t.GridItem,{key:"description",col:6,xs:12},e.createElement(ke.Z,{errors:o,values:d,canEditInputs:r,onChange:i})),e.createElement(t.GridItem,{key:"lifespan",col:6,xs:12},e.createElement(Ae.Z,{isCreating:T,errors:o,values:d,onChange:i,token:l})),e.createElement(t.GridItem,{key:"type",col:6,xs:12},e.createElement(Re,{values:d,errors:o,label:{id:"Settings.apiTokens.form.type",defaultMessage:"Token type"},onChange:x=>{U({target:{value:x}}),i({target:{name:"type",value:x}})},options:$,canEditInputs:r})))))};te.propTypes={errors:s().shape({name:s().string,description:s().string,lifespan:s().string,type:s().string}),onChange:s().func.isRequired,canEditInputs:s().bool.isRequired,values:s().shape({name:s().string,description:s().string,lifespan:s().oneOfType([s().number,s().string]),type:s().string}).isRequired,isCreating:s().bool.isRequired,apiToken:s().shape({id:s().oneOfType([s().number,s().string]),type:s().string,lifespan:s().string,name:s().string,accessKey:s().string,permissions:s().array,description:s().string,createdAt:s().string}),onDispatch:s().func.isRequired,setHasChangedPermissions:s().func.isRequired},te.defaultProps={errors:{},apiToken:{}};var Pe=te,Oe=n(93682),Ie=n(30493),Y=n(68774);const Se="Name already taken";var De=()=>{(0,a.useFocusWhenNavigate)();const{formatMessage:o}=(0,g.useIntl)(),{lockApp:i,unlockApp:r}=(0,a.useOverlayBlocker)(),T=(0,a.useNotification)(),d=(0,A.useHistory)(),[l,k]=(0,e.useState)(d.location.state?.apiToken.accessKey?{...d.location.state.apiToken}:null),{trackUsage:B}=(0,a.useTracking)(),P=(0,e.useRef)(B),{setCurrentStep:U}=(0,a.useGuidedTour)(),{allowedActions:{canCreate:$,canUpdate:x,canRegenerate:j}}=(0,a.useRBAC)(R.Z.settings["api-tokens"]),[H,S]=(0,e.useReducer)(re,X,p=>G(p,{})),{params:{id:F}}=(0,A.useRouteMatch)("/settings/api-tokens/:id"),{get:D,post:Q,put:ne}=(0,a.useFetchClient)(),m=F==="create";(0,E.useQuery)("content-api-permissions",async()=>{const[p,K]=await Promise.all(["/admin/content-api/permissions","/admin/content-api/routes"].map(async V=>{const{data:O}=await D(V);return O.data}));S({type:"UPDATE_PERMISSIONS_LAYOUT",value:p}),S({type:"UPDATE_ROUTES",value:K}),l&&(l?.type==="read-only"&&S({type:"ON_CHANGE_READ_ONLY"}),l?.type==="full-access"&&S({type:"SELECT_ALL_ACTIONS"}),l?.type==="custom"&&S({type:"UPDATE_PERMISSIONS",value:l?.permissions}))},{onError(){T({type:"warning",message:{id:"notification.error",defaultMessage:"An error occured"}})}}),(0,e.useEffect)(()=>{P.current(m?"didAddTokenFromList":"didEditTokenFromList",{tokenType:Y.Z})},[m]);const{status:_e}=(0,E.useQuery)(["api-token",F],async()=>{const{data:{data:p}}=await D(`/admin/api-tokens/${F}`);return k({...p}),p?.type==="read-only"&&S({type:"ON_CHANGE_READ_ONLY"}),p?.type==="full-access"&&S({type:"SELECT_ALL_ACTIONS"}),p?.type==="custom"&&S({type:"UPDATE_PERMISSIONS",value:p?.permissions}),p},{enabled:!m&&!l,onError(){T({type:"warning",message:{id:"notification.error",defaultMessage:"An error occured"}})}}),Le=async(p,K)=>{P.current(m?"willCreateToken":"willEditToken",{tokenType:Y.Z}),i();const V=p.lifespan&&parseInt(p.lifespan,10)&&p.lifespan!=="0"?parseInt(p.lifespan,10):null;try{const{data:{data:O}}=m?await Q("/admin/api-tokens",{...p,lifespan:V,permissions:p.type==="custom"?H.selectedActions:null}):await ne(`/admin/api-tokens/${F}`,{name:p.name,description:p.description,type:p.type,permissions:p.type==="custom"?H.selectedActions:null});m&&(d.replace(`/settings/api-tokens/${O.id}`,{apiToken:O}),U("apiTokens.success")),r(),k({...O}),T({type:"success",message:o(m?{id:"notification.success.tokencreated",defaultMessage:"API Token successfully created"}:{id:"notification.success.tokenedited",defaultMessage:"API Token successfully edited"})}),P.current(m?"didCreateToken":"didEditToken",{type:l.type,tokenType:Y.Z})}catch(O){const oe=(0,f.Iz)(O.response.data);K.setErrors(oe),O?.response?.data?.error?.message===Se?T({type:"warning",message:O.response.data.message||"notification.error.tokennamenotunique"}):T({type:"warning",message:O?.response?.data?.message||"notification.error"}),r()}},[xe,se]=(0,e.useState)(!1),Be={...H,onChange:({target:{value:p}})=>{se(!0),S({type:"ON_CHANGE",value:p})},onChangeSelectAll:({target:{value:p}})=>{se(!0),S({type:"SELECT_ALL_IN_PERMISSION",value:p})},setSelectedAction:({target:{value:p}})=>{S({type:"SET_SELECTED_ACTION",value:p})}},ae=x&&!m||$&&m;return!m&&!l&&_e!=="success"?e.createElement(c,{apiTokenName:l?.name}):e.createElement(_,{value:Be},e.createElement(t.Main,null,e.createElement(a.SettingsPageTitle,{name:"API Tokens"}),e.createElement(u.Formik,{validationSchema:C.fK,validateOnChange:!1,initialValues:{name:l?.name||"",description:l?.description||"",type:l?.type,lifespan:l?.lifespan?l.lifespan.toString():l?.lifespan},enableReinitialize:!0,onSubmit:(p,K)=>Le(p,K)},({errors:p,handleChange:K,isSubmitting:V,values:O,setFieldValue:oe})=>(xe&&O?.type!=="custom"&&oe("type","custom"),e.createElement(a.Form,null,e.createElement(Ie.Z,{backUrl:"/settings/api-tokens",title:{id:"Settings.apiTokens.createPage.title",defaultMessage:"Create API Token"},token:l,setToken:k,canEditInputs:ae,canRegenerate:j,isSubmitting:V,regenerateUrl:"/admin/api-tokens/"}),e.createElement(t.ContentLayout,null,e.createElement(t.Stack,{spacing:6},Boolean(l?.name)&&e.createElement(Oe.Z,{token:l?.accessKey,tokenType:Y.Z}),e.createElement(Pe,{errors:p,onChange:K,canEditInputs:ae,isCreating:m,values:O,apiToken:l,onDispatch:S,setHasChangedPermissions:se}),e.createElement(ve,{disabled:!ae||O?.type==="read-only"||O?.type==="full-access"}))))))))}},37944:function(b,M,n){"use strict";n.d(M,{IX:function(){return t},fK:function(){return f},mk:function(){return v}});var e=n(64213),g=n(54049),t=(s,y,c="en")=>{if(y&&typeof y=="number"){const R=y/24/60/60/1e3;return(0,e.format)((0,e.addDays)(new Date(s),R),"PPP",{locale:g[c]})}return"Unlimited"},u=n(5173),A=n(13478),f=u.Ry().shape({name:u.Z_(A.translatedErrors.string).required(A.translatedErrors.required),type:u.Z_(A.translatedErrors.string).oneOf(["read-only","full-access","custom"]).required(A.translatedErrors.required),description:u.Z_().nullable(),lifespan:u.Rx().integer().min(0).nullable().defined(A.translatedErrors.required)}),C=n(92891),v=s=>{const y={allActionsIds:[],permissions:[]};return y.permissions=Object.keys(s).map(c=>({apiId:c,label:c.split("::")[1],controllers:(0,C.flatten)(Object.keys(s[c].controllers).map(R=>({controller:R,actions:(0,C.flatten)(s[c].controllers[R].map(I=>{const _=`${c}.${R}.${I}`;return c.includes("api::")&&y.allActionsIds.push(_),{action:I,actionId:_}}))})))})),y}},15738:function(b,M,n){var e=n(4293);function g(a){var t=a==null?0:a.length;return t?e(a,1,t):[]}b.exports=g}}]);
|