@strapi/plugin-users-permissions 0.0.0-next.e50ef5e2ea57ecf3da5bcf308508b51ee3c0deca → 0.0.0-next.e5b87a54008c9de2b3286a4774635dcf69895d9b
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/Permissions/PermissionRow/SubCategory.jsx +8 -1
- package/admin/src/components/Policies/index.jsx +1 -0
- package/admin/src/components/UsersPermissions/index.jsx +1 -0
- package/admin/src/pages/AdvancedSettings/index.jsx +1 -0
- package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +2 -1
- package/admin/src/pages/Providers/index.jsx +74 -76
- package/admin/src/pages/Roles/pages/CreatePage.jsx +26 -8
- package/admin/src/pages/Roles/pages/EditPage.jsx +13 -7
- package/admin/src/pages/Roles/pages/ListPage/index.jsx +92 -93
- package/admin/src/translations/de.json +27 -2
- package/admin/src/translations/en.json +1 -0
- package/dist/admin/components/FormModal/Input/index.js.map +1 -1
- package/dist/admin/components/FormModal/Input/index.mjs.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.js +3 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.js.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs +3 -1
- package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/index.js.map +1 -1
- package/dist/admin/components/Permissions/PermissionRow/index.mjs.map +1 -1
- package/dist/admin/components/Policies/index.js +1 -0
- package/dist/admin/components/Policies/index.js.map +1 -1
- package/dist/admin/components/Policies/index.mjs +1 -0
- package/dist/admin/components/Policies/index.mjs.map +1 -1
- package/dist/admin/components/UsersPermissions/index.js +1 -0
- package/dist/admin/components/UsersPermissions/index.js.map +1 -1
- package/dist/admin/components/UsersPermissions/index.mjs +1 -0
- package/dist/admin/components/UsersPermissions/index.mjs.map +1 -1
- package/dist/admin/pages/AdvancedSettings/index.js +1 -0
- package/dist/admin/pages/AdvancedSettings/index.js.map +1 -1
- package/dist/admin/pages/AdvancedSettings/index.mjs +1 -0
- package/dist/admin/pages/AdvancedSettings/index.mjs.map +1 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.js +2 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.js.map +1 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs +2 -1
- package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs.map +1 -1
- package/dist/admin/pages/Providers/index.js +84 -88
- package/dist/admin/pages/Providers/index.js.map +1 -1
- package/dist/admin/pages/Providers/index.mjs +84 -88
- package/dist/admin/pages/Providers/index.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/CreatePage.js +15 -1
- package/dist/admin/pages/Roles/pages/CreatePage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/CreatePage.mjs +17 -3
- package/dist/admin/pages/Roles/pages/CreatePage.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.js +14 -3
- package/dist/admin/pages/Roles/pages/EditPage.js.map +1 -1
- package/dist/admin/pages/Roles/pages/EditPage.mjs +15 -4
- package/dist/admin/pages/Roles/pages/EditPage.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js +96 -99
- package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs +96 -99
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
- package/dist/admin/translations/de.json.js +27 -2
- package/dist/admin/translations/de.json.js.map +1 -1
- package/dist/admin/translations/de.json.mjs +27 -2
- package/dist/admin/translations/de.json.mjs.map +1 -1
- package/dist/admin/translations/en.json.js +1 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +1 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/getTrad.js.map +1 -1
- package/dist/admin/utils/getTrad.mjs.map +1 -1
- package/dist/admin/utils/prefixPluginTranslations.js.map +1 -1
- package/dist/admin/utils/prefixPluginTranslations.mjs.map +1 -1
- package/dist/server/bootstrap/index.js +28 -7
- package/dist/server/bootstrap/index.js.map +1 -1
- package/dist/server/bootstrap/index.mjs +28 -7
- package/dist/server/bootstrap/index.mjs.map +1 -1
- package/dist/server/config.js +16 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +16 -0
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/controllers/auth.js +204 -3
- package/dist/server/controllers/auth.js.map +1 -1
- package/dist/server/controllers/auth.mjs +204 -3
- package/dist/server/controllers/auth.mjs.map +1 -1
- package/dist/server/controllers/content-manager-user.js +3 -3
- package/dist/server/controllers/content-manager-user.js.map +1 -1
- package/dist/server/controllers/content-manager-user.mjs +3 -3
- package/dist/server/controllers/content-manager-user.mjs.map +1 -1
- package/dist/server/controllers/validation/user.js +6 -1
- package/dist/server/controllers/validation/user.js.map +1 -1
- package/dist/server/controllers/validation/user.mjs +6 -1
- package/dist/server/controllers/validation/user.mjs.map +1 -1
- package/dist/server/graphql/resolvers-configs.js.map +1 -1
- package/dist/server/graphql/resolvers-configs.mjs.map +1 -1
- package/dist/server/middlewares/rateLimit.js.map +1 -1
- package/dist/server/middlewares/rateLimit.mjs.map +1 -1
- package/dist/server/routes/content-api/auth.js +155 -91
- package/dist/server/routes/content-api/auth.js.map +1 -1
- package/dist/server/routes/content-api/auth.mjs +155 -91
- package/dist/server/routes/content-api/auth.mjs.map +1 -1
- package/dist/server/routes/content-api/index.js +11 -9
- package/dist/server/routes/content-api/index.js.map +1 -1
- package/dist/server/routes/content-api/index.mjs +11 -9
- package/dist/server/routes/content-api/index.mjs.map +1 -1
- package/dist/server/routes/content-api/permissions.js +14 -7
- package/dist/server/routes/content-api/permissions.js.map +1 -1
- package/dist/server/routes/content-api/permissions.mjs +14 -7
- package/dist/server/routes/content-api/permissions.mjs.map +1 -1
- package/dist/server/routes/content-api/role.js +61 -27
- package/dist/server/routes/content-api/role.js.map +1 -1
- package/dist/server/routes/content-api/role.mjs +61 -27
- package/dist/server/routes/content-api/role.mjs.map +1 -1
- package/dist/server/routes/content-api/user.js +119 -57
- package/dist/server/routes/content-api/user.js.map +1 -1
- package/dist/server/routes/content-api/user.mjs +119 -57
- package/dist/server/routes/content-api/user.mjs.map +1 -1
- package/dist/server/routes/content-api/validation.js +217 -0
- package/dist/server/routes/content-api/validation.js.map +1 -0
- package/dist/server/routes/content-api/validation.mjs +215 -0
- package/dist/server/routes/content-api/validation.mjs.map +1 -0
- package/dist/server/services/constants.js +19 -0
- package/dist/server/services/constants.js.map +1 -0
- package/dist/server/services/constants.mjs +17 -0
- package/dist/server/services/constants.mjs.map +1 -0
- package/dist/server/services/jwt.js +45 -2
- package/dist/server/services/jwt.js.map +1 -1
- package/dist/server/services/jwt.mjs +45 -2
- package/dist/server/services/jwt.mjs.map +1 -1
- package/dist/server/services/providers-registry.js.map +1 -1
- package/dist/server/services/providers-registry.mjs.map +1 -1
- package/dist/server/services/role.js.map +1 -1
- package/dist/server/services/role.mjs.map +1 -1
- package/dist/server/services/user.js +29 -20
- package/dist/server/services/user.js.map +1 -1
- package/dist/server/services/user.mjs +29 -20
- package/dist/server/services/user.mjs.map +1 -1
- package/dist/server/services/users-permissions.js +12 -3
- package/dist/server/services/users-permissions.js.map +1 -1
- package/dist/server/services/users-permissions.mjs +12 -3
- package/dist/server/services/users-permissions.mjs.map +1 -1
- package/package.json +13 -12
- package/server/bootstrap/index.js +31 -0
- package/server/config.js +22 -0
- package/server/controllers/auth.js +246 -8
- package/server/controllers/content-manager-user.js +3 -4
- package/server/controllers/validation/user.js +12 -1
- package/server/routes/content-api/auth.js +119 -71
- package/server/routes/content-api/index.js +11 -4
- package/server/routes/content-api/permissions.js +14 -7
- package/server/routes/content-api/role.js +57 -27
- package/server/routes/content-api/user.js +108 -51
- package/server/routes/content-api/validation.js +250 -0
- package/server/services/constants.js +9 -0
- package/server/services/jwt.js +50 -2
- package/server/services/user.js +11 -0
- package/server/services/users-permissions.js +6 -2
|
@@ -31,6 +31,12 @@ const sanitizeUser = (user, ctx) => {
|
|
|
31
31
|
return strapi.contentAPI.sanitize.output(user, userSchema, { auth });
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
const extractDeviceId = (requestBody) => {
|
|
35
|
+
const { deviceId } = requestBody || {};
|
|
36
|
+
|
|
37
|
+
return typeof deviceId === 'string' && deviceId.length > 0 ? deviceId : undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
module.exports = ({ strapi }) => ({
|
|
35
41
|
async callback(ctx) {
|
|
36
42
|
const provider = ctx.params.provider || 'local';
|
|
@@ -86,6 +92,51 @@ module.exports = ({ strapi }) => ({
|
|
|
86
92
|
throw new ApplicationError('Your account has been blocked by an administrator');
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
96
|
+
if (mode === 'refresh') {
|
|
97
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
98
|
+
|
|
99
|
+
const refresh = await strapi
|
|
100
|
+
.sessionManager('users-permissions')
|
|
101
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
102
|
+
|
|
103
|
+
const access = await strapi
|
|
104
|
+
.sessionManager('users-permissions')
|
|
105
|
+
.generateAccessToken(refresh.token);
|
|
106
|
+
if ('error' in access) {
|
|
107
|
+
throw new ApplicationError('Invalid credentials');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
111
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
112
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
113
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
114
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
115
|
+
const isSecure =
|
|
116
|
+
typeof upSessions.cookie?.secure === 'boolean'
|
|
117
|
+
? upSessions.cookie?.secure
|
|
118
|
+
: isProduction;
|
|
119
|
+
|
|
120
|
+
const cookieOptions = {
|
|
121
|
+
httpOnly: true,
|
|
122
|
+
secure: isSecure,
|
|
123
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
124
|
+
path: upSessions.cookie?.path ?? '/',
|
|
125
|
+
domain: upSessions.cookie?.domain,
|
|
126
|
+
overwrite: true,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
ctx.cookies.set(cookieName, refresh.token, cookieOptions);
|
|
130
|
+
return ctx.send({ jwt: access.token, user: await sanitizeUser(user, ctx) });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return ctx.send({
|
|
134
|
+
jwt: access.token,
|
|
135
|
+
refreshToken: refresh.token,
|
|
136
|
+
user: await sanitizeUser(user, ctx),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
89
140
|
return ctx.send({
|
|
90
141
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
91
142
|
user: await sanitizeUser(user, ctx),
|
|
@@ -100,6 +151,49 @@ module.exports = ({ strapi }) => ({
|
|
|
100
151
|
throw new ForbiddenError('Your account has been blocked by an administrator');
|
|
101
152
|
}
|
|
102
153
|
|
|
154
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
155
|
+
if (mode === 'refresh') {
|
|
156
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
157
|
+
|
|
158
|
+
const refresh = await strapi
|
|
159
|
+
.sessionManager('users-permissions')
|
|
160
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
161
|
+
|
|
162
|
+
const access = await strapi
|
|
163
|
+
.sessionManager('users-permissions')
|
|
164
|
+
.generateAccessToken(refresh.token);
|
|
165
|
+
if ('error' in access) {
|
|
166
|
+
throw new ApplicationError('Invalid credentials');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
170
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
171
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
172
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
173
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
174
|
+
const isSecure =
|
|
175
|
+
typeof upSessions.cookie?.secure === 'boolean'
|
|
176
|
+
? upSessions.cookie?.secure
|
|
177
|
+
: isProduction;
|
|
178
|
+
|
|
179
|
+
const cookieOptions = {
|
|
180
|
+
httpOnly: true,
|
|
181
|
+
secure: isSecure,
|
|
182
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
183
|
+
path: upSessions.cookie?.path ?? '/',
|
|
184
|
+
domain: upSessions.cookie?.domain,
|
|
185
|
+
overwrite: true,
|
|
186
|
+
};
|
|
187
|
+
ctx.cookies.set(cookieName, refresh.token, cookieOptions);
|
|
188
|
+
return ctx.send({ jwt: access.token, user: await sanitizeUser(user, ctx) });
|
|
189
|
+
}
|
|
190
|
+
return ctx.send({
|
|
191
|
+
jwt: access.token,
|
|
192
|
+
refreshToken: refresh.token,
|
|
193
|
+
user: await sanitizeUser(user, ctx),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
103
197
|
return ctx.send({
|
|
104
198
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
105
199
|
user: await sanitizeUser(user, ctx),
|
|
@@ -137,7 +231,33 @@ module.exports = ({ strapi }) => ({
|
|
|
137
231
|
|
|
138
232
|
await getService('user').edit(user.id, { password });
|
|
139
233
|
|
|
140
|
-
|
|
234
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
235
|
+
if (mode === 'refresh') {
|
|
236
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
237
|
+
|
|
238
|
+
// Invalidate all sessions when password changes for security
|
|
239
|
+
await strapi.sessionManager('users-permissions').invalidateRefreshToken(String(user.id));
|
|
240
|
+
|
|
241
|
+
const newDeviceId = deviceId || crypto.randomUUID();
|
|
242
|
+
const refresh = await strapi
|
|
243
|
+
.sessionManager('users-permissions')
|
|
244
|
+
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
245
|
+
|
|
246
|
+
const access = await strapi
|
|
247
|
+
.sessionManager('users-permissions')
|
|
248
|
+
.generateAccessToken(refresh.token);
|
|
249
|
+
if ('error' in access) {
|
|
250
|
+
throw new ApplicationError('Invalid credentials');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return ctx.send({
|
|
254
|
+
jwt: access.token,
|
|
255
|
+
refreshToken: refresh.token,
|
|
256
|
+
user: await sanitizeUser(user, ctx),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return ctx.send({
|
|
141
261
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
142
262
|
user: await sanitizeUser(user, ctx),
|
|
143
263
|
});
|
|
@@ -168,13 +288,117 @@ module.exports = ({ strapi }) => ({
|
|
|
168
288
|
password,
|
|
169
289
|
});
|
|
170
290
|
|
|
171
|
-
|
|
172
|
-
|
|
291
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
292
|
+
if (mode === 'refresh') {
|
|
293
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
294
|
+
|
|
295
|
+
// Invalidate all sessions when password is reset for security
|
|
296
|
+
await strapi.sessionManager('users-permissions').invalidateRefreshToken(String(user.id));
|
|
297
|
+
|
|
298
|
+
const newDeviceId = deviceId || crypto.randomUUID();
|
|
299
|
+
const refresh = await strapi
|
|
300
|
+
.sessionManager('users-permissions')
|
|
301
|
+
.generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
|
|
302
|
+
|
|
303
|
+
const access = await strapi
|
|
304
|
+
.sessionManager('users-permissions')
|
|
305
|
+
.generateAccessToken(refresh.token);
|
|
306
|
+
if ('error' in access) {
|
|
307
|
+
throw new ApplicationError('Invalid credentials');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return ctx.send({
|
|
311
|
+
jwt: access.token,
|
|
312
|
+
refreshToken: refresh.token,
|
|
313
|
+
user: await sanitizeUser(user, ctx),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return ctx.send({
|
|
173
318
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
174
319
|
user: await sanitizeUser(user, ctx),
|
|
175
320
|
});
|
|
176
321
|
},
|
|
322
|
+
async refresh(ctx) {
|
|
323
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
324
|
+
if (mode !== 'refresh') {
|
|
325
|
+
return ctx.notFound();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
329
|
+
const cookieName = upSessions?.cookie?.name || 'strapi_up_refresh';
|
|
330
|
+
|
|
331
|
+
// Check for refresh token in cookie first (if httpOnly is configured), then in body
|
|
332
|
+
let refreshToken = ctx.cookies.get(cookieName);
|
|
333
|
+
if (!refreshToken) {
|
|
334
|
+
refreshToken = ctx.request.body?.refreshToken;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!refreshToken || typeof refreshToken !== 'string') {
|
|
338
|
+
return ctx.badRequest('Missing refresh token');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const rotation = await strapi
|
|
342
|
+
.sessionManager('users-permissions')
|
|
343
|
+
.rotateRefreshToken(refreshToken);
|
|
344
|
+
if ('error' in rotation) {
|
|
345
|
+
return ctx.unauthorized('Invalid refresh token');
|
|
346
|
+
}
|
|
177
347
|
|
|
348
|
+
const result = await strapi
|
|
349
|
+
.sessionManager('users-permissions')
|
|
350
|
+
.generateAccessToken(rotation.token);
|
|
351
|
+
if ('error' in result) {
|
|
352
|
+
return ctx.unauthorized('Invalid refresh token');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
356
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
357
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
358
|
+
const isSecure =
|
|
359
|
+
typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
|
|
360
|
+
|
|
361
|
+
const cookieOptions = {
|
|
362
|
+
httpOnly: true,
|
|
363
|
+
secure: isSecure,
|
|
364
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
365
|
+
path: upSessions.cookie?.path ?? '/',
|
|
366
|
+
domain: upSessions.cookie?.domain,
|
|
367
|
+
overwrite: true,
|
|
368
|
+
};
|
|
369
|
+
ctx.cookies.set(cookieName, rotation.token, cookieOptions);
|
|
370
|
+
return ctx.send({ jwt: result.token });
|
|
371
|
+
}
|
|
372
|
+
return ctx.send({ jwt: result.token, refreshToken: rotation.token });
|
|
373
|
+
},
|
|
374
|
+
async logout(ctx) {
|
|
375
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
376
|
+
if (mode !== 'refresh') {
|
|
377
|
+
return ctx.notFound();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Invalidate all sessions for the authenticated user, or by deviceId if provided
|
|
381
|
+
if (!ctx.state.user) {
|
|
382
|
+
return ctx.unauthorized('Missing authentication');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const deviceId = extractDeviceId(ctx.request.body);
|
|
386
|
+
try {
|
|
387
|
+
await strapi
|
|
388
|
+
.sessionManager('users-permissions')
|
|
389
|
+
.invalidateRefreshToken(String(ctx.state.user.id), deviceId);
|
|
390
|
+
} catch (err) {
|
|
391
|
+
strapi.log.error('UP logout failed', err);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const upSessions = strapi.config.get('plugin::users-permissions.sessions');
|
|
395
|
+
const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
|
|
396
|
+
if (upSessions?.httpOnly || requestHttpOnly) {
|
|
397
|
+
const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
|
|
398
|
+
ctx.cookies.set(cookieName, '', { expires: new Date(0) });
|
|
399
|
+
}
|
|
400
|
+
return ctx.send({ ok: true });
|
|
401
|
+
},
|
|
178
402
|
async connect(ctx, next) {
|
|
179
403
|
const grant = require('grant').koa();
|
|
180
404
|
|
|
@@ -387,12 +611,26 @@ module.exports = ({ strapi }) => ({
|
|
|
387
611
|
return ctx.send({ user: sanitizedUser });
|
|
388
612
|
}
|
|
389
613
|
|
|
390
|
-
const
|
|
614
|
+
const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
|
|
615
|
+
if (mode === 'refresh') {
|
|
616
|
+
const deviceId = extractDeviceId(ctx.request.body) || crypto.randomUUID();
|
|
391
617
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
618
|
+
const refresh = await strapi
|
|
619
|
+
.sessionManager('users-permissions')
|
|
620
|
+
.generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
|
|
621
|
+
|
|
622
|
+
const access = await strapi
|
|
623
|
+
.sessionManager('users-permissions')
|
|
624
|
+
.generateAccessToken(refresh.token);
|
|
625
|
+
if ('error' in access) {
|
|
626
|
+
throw new ApplicationError('Invalid credentials');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return ctx.send({ jwt: access.token, refreshToken: refresh.token, user: sanitizedUser });
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const jwt = getService('jwt').issue(_.pick(user, ['id']));
|
|
633
|
+
return ctx.send({ jwt, user: sanitizedUser });
|
|
396
634
|
},
|
|
397
635
|
|
|
398
636
|
async emailConfirmation(ctx, next, returnUser) {
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
|
|
5
|
-
const { ApplicationError,
|
|
6
|
-
require('@strapi/utils').errors;
|
|
5
|
+
const { ApplicationError, NotFoundError, ForbiddenError } = require('@strapi/utils').errors;
|
|
7
6
|
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
8
7
|
|
|
9
8
|
const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
|
|
@@ -133,8 +132,8 @@ module.exports = {
|
|
|
133
132
|
|
|
134
133
|
await validateUpdateUserBody(ctx.request.body);
|
|
135
134
|
|
|
136
|
-
if (_.has(body, 'password') &&
|
|
137
|
-
|
|
135
|
+
if (_.has(body, 'password') && (password == null || password === '')) {
|
|
136
|
+
delete body.password;
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
if (_.has(body, 'username')) {
|
|
@@ -29,7 +29,18 @@ const createUserBodySchema = yup.object().shape({
|
|
|
29
29
|
const updateUserBodySchema = yup.object().shape({
|
|
30
30
|
email: yup.string().email().min(1),
|
|
31
31
|
username: yup.string().min(1),
|
|
32
|
-
password: yup
|
|
32
|
+
password: yup
|
|
33
|
+
.mixed()
|
|
34
|
+
.test(
|
|
35
|
+
'password-validation',
|
|
36
|
+
'Password must be at least 1 character',
|
|
37
|
+
function validatePassword(value) {
|
|
38
|
+
if (value == null || value === '') {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return typeof value === 'string' && value.length >= 1;
|
|
42
|
+
}
|
|
43
|
+
),
|
|
33
44
|
role: yup.lazy((value) =>
|
|
34
45
|
typeof value === 'object'
|
|
35
46
|
? yup.object().shape({
|
|
@@ -1,82 +1,130 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const { UsersPermissionsRouteValidator } = require('./validation');
|
|
4
|
+
|
|
5
|
+
module.exports = (strapi) => {
|
|
6
|
+
const validator = new UsersPermissionsRouteValidator(strapi);
|
|
7
|
+
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/connect/(.*)',
|
|
12
|
+
handler: 'auth.connect',
|
|
13
|
+
config: {
|
|
14
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
15
|
+
prefix: '',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
method: 'POST',
|
|
20
|
+
path: '/auth/local',
|
|
21
|
+
handler: 'auth.callback',
|
|
22
|
+
config: {
|
|
23
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
24
|
+
prefix: '',
|
|
25
|
+
},
|
|
26
|
+
request: {
|
|
27
|
+
body: { 'application/json': validator.loginBodySchema },
|
|
28
|
+
},
|
|
29
|
+
response: validator.authResponseSchema,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
method: 'POST',
|
|
33
|
+
path: '/auth/local/register',
|
|
34
|
+
handler: 'auth.register',
|
|
35
|
+
config: {
|
|
36
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
37
|
+
prefix: '',
|
|
38
|
+
},
|
|
39
|
+
request: {
|
|
40
|
+
body: { 'application/json': validator.registerBodySchema },
|
|
41
|
+
},
|
|
42
|
+
response: validator.authRegisterResponseSchema,
|
|
11
43
|
},
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
{
|
|
45
|
+
method: 'GET',
|
|
46
|
+
path: '/auth/:provider/callback',
|
|
47
|
+
handler: 'auth.callback',
|
|
48
|
+
config: {
|
|
49
|
+
prefix: '',
|
|
50
|
+
},
|
|
51
|
+
request: {
|
|
52
|
+
params: {
|
|
53
|
+
provider: validator.providerParam,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
response: validator.authResponseSchema,
|
|
20
57
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
{
|
|
59
|
+
method: 'POST',
|
|
60
|
+
path: '/auth/forgot-password',
|
|
61
|
+
handler: 'auth.forgotPassword',
|
|
62
|
+
config: {
|
|
63
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
64
|
+
prefix: '',
|
|
65
|
+
},
|
|
66
|
+
request: {
|
|
67
|
+
body: { 'application/json': validator.forgotPasswordBodySchema },
|
|
68
|
+
},
|
|
69
|
+
response: validator.forgotPasswordResponseSchema,
|
|
29
70
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
71
|
+
{
|
|
72
|
+
method: 'POST',
|
|
73
|
+
path: '/auth/reset-password',
|
|
74
|
+
handler: 'auth.resetPassword',
|
|
75
|
+
config: {
|
|
76
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
77
|
+
prefix: '',
|
|
78
|
+
},
|
|
79
|
+
request: {
|
|
80
|
+
body: { 'application/json': validator.resetPasswordBodySchema },
|
|
81
|
+
},
|
|
82
|
+
response: validator.authResponseSchema,
|
|
37
83
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
prefix: '',
|
|
84
|
+
{
|
|
85
|
+
method: 'GET',
|
|
86
|
+
path: '/auth/email-confirmation',
|
|
87
|
+
handler: 'auth.emailConfirmation',
|
|
88
|
+
config: {
|
|
89
|
+
prefix: '',
|
|
90
|
+
},
|
|
46
91
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
92
|
+
{
|
|
93
|
+
method: 'POST',
|
|
94
|
+
path: '/auth/send-email-confirmation',
|
|
95
|
+
handler: 'auth.sendEmailConfirmation',
|
|
96
|
+
config: {
|
|
97
|
+
prefix: '',
|
|
98
|
+
},
|
|
99
|
+
request: {
|
|
100
|
+
body: { 'application/json': validator.sendEmailConfirmationBodySchema },
|
|
101
|
+
},
|
|
102
|
+
response: validator.sendEmailConfirmationResponseSchema,
|
|
55
103
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
104
|
+
{
|
|
105
|
+
method: 'POST',
|
|
106
|
+
path: '/auth/change-password',
|
|
107
|
+
handler: 'auth.changePassword',
|
|
108
|
+
config: {
|
|
109
|
+
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
110
|
+
prefix: '',
|
|
111
|
+
},
|
|
112
|
+
request: {
|
|
113
|
+
body: { 'application/json': validator.changePasswordBodySchema },
|
|
114
|
+
},
|
|
115
|
+
response: validator.authResponseSchema,
|
|
63
116
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
config: {
|
|
70
|
-
prefix: '',
|
|
117
|
+
{
|
|
118
|
+
method: 'POST',
|
|
119
|
+
path: '/auth/refresh',
|
|
120
|
+
handler: 'auth.refresh',
|
|
121
|
+
config: { prefix: '' },
|
|
71
122
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
config: {
|
|
78
|
-
middlewares: ['plugin::users-permissions.rateLimit'],
|
|
79
|
-
prefix: '',
|
|
123
|
+
{
|
|
124
|
+
method: 'POST',
|
|
125
|
+
path: '/auth/logout',
|
|
126
|
+
handler: 'auth.logout',
|
|
127
|
+
config: { prefix: '' },
|
|
80
128
|
},
|
|
81
|
-
|
|
82
|
-
|
|
129
|
+
];
|
|
130
|
+
};
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createContentApiRoutesFactory } = require('@strapi/utils');
|
|
3
4
|
const authRoutes = require('./auth');
|
|
4
5
|
const userRoutes = require('./user');
|
|
5
6
|
const roleRoutes = require('./role');
|
|
6
7
|
const permissionsRoutes = require('./permissions');
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const createContentApiRoutes = createContentApiRoutesFactory(() => {
|
|
10
|
+
return [
|
|
11
|
+
...authRoutes(strapi),
|
|
12
|
+
...userRoutes(strapi),
|
|
13
|
+
...roleRoutes(strapi),
|
|
14
|
+
...permissionsRoutes(strapi),
|
|
15
|
+
];
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
module.exports = createContentApiRoutes;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const { UsersPermissionsRouteValidator } = require('./validation');
|
|
4
|
+
|
|
5
|
+
module.exports = (strapi) => {
|
|
6
|
+
const validator = new UsersPermissionsRouteValidator(strapi);
|
|
7
|
+
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/permissions',
|
|
12
|
+
handler: 'permissions.getPermissions',
|
|
13
|
+
response: validator.permissionsResponseSchema,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
};
|
|
@@ -1,29 +1,59 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
3
|
+
const { UsersPermissionsRouteValidator } = require('./validation');
|
|
4
|
+
|
|
5
|
+
module.exports = (strapi) => {
|
|
6
|
+
const validator = new UsersPermissionsRouteValidator(strapi);
|
|
7
|
+
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/roles/:id',
|
|
12
|
+
handler: 'role.findOne',
|
|
13
|
+
request: {
|
|
14
|
+
params: {
|
|
15
|
+
id: validator.roleIdParam,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
response: validator.roleResponseSchema,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
method: 'GET',
|
|
22
|
+
path: '/roles',
|
|
23
|
+
handler: 'role.find',
|
|
24
|
+
response: validator.rolesResponseSchema,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
method: 'POST',
|
|
28
|
+
path: '/roles',
|
|
29
|
+
handler: 'role.createRole',
|
|
30
|
+
request: {
|
|
31
|
+
body: { 'application/json': validator.createRoleBodySchema },
|
|
32
|
+
},
|
|
33
|
+
response: validator.roleSuccessResponseSchema,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
method: 'PUT',
|
|
37
|
+
path: '/roles/:role',
|
|
38
|
+
handler: 'role.updateRole',
|
|
39
|
+
request: {
|
|
40
|
+
params: {
|
|
41
|
+
role: validator.roleIdParam,
|
|
42
|
+
},
|
|
43
|
+
body: { 'application/json': validator.updateRoleBodySchema },
|
|
44
|
+
},
|
|
45
|
+
response: validator.roleSuccessResponseSchema,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
method: 'DELETE',
|
|
49
|
+
path: '/roles/:role',
|
|
50
|
+
handler: 'role.deleteRole',
|
|
51
|
+
request: {
|
|
52
|
+
params: {
|
|
53
|
+
role: validator.roleIdParam,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
response: validator.roleSuccessResponseSchema,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
};
|