@strapi/plugin-users-permissions 5.49.0 → 5.50.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.
- package/.eslintrc.cjs +1 -0
- package/admin/src/pages/AdvancedSettings/index.jsx +1 -1
- package/admin/src/pages/Roles/pages/ListPage/index.jsx +1 -1
- package/admin/src/translations/ja.json +46 -7
- package/dist/admin/pages/AdvancedSettings/index.js +1 -1
- package/dist/admin/pages/AdvancedSettings/index.js.map +1 -1
- package/dist/admin/pages/AdvancedSettings/index.mjs +1 -1
- package/dist/admin/pages/AdvancedSettings/index.mjs.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs +1 -1
- package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
- package/dist/admin/translations/ja.json.js +46 -7
- package/dist/admin/translations/ja.json.js.map +1 -1
- package/dist/admin/translations/ja.json.mjs +46 -7
- package/dist/admin/translations/ja.json.mjs.map +1 -1
- package/dist/server/controllers/auth.js +133 -114
- package/dist/server/controllers/auth.js.map +1 -1
- package/dist/server/controllers/auth.mjs +132 -113
- package/dist/server/controllers/auth.mjs.map +1 -1
- package/dist/server/controllers/validation/user.js +16 -9
- package/dist/server/controllers/validation/user.js.map +1 -1
- package/dist/server/controllers/validation/user.mjs +16 -9
- package/dist/server/controllers/validation/user.mjs.map +1 -1
- package/dist/server/routes/content-api/auth.js +16 -0
- package/dist/server/routes/content-api/auth.js.map +1 -1
- package/dist/server/routes/content-api/auth.mjs +16 -0
- package/dist/server/routes/content-api/auth.mjs.map +1 -1
- package/dist/server/routes/content-api/validation.js +29 -2
- package/dist/server/routes/content-api/validation.js.map +1 -1
- package/dist/server/routes/content-api/validation.mjs +29 -2
- package/dist/server/routes/content-api/validation.mjs.map +1 -1
- package/dist/server/services/jwt.js +8 -6
- package/dist/server/services/jwt.js.map +1 -1
- package/dist/server/services/jwt.mjs +8 -6
- package/dist/server/services/jwt.mjs.map +1 -1
- package/dist/server/services/users-permissions.js +8 -0
- package/dist/server/services/users-permissions.js.map +1 -1
- package/dist/server/services/users-permissions.mjs +8 -0
- package/dist/server/services/users-permissions.mjs.map +1 -1
- package/dist/server/strategies/users-permissions.js +8 -1
- package/dist/server/strategies/users-permissions.js.map +1 -1
- package/dist/server/strategies/users-permissions.mjs +8 -1
- package/dist/server/strategies/users-permissions.mjs.map +1 -1
- package/dist/server/utils/refresh-cookie-options.js +27 -0
- package/dist/server/utils/refresh-cookie-options.js.map +1 -0
- package/dist/server/utils/refresh-cookie-options.mjs +25 -0
- package/dist/server/utils/refresh-cookie-options.mjs.map +1 -0
- package/lint-staged.config.mjs +1 -0
- package/package.json +5 -5
- package/rollup.config.mjs +3 -3
- package/server/controllers/auth.js +162 -141
- package/server/controllers/validation/user.js +22 -9
- package/server/routes/content-api/auth.js +12 -0
- package/server/routes/content-api/validation.js +32 -2
- package/server/services/jwt.js +4 -3
- package/server/services/users-permissions.js +2 -0
- package/server/strategies/users-permissions.js +6 -1
- package/server/utils/refresh-cookie-options.js +20 -0
|
@@ -6,6 +6,17 @@ const deleteRoleSchema = yup.object().shape({
|
|
|
6
6
|
role: yup.strapiID().required(),
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
+
// A role relation entry may be referenced by numeric `id` (legacy) or by
|
|
10
|
+
// `documentId` (the v5 default), but must carry at least one of them.
|
|
11
|
+
const roleRelationEntrySchema = yup
|
|
12
|
+
.object()
|
|
13
|
+
.shape({ id: yup.strapiID(), documentId: yup.strapiID() })
|
|
14
|
+
.test(
|
|
15
|
+
'id-or-documentId',
|
|
16
|
+
'Relation entry must include an id or documentId',
|
|
17
|
+
(entry) => !!entry && (entry.id != null || entry.documentId != null)
|
|
18
|
+
);
|
|
19
|
+
|
|
9
20
|
const createUserBodySchema = yup.object().shape({
|
|
10
21
|
email: yup.string().email().required(),
|
|
11
22
|
username: yup.string().min(1).required(),
|
|
@@ -17,7 +28,7 @@ const createUserBodySchema = yup.object().shape({
|
|
|
17
28
|
.shape({
|
|
18
29
|
connect: yup
|
|
19
30
|
.array()
|
|
20
|
-
.of(
|
|
31
|
+
.of(roleRelationEntrySchema)
|
|
21
32
|
.min(1, 'Users must have a role')
|
|
22
33
|
.required(),
|
|
23
34
|
})
|
|
@@ -44,20 +55,22 @@ const updateUserBodySchema = yup.object().shape({
|
|
|
44
55
|
role: yup.lazy((value) =>
|
|
45
56
|
typeof value === 'object'
|
|
46
57
|
? yup.object().shape({
|
|
47
|
-
connect
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
// connect/disconnect are each optional (matching core relation inputs),
|
|
59
|
+
// but a role must remain: reject disconnecting every role without
|
|
60
|
+
// connecting a replacement.
|
|
61
|
+
connect: yup.array().of(roleRelationEntrySchema),
|
|
51
62
|
disconnect: yup
|
|
52
63
|
.array()
|
|
53
|
-
.
|
|
54
|
-
|
|
64
|
+
.of(roleRelationEntrySchema)
|
|
65
|
+
.test('CheckDisconnect', 'Cannot remove role', function test(disconnect) {
|
|
66
|
+
const connectValue = value.connect || [];
|
|
67
|
+
const disconnectValue = disconnect || [];
|
|
68
|
+
if (connectValue.length === 0 && disconnectValue.length > 0) {
|
|
55
69
|
return false;
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
return true;
|
|
59
|
-
})
|
|
60
|
-
.required(),
|
|
73
|
+
}),
|
|
61
74
|
})
|
|
62
75
|
: yup.strapiID()
|
|
63
76
|
),
|
|
@@ -127,5 +127,17 @@ module.exports = (strapi) => {
|
|
|
127
127
|
handler: 'auth.logout',
|
|
128
128
|
config: { prefix: '' },
|
|
129
129
|
},
|
|
130
|
+
{
|
|
131
|
+
method: 'GET',
|
|
132
|
+
path: '/auth/sessions',
|
|
133
|
+
handler: 'auth.getSessions',
|
|
134
|
+
config: { prefix: '' },
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
method: 'DELETE',
|
|
138
|
+
path: '/auth/sessions/:sessionId',
|
|
139
|
+
handler: 'auth.revokeSession',
|
|
140
|
+
config: { prefix: '' },
|
|
141
|
+
},
|
|
130
142
|
];
|
|
131
143
|
};
|
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
const { AbstractRouteValidator } = require('@strapi/utils');
|
|
4
4
|
const z = require('zod/v4');
|
|
5
5
|
|
|
6
|
+
// A single role relation entry, referenced by numeric `id` (legacy) or by
|
|
7
|
+
// `documentId` (the v5 default), but must carry at least one of them. The
|
|
8
|
+
// detailed rules (min one role on create, cannot remove the last role on
|
|
9
|
+
// update) stay in the Yup controller validator.
|
|
10
|
+
const roleRelationEntry = z
|
|
11
|
+
.object({
|
|
12
|
+
id: z.union([z.number(), z.string()]).optional(),
|
|
13
|
+
documentId: z.string().optional(),
|
|
14
|
+
})
|
|
15
|
+
.refine((entry) => entry.id != null || entry.documentId != null, {
|
|
16
|
+
message: 'Relation entry must include an id or documentId',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// The `role` relation input: shorthand scalar (numeric id or documentId) or the
|
|
20
|
+
// longhand connect/disconnect object, matching every other v5 relation. The
|
|
21
|
+
// object form must carry at least one of connect/disconnect (an empty object is
|
|
22
|
+
// a no-op and almost certainly a mistake).
|
|
23
|
+
const roleRelationInput = z.union([
|
|
24
|
+
z.number(),
|
|
25
|
+
z.string(),
|
|
26
|
+
z
|
|
27
|
+
.object({
|
|
28
|
+
connect: z.array(roleRelationEntry).optional(),
|
|
29
|
+
disconnect: z.array(roleRelationEntry).optional(),
|
|
30
|
+
})
|
|
31
|
+
.refine((input) => input.connect != null || input.disconnect != null, {
|
|
32
|
+
message: 'Relation input must include connect or disconnect',
|
|
33
|
+
}),
|
|
34
|
+
]);
|
|
35
|
+
|
|
6
36
|
class UsersPermissionsRouteValidator extends AbstractRouteValidator {
|
|
7
37
|
constructor(strapi) {
|
|
8
38
|
super();
|
|
@@ -201,7 +231,7 @@ class UsersPermissionsRouteValidator extends AbstractRouteValidator {
|
|
|
201
231
|
username: z.string(),
|
|
202
232
|
email: z.email(),
|
|
203
233
|
password: z.string(),
|
|
204
|
-
role:
|
|
234
|
+
role: roleRelationInput.optional(),
|
|
205
235
|
});
|
|
206
236
|
}
|
|
207
237
|
|
|
@@ -210,7 +240,7 @@ class UsersPermissionsRouteValidator extends AbstractRouteValidator {
|
|
|
210
240
|
username: z.string().optional(),
|
|
211
241
|
email: z.email().optional(),
|
|
212
242
|
password: z.string().optional(),
|
|
213
|
-
role:
|
|
243
|
+
role: roleRelationInput.optional(),
|
|
214
244
|
});
|
|
215
245
|
}
|
|
216
246
|
|
package/server/services/jwt.js
CHANGED
|
@@ -80,17 +80,18 @@ module.exports = ({ strapi }) => ({
|
|
|
80
80
|
throw new Error('Invalid token.');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
// Surface the sessionId so the strategy can flag the "current" session.
|
|
84
|
+
return { id: user.id, sessionId: result.payload.sessionId };
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
return new Promise((resolve, reject) => {
|
|
87
88
|
const jwtConfig = strapi.config.get('plugin::users-permissions.jwt', {});
|
|
88
|
-
const algorithms = jwtConfig
|
|
89
|
+
const algorithms = [jwtConfig?.algorithm || 'HS256'];
|
|
89
90
|
|
|
90
91
|
jwt.verify(
|
|
91
92
|
token,
|
|
92
93
|
strapi.config.get('plugin::users-permissions.jwtSecret'),
|
|
93
|
-
|
|
94
|
+
{ algorithms },
|
|
94
95
|
(err, tokenPayload = {}) => {
|
|
95
96
|
if (err) {
|
|
96
97
|
return reject(new Error('Invalid token.'));
|
|
@@ -22,6 +22,8 @@ const DEFAULT_PERMISSIONS = [
|
|
|
22
22
|
{ action: 'plugin::users-permissions.auth.sendEmailConfirmation', roleType: 'public' },
|
|
23
23
|
{ action: 'plugin::users-permissions.auth.refresh', roleType: 'public' },
|
|
24
24
|
{ action: 'plugin::users-permissions.auth.logout', roleType: 'authenticated' },
|
|
25
|
+
{ action: 'plugin::users-permissions.auth.getSessions', roleType: 'authenticated' },
|
|
26
|
+
{ action: 'plugin::users-permissions.auth.revokeSession', roleType: 'authenticated' },
|
|
25
27
|
{ action: 'plugin::users-permissions.user.me', roleType: 'authenticated' },
|
|
26
28
|
{ action: 'plugin::users-permissions.auth.changePassword', roleType: 'authenticated' },
|
|
27
29
|
];
|
|
@@ -14,7 +14,7 @@ const authenticate = async (ctx) => {
|
|
|
14
14
|
const token = await getService('jwt').getToken(ctx);
|
|
15
15
|
|
|
16
16
|
if (token) {
|
|
17
|
-
const { id } = token;
|
|
17
|
+
const { id, sessionId } = token;
|
|
18
18
|
|
|
19
19
|
// Invalid token
|
|
20
20
|
if (id === undefined) {
|
|
@@ -49,6 +49,11 @@ const authenticate = async (ctx) => {
|
|
|
49
49
|
const ability = await strapi.contentAPI.permissions.engine.generateAbility(permissions);
|
|
50
50
|
|
|
51
51
|
ctx.state.user = user;
|
|
52
|
+
// Expose the session backing this request (refresh mode only) so endpoints
|
|
53
|
+
// can flag the "current" session when listing active sessions.
|
|
54
|
+
if (sessionId) {
|
|
55
|
+
ctx.state.session = { id: sessionId };
|
|
56
|
+
}
|
|
52
57
|
|
|
53
58
|
return {
|
|
54
59
|
authenticated: true,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const buildRefreshCookieOptions = (upSessions, isProduction) => {
|
|
4
|
+
const isSecure =
|
|
5
|
+
typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
httpOnly: true,
|
|
9
|
+
secure: isSecure,
|
|
10
|
+
sameSite: upSessions.cookie?.sameSite ?? 'lax',
|
|
11
|
+
path: upSessions.cookie?.path ?? '/',
|
|
12
|
+
domain: upSessions.cookie?.domain,
|
|
13
|
+
maxAge: upSessions.cookie?.maxAge,
|
|
14
|
+
overwrite: true,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
buildRefreshCookieOptions,
|
|
20
|
+
};
|