@strapi/plugin-users-permissions 0.0.0-4fc90398602f
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/LICENSE +22 -0
- package/README.md +1 -0
- package/admin/src/components/BoundRoute/getMethodColor.js +41 -0
- package/admin/src/components/BoundRoute/index.js +72 -0
- package/admin/src/components/FormModal/Input/index.js +121 -0
- package/admin/src/components/FormModal/index.js +121 -0
- package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +30 -0
- package/admin/src/components/Permissions/PermissionRow/SubCategory.js +114 -0
- package/admin/src/components/Permissions/PermissionRow/index.js +53 -0
- package/admin/src/components/Permissions/index.js +56 -0
- package/admin/src/components/Permissions/init.js +9 -0
- package/admin/src/components/Permissions/reducer.js +27 -0
- package/admin/src/components/Policies/index.js +60 -0
- package/admin/src/components/UsersPermissions/index.js +94 -0
- package/admin/src/components/UsersPermissions/init.js +10 -0
- package/admin/src/components/UsersPermissions/reducer.js +60 -0
- package/admin/src/contexts/UsersPermissionsContext/index.js +17 -0
- package/admin/src/hooks/index.js +5 -0
- package/admin/src/hooks/useFetchRole/index.js +64 -0
- package/admin/src/hooks/useFetchRole/reducer.js +31 -0
- package/admin/src/hooks/useForm/index.js +70 -0
- package/admin/src/hooks/useForm/reducer.js +40 -0
- package/admin/src/hooks/usePlugins/index.js +65 -0
- package/admin/src/hooks/usePlugins/init.js +5 -0
- package/admin/src/hooks/usePlugins/reducer.js +34 -0
- package/admin/src/hooks/useRolesList/index.js +63 -0
- package/admin/src/hooks/useRolesList/init.js +5 -0
- package/admin/src/hooks/useRolesList/reducer.js +31 -0
- package/admin/src/index.js +123 -0
- package/admin/src/pages/AdvancedSettings/index.js +238 -0
- package/admin/src/pages/AdvancedSettings/utils/api.js +13 -0
- package/admin/src/pages/AdvancedSettings/utils/layout.js +96 -0
- package/admin/src/pages/AdvancedSettings/utils/schema.js +19 -0
- package/admin/src/pages/EmailTemplates/components/EmailForm.js +173 -0
- package/admin/src/pages/EmailTemplates/components/EmailTable.js +121 -0
- package/admin/src/pages/EmailTemplates/index.js +162 -0
- package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
- package/admin/src/pages/EmailTemplates/utils/schema.js +22 -0
- package/admin/src/pages/Providers/index.js +274 -0
- package/admin/src/pages/Providers/reducer.js +54 -0
- package/admin/src/pages/Providers/utils/api.js +21 -0
- package/admin/src/pages/Providers/utils/createProvidersArray.js +21 -0
- package/admin/src/pages/Providers/utils/forms.js +244 -0
- package/admin/src/pages/Roles/CreatePage/index.js +177 -0
- package/admin/src/pages/Roles/CreatePage/utils/schema.js +9 -0
- package/admin/src/pages/Roles/EditPage/index.js +190 -0
- package/admin/src/pages/Roles/EditPage/utils/schema.js +9 -0
- package/admin/src/pages/Roles/ListPage/components/TableBody.js +96 -0
- package/admin/src/pages/Roles/ListPage/index.js +216 -0
- package/admin/src/pages/Roles/ListPage/utils/api.js +28 -0
- package/admin/src/pages/Roles/ProtectedCreatePage/index.js +12 -0
- package/admin/src/pages/Roles/ProtectedEditPage/index.js +12 -0
- package/admin/src/pages/Roles/ProtectedListPage/index.js +15 -0
- package/admin/src/pages/Roles/index.js +27 -0
- package/admin/src/permissions.js +31 -0
- package/admin/src/pluginId.js +5 -0
- package/admin/src/translations/ar.json +40 -0
- package/admin/src/translations/cs.json +46 -0
- package/admin/src/translations/de.json +58 -0
- package/admin/src/translations/dk.json +83 -0
- package/admin/src/translations/en.json +83 -0
- package/admin/src/translations/es.json +83 -0
- package/admin/src/translations/fr.json +46 -0
- package/admin/src/translations/id.json +58 -0
- package/admin/src/translations/it.json +58 -0
- package/admin/src/translations/ja.json +44 -0
- package/admin/src/translations/ko.json +83 -0
- package/admin/src/translations/ms.json +45 -0
- package/admin/src/translations/nl.json +44 -0
- package/admin/src/translations/pl.json +83 -0
- package/admin/src/translations/pt-BR.json +40 -0
- package/admin/src/translations/pt.json +44 -0
- package/admin/src/translations/ru.json +58 -0
- package/admin/src/translations/sk.json +46 -0
- package/admin/src/translations/sv.json +58 -0
- package/admin/src/translations/th.json +56 -0
- package/admin/src/translations/tr.json +44 -0
- package/admin/src/translations/uk.json +45 -0
- package/admin/src/translations/vi.json +46 -0
- package/admin/src/translations/zh-Hans.json +62 -0
- package/admin/src/translations/zh.json +44 -0
- package/admin/src/utils/axiosInstance.js +36 -0
- package/admin/src/utils/cleanPermissions.js +25 -0
- package/admin/src/utils/formatPluginName.js +26 -0
- package/admin/src/utils/formatPolicies.js +8 -0
- package/admin/src/utils/getRequestURL.js +5 -0
- package/admin/src/utils/getTrad.js +5 -0
- package/admin/src/utils/index.js +5 -0
- package/documentation/content-api.yaml +848 -0
- package/jest.config.front.js +10 -0
- package/package.json +60 -0
- package/server/bootstrap/grant-config.js +123 -0
- package/server/bootstrap/index.js +133 -0
- package/server/bootstrap/users-permissions-actions.js +80 -0
- package/server/config.js +23 -0
- package/server/content-types/index.js +11 -0
- package/server/content-types/permission/index.js +34 -0
- package/server/content-types/role/index.js +51 -0
- package/server/content-types/user/index.js +72 -0
- package/server/content-types/user/schema-config.js +15 -0
- package/server/controllers/auth.js +398 -0
- package/server/controllers/content-manager-user.js +175 -0
- package/server/controllers/index.js +17 -0
- package/server/controllers/permissions.js +26 -0
- package/server/controllers/role.js +77 -0
- package/server/controllers/settings.js +85 -0
- package/server/controllers/user.js +198 -0
- package/server/controllers/validation/auth.js +57 -0
- package/server/controllers/validation/email-template.js +50 -0
- package/server/controllers/validation/user.js +26 -0
- package/server/graphql/index.js +44 -0
- package/server/graphql/mutations/auth/change-password.js +38 -0
- package/server/graphql/mutations/auth/email-confirmation.js +39 -0
- package/server/graphql/mutations/auth/forgot-password.js +35 -0
- package/server/graphql/mutations/auth/login.js +35 -0
- package/server/graphql/mutations/auth/register.js +36 -0
- package/server/graphql/mutations/auth/reset-password.js +38 -0
- package/server/graphql/mutations/crud/role/create-role.js +34 -0
- package/server/graphql/mutations/crud/role/delete-role.js +25 -0
- package/server/graphql/mutations/crud/role/update-role.js +35 -0
- package/server/graphql/mutations/crud/user/create-user.js +45 -0
- package/server/graphql/mutations/crud/user/delete-user.js +39 -0
- package/server/graphql/mutations/crud/user/update-user.js +46 -0
- package/server/graphql/mutations/index.js +43 -0
- package/server/graphql/queries/index.js +13 -0
- package/server/graphql/queries/me.js +17 -0
- package/server/graphql/resolvers-configs.js +42 -0
- package/server/graphql/types/create-role-payload.js +11 -0
- package/server/graphql/types/delete-role-payload.js +11 -0
- package/server/graphql/types/index.js +21 -0
- package/server/graphql/types/login-input.js +13 -0
- package/server/graphql/types/login-payload.js +12 -0
- package/server/graphql/types/me-role.js +14 -0
- package/server/graphql/types/me.js +16 -0
- package/server/graphql/types/password-payload.js +11 -0
- package/server/graphql/types/register-input.js +13 -0
- package/server/graphql/types/update-role-payload.js +11 -0
- package/server/graphql/utils.js +27 -0
- package/server/index.js +21 -0
- package/server/middlewares/index.js +7 -0
- package/server/middlewares/rateLimit.js +27 -0
- package/server/register.js +23 -0
- package/server/routes/admin/index.js +10 -0
- package/server/routes/admin/permissions.js +20 -0
- package/server/routes/admin/role.js +79 -0
- package/server/routes/admin/settings.js +95 -0
- package/server/routes/content-api/auth.js +82 -0
- package/server/routes/content-api/index.js +11 -0
- package/server/routes/content-api/permissions.js +9 -0
- package/server/routes/content-api/role.js +29 -0
- package/server/routes/content-api/user.js +60 -0
- package/server/routes/index.js +6 -0
- package/server/services/index.js +17 -0
- package/server/services/jwt.js +55 -0
- package/server/services/providers-registry.js +292 -0
- package/server/services/providers.js +115 -0
- package/server/services/role.js +177 -0
- package/server/services/user.js +140 -0
- package/server/services/users-permissions.js +236 -0
- package/server/strategies/users-permissions.js +102 -0
- package/server/utils/index.d.ts +16 -0
- package/server/utils/index.js +12 -0
- package/server/utils/sanitize/index.js +9 -0
- package/server/utils/sanitize/sanitizers.js +19 -0
- package/server/utils/sanitize/visitors/index.js +5 -0
- package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +11 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +3 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auth.js controller
|
|
5
|
+
*
|
|
6
|
+
* @description: A set of functions called "actions" for managing `Auth`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* eslint-disable no-useless-escape */
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const _ = require('lodash');
|
|
12
|
+
const utils = require('@strapi/utils');
|
|
13
|
+
const { getService } = require('../utils');
|
|
14
|
+
const {
|
|
15
|
+
validateCallbackBody,
|
|
16
|
+
validateRegisterBody,
|
|
17
|
+
validateSendEmailConfirmationBody,
|
|
18
|
+
validateForgotPasswordBody,
|
|
19
|
+
validateResetPasswordBody,
|
|
20
|
+
validateEmailConfirmationBody,
|
|
21
|
+
validateChangePasswordBody,
|
|
22
|
+
} = require('./validation/auth');
|
|
23
|
+
|
|
24
|
+
const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
|
|
25
|
+
const { ApplicationError, ValidationError } = utils.errors;
|
|
26
|
+
|
|
27
|
+
const sanitizeUser = (user, ctx) => {
|
|
28
|
+
const { auth } = ctx.state;
|
|
29
|
+
const userSchema = strapi.getModel('plugin::users-permissions.user');
|
|
30
|
+
|
|
31
|
+
return sanitize.contentAPI.output(user, userSchema, { auth });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
async callback(ctx) {
|
|
36
|
+
const provider = ctx.params.provider || 'local';
|
|
37
|
+
const params = ctx.request.body;
|
|
38
|
+
|
|
39
|
+
const store = strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
40
|
+
const grantSettings = await store.get({ key: 'grant' });
|
|
41
|
+
|
|
42
|
+
const grantProvider = provider === 'local' ? 'email' : provider;
|
|
43
|
+
|
|
44
|
+
if (!_.get(grantSettings, [grantProvider, 'enabled'])) {
|
|
45
|
+
throw new ApplicationError('This provider is disabled');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (provider === 'local') {
|
|
49
|
+
await validateCallbackBody(params);
|
|
50
|
+
|
|
51
|
+
const { identifier } = params;
|
|
52
|
+
|
|
53
|
+
// Check if the user exists.
|
|
54
|
+
const user = await strapi.query('plugin::users-permissions.user').findOne({
|
|
55
|
+
where: {
|
|
56
|
+
provider,
|
|
57
|
+
$or: [{ email: identifier.toLowerCase() }, { username: identifier }],
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!user) {
|
|
62
|
+
throw new ValidationError('Invalid identifier or password');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!user.password) {
|
|
66
|
+
throw new ValidationError('Invalid identifier or password');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const validPassword = await getService('user').validatePassword(
|
|
70
|
+
params.password,
|
|
71
|
+
user.password
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!validPassword) {
|
|
75
|
+
throw new ValidationError('Invalid identifier or password');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const advancedSettings = await store.get({ key: 'advanced' });
|
|
79
|
+
const requiresConfirmation = _.get(advancedSettings, 'email_confirmation');
|
|
80
|
+
|
|
81
|
+
if (requiresConfirmation && user.confirmed !== true) {
|
|
82
|
+
throw new ApplicationError('Your account email is not confirmed');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (user.blocked === true) {
|
|
86
|
+
throw new ApplicationError('Your account has been blocked by an administrator');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return ctx.send({
|
|
90
|
+
jwt: getService('jwt').issue({ id: user.id }),
|
|
91
|
+
user: await sanitizeUser(user, ctx),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async changePassword(ctx) {
|
|
97
|
+
if (!ctx.state.user) {
|
|
98
|
+
throw new ApplicationError('You must be authenticated to reset your password');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { currentPassword, password } = await validateChangePasswordBody(ctx.request.body);
|
|
102
|
+
|
|
103
|
+
const user = await strapi.entityService.findOne(
|
|
104
|
+
'plugin::users-permissions.user',
|
|
105
|
+
ctx.state.user.id
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const validPassword = await getService('user').validatePassword(currentPassword, user.password);
|
|
109
|
+
|
|
110
|
+
if (!validPassword) {
|
|
111
|
+
throw new ValidationError('The provided current password is invalid');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (currentPassword === password) {
|
|
115
|
+
throw new ValidationError('Your new password must be different than your current password');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await getService('user').edit(user.id, { password });
|
|
119
|
+
|
|
120
|
+
ctx.send({
|
|
121
|
+
jwt: getService('jwt').issue({ id: user.id }),
|
|
122
|
+
user: await sanitizeUser(user, ctx),
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
async resetPassword(ctx) {
|
|
127
|
+
const { password, passwordConfirmation, code } = await validateResetPasswordBody(
|
|
128
|
+
ctx.request.body
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
if (password !== passwordConfirmation) {
|
|
132
|
+
throw new ValidationError('Passwords do not match');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const user = await strapi
|
|
136
|
+
.query('plugin::users-permissions.user')
|
|
137
|
+
.findOne({ where: { resetPasswordToken: code } });
|
|
138
|
+
|
|
139
|
+
if (!user) {
|
|
140
|
+
throw new ValidationError('Incorrect code provided');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await getService('user').edit(user.id, {
|
|
144
|
+
resetPasswordToken: null,
|
|
145
|
+
password,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Update the user.
|
|
149
|
+
ctx.send({
|
|
150
|
+
jwt: getService('jwt').issue({ id: user.id }),
|
|
151
|
+
user: await sanitizeUser(user, ctx),
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async connect(ctx, next) {
|
|
156
|
+
const grant = require('grant-koa');
|
|
157
|
+
|
|
158
|
+
const providers = await strapi
|
|
159
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
|
|
160
|
+
.get();
|
|
161
|
+
|
|
162
|
+
const apiPrefix = strapi.config.get('api.rest.prefix');
|
|
163
|
+
const grantConfig = {
|
|
164
|
+
defaults: {
|
|
165
|
+
prefix: `${apiPrefix}/connect`,
|
|
166
|
+
},
|
|
167
|
+
...providers,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const [requestPath] = ctx.request.url.split('?');
|
|
171
|
+
const provider = requestPath.split('/connect/')[1].split('/')[0];
|
|
172
|
+
|
|
173
|
+
if (!_.get(grantConfig[provider], 'enabled')) {
|
|
174
|
+
throw new ApplicationError('This provider is disabled');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!strapi.config.server.url.startsWith('http')) {
|
|
178
|
+
strapi.log.warn(
|
|
179
|
+
'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Ability to pass OAuth callback dynamically
|
|
184
|
+
grantConfig[provider].callback =
|
|
185
|
+
_.get(ctx, 'query.callback') ||
|
|
186
|
+
_.get(ctx, 'session.grant.dynamic.callback') ||
|
|
187
|
+
grantConfig[provider].callback;
|
|
188
|
+
grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
|
|
189
|
+
|
|
190
|
+
return grant(grantConfig)(ctx, next);
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async forgotPassword(ctx) {
|
|
194
|
+
const { email } = await validateForgotPasswordBody(ctx.request.body);
|
|
195
|
+
|
|
196
|
+
const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
197
|
+
|
|
198
|
+
const emailSettings = await pluginStore.get({ key: 'email' });
|
|
199
|
+
const advancedSettings = await pluginStore.get({ key: 'advanced' });
|
|
200
|
+
|
|
201
|
+
// Find the user by email.
|
|
202
|
+
const user = await strapi
|
|
203
|
+
.query('plugin::users-permissions.user')
|
|
204
|
+
.findOne({ where: { email: email.toLowerCase() } });
|
|
205
|
+
|
|
206
|
+
if (!user || user.blocked) {
|
|
207
|
+
return ctx.send({ ok: true });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Generate random token.
|
|
211
|
+
const userInfo = await sanitizeUser(user, ctx);
|
|
212
|
+
|
|
213
|
+
const resetPasswordToken = crypto.randomBytes(64).toString('hex');
|
|
214
|
+
|
|
215
|
+
const resetPasswordSettings = _.get(emailSettings, 'reset_password.options', {});
|
|
216
|
+
const emailBody = await getService('users-permissions').template(
|
|
217
|
+
resetPasswordSettings.message,
|
|
218
|
+
{
|
|
219
|
+
URL: advancedSettings.email_reset_password,
|
|
220
|
+
SERVER_URL: getAbsoluteServerUrl(strapi.config),
|
|
221
|
+
ADMIN_URL: getAbsoluteAdminUrl(strapi.config),
|
|
222
|
+
USER: userInfo,
|
|
223
|
+
TOKEN: resetPasswordToken,
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const emailObject = await getService('users-permissions').template(
|
|
228
|
+
resetPasswordSettings.object,
|
|
229
|
+
{
|
|
230
|
+
USER: userInfo,
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const emailToSend = {
|
|
235
|
+
to: user.email,
|
|
236
|
+
from:
|
|
237
|
+
resetPasswordSettings.from.email || resetPasswordSettings.from.name
|
|
238
|
+
? `${resetPasswordSettings.from.name} <${resetPasswordSettings.from.email}>`
|
|
239
|
+
: undefined,
|
|
240
|
+
replyTo: resetPasswordSettings.response_email,
|
|
241
|
+
subject: emailObject,
|
|
242
|
+
text: emailBody,
|
|
243
|
+
html: emailBody,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// NOTE: Update the user before sending the email so an Admin can generate the link if the email fails
|
|
247
|
+
await getService('user').edit(user.id, { resetPasswordToken });
|
|
248
|
+
|
|
249
|
+
// Send an email to the user.
|
|
250
|
+
await strapi.plugin('email').service('email').send(emailToSend);
|
|
251
|
+
|
|
252
|
+
ctx.send({ ok: true });
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
async register(ctx) {
|
|
256
|
+
const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
257
|
+
|
|
258
|
+
const settings = await pluginStore.get({ key: 'advanced' });
|
|
259
|
+
|
|
260
|
+
if (!settings.allow_register) {
|
|
261
|
+
throw new ApplicationError('Register action is currently disabled');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const params = {
|
|
265
|
+
..._.omit(ctx.request.body, [
|
|
266
|
+
'confirmed',
|
|
267
|
+
'blocked',
|
|
268
|
+
'confirmationToken',
|
|
269
|
+
'resetPasswordToken',
|
|
270
|
+
'provider',
|
|
271
|
+
]),
|
|
272
|
+
provider: 'local',
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
await validateRegisterBody(params);
|
|
276
|
+
|
|
277
|
+
const role = await strapi
|
|
278
|
+
.query('plugin::users-permissions.role')
|
|
279
|
+
.findOne({ where: { type: settings.default_role } });
|
|
280
|
+
|
|
281
|
+
if (!role) {
|
|
282
|
+
throw new ApplicationError('Impossible to find the default role');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const { email, username, provider } = params;
|
|
286
|
+
|
|
287
|
+
const identifierFilter = {
|
|
288
|
+
$or: [
|
|
289
|
+
{ email: email.toLowerCase() },
|
|
290
|
+
{ username: email.toLowerCase() },
|
|
291
|
+
{ username },
|
|
292
|
+
{ email: username },
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
|
|
297
|
+
where: { ...identifierFilter, provider },
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (conflictingUserCount > 0) {
|
|
301
|
+
throw new ApplicationError('Email or Username are already taken');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (settings.unique_email) {
|
|
305
|
+
const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
|
|
306
|
+
where: { ...identifierFilter },
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (conflictingUserCount > 0) {
|
|
310
|
+
throw new ApplicationError('Email or Username are already taken');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const newUser = {
|
|
315
|
+
...params,
|
|
316
|
+
role: role.id,
|
|
317
|
+
email: email.toLowerCase(),
|
|
318
|
+
username,
|
|
319
|
+
confirmed: !settings.email_confirmation,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const user = await getService('user').add(newUser);
|
|
323
|
+
|
|
324
|
+
const sanitizedUser = await sanitizeUser(user, ctx);
|
|
325
|
+
|
|
326
|
+
if (settings.email_confirmation) {
|
|
327
|
+
try {
|
|
328
|
+
await getService('user').sendConfirmationEmail(sanitizedUser);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
throw new ApplicationError(err.message);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return ctx.send({ user: sanitizedUser });
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const jwt = getService('jwt').issue(_.pick(user, ['id']));
|
|
337
|
+
|
|
338
|
+
return ctx.send({
|
|
339
|
+
jwt,
|
|
340
|
+
user: sanitizedUser,
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
async emailConfirmation(ctx, next, returnUser) {
|
|
345
|
+
const { confirmation: confirmationToken } = await validateEmailConfirmationBody(ctx.query);
|
|
346
|
+
|
|
347
|
+
const userService = getService('user');
|
|
348
|
+
const jwtService = getService('jwt');
|
|
349
|
+
|
|
350
|
+
const [user] = await userService.fetchAll({ filters: { confirmationToken } });
|
|
351
|
+
|
|
352
|
+
if (!user) {
|
|
353
|
+
throw new ValidationError('Invalid token');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
await userService.edit(user.id, { confirmed: true, confirmationToken: null });
|
|
357
|
+
|
|
358
|
+
if (returnUser) {
|
|
359
|
+
ctx.send({
|
|
360
|
+
jwt: jwtService.issue({ id: user.id }),
|
|
361
|
+
user: await sanitizeUser(user, ctx),
|
|
362
|
+
});
|
|
363
|
+
} else {
|
|
364
|
+
const settings = await strapi
|
|
365
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
366
|
+
.get();
|
|
367
|
+
|
|
368
|
+
ctx.redirect(settings.email_confirmation_redirection || '/');
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
async sendEmailConfirmation(ctx) {
|
|
373
|
+
const { email } = await validateSendEmailConfirmationBody(ctx.request.body);
|
|
374
|
+
|
|
375
|
+
const user = await strapi.query('plugin::users-permissions.user').findOne({
|
|
376
|
+
where: { email: email.toLowerCase() },
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (!user) {
|
|
380
|
+
return ctx.send({ email, sent: true });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (user.confirmed) {
|
|
384
|
+
throw new ApplicationError('Already confirmed');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (user.blocked) {
|
|
388
|
+
throw new ApplicationError('User blocked');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
await getService('user').sendConfirmationEmail(user);
|
|
392
|
+
|
|
393
|
+
ctx.send({
|
|
394
|
+
email: user.email,
|
|
395
|
+
sent: true,
|
|
396
|
+
});
|
|
397
|
+
},
|
|
398
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
|
|
5
|
+
const { ApplicationError, ValidationError, NotFoundError, ForbiddenError } =
|
|
6
|
+
require('@strapi/utils').errors;
|
|
7
|
+
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
8
|
+
|
|
9
|
+
const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
|
|
10
|
+
|
|
11
|
+
const userModel = 'plugin::users-permissions.user';
|
|
12
|
+
const ACTIONS = {
|
|
13
|
+
read: 'plugin::content-manager.explorer.read',
|
|
14
|
+
create: 'plugin::content-manager.explorer.create',
|
|
15
|
+
edit: 'plugin::content-manager.explorer.update',
|
|
16
|
+
delete: 'plugin::content-manager.explorer.delete',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
|
20
|
+
const entity = await strapi.query(userModel).findOne({
|
|
21
|
+
where: { id },
|
|
22
|
+
populate: [`${CREATED_BY_ATTRIBUTE}.roles`],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (_.isNil(entity)) {
|
|
26
|
+
throw new NotFoundError();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
|
|
30
|
+
|
|
31
|
+
if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
|
|
32
|
+
throw new ForbiddenError();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const entityWithoutCreatorRoles = _.omit(entity, `${CREATED_BY_ATTRIBUTE}.roles`);
|
|
36
|
+
|
|
37
|
+
return { pm, entity: entityWithoutCreatorRoles };
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
/**
|
|
42
|
+
* Create a/an user record.
|
|
43
|
+
* @return {Object}
|
|
44
|
+
*/
|
|
45
|
+
async create(ctx) {
|
|
46
|
+
const { body } = ctx.request;
|
|
47
|
+
const { user: admin, userAbility } = ctx.state;
|
|
48
|
+
|
|
49
|
+
const { email, username } = body;
|
|
50
|
+
|
|
51
|
+
const pm = strapi.admin.services.permission.createPermissionsManager({
|
|
52
|
+
ability: userAbility,
|
|
53
|
+
action: ACTIONS.create,
|
|
54
|
+
model: userModel,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!pm.isAllowed) {
|
|
58
|
+
return ctx.forbidden();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sanitizedBody = await pm.pickPermittedFieldsOf(body, { subject: userModel });
|
|
62
|
+
|
|
63
|
+
const advanced = await strapi
|
|
64
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
65
|
+
.get();
|
|
66
|
+
|
|
67
|
+
await validateCreateUserBody(ctx.request.body);
|
|
68
|
+
|
|
69
|
+
const userWithSameUsername = await strapi
|
|
70
|
+
.query('plugin::users-permissions.user')
|
|
71
|
+
.findOne({ where: { username } });
|
|
72
|
+
|
|
73
|
+
if (userWithSameUsername) {
|
|
74
|
+
throw new ApplicationError('Username already taken');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (advanced.unique_email) {
|
|
78
|
+
const userWithSameEmail = await strapi
|
|
79
|
+
.query('plugin::users-permissions.user')
|
|
80
|
+
.findOne({ where: { email: email.toLowerCase() } });
|
|
81
|
+
|
|
82
|
+
if (userWithSameEmail) {
|
|
83
|
+
throw new ApplicationError('Email already taken');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const user = {
|
|
88
|
+
...sanitizedBody,
|
|
89
|
+
provider: 'local',
|
|
90
|
+
[CREATED_BY_ATTRIBUTE]: admin.id,
|
|
91
|
+
[UPDATED_BY_ATTRIBUTE]: admin.id,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
user.email = _.toLower(user.email);
|
|
95
|
+
|
|
96
|
+
if (!user.role) {
|
|
97
|
+
const defaultRole = await strapi
|
|
98
|
+
.query('plugin::users-permissions.role')
|
|
99
|
+
.findOne({ where: { type: advanced.default_role } });
|
|
100
|
+
|
|
101
|
+
user.role = defaultRole.id;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const data = await strapi
|
|
106
|
+
.service('plugin::content-manager.entity-manager')
|
|
107
|
+
.create(user, userModel);
|
|
108
|
+
const sanitizedData = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
109
|
+
|
|
110
|
+
ctx.created(sanitizedData);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new ApplicationError(error.message);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Update a/an user record.
|
|
117
|
+
* @return {Object}
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
async update(ctx) {
|
|
121
|
+
const { id } = ctx.params;
|
|
122
|
+
const { body } = ctx.request;
|
|
123
|
+
const { user: admin, userAbility } = ctx.state;
|
|
124
|
+
|
|
125
|
+
const advancedConfigs = await strapi
|
|
126
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
127
|
+
.get();
|
|
128
|
+
|
|
129
|
+
const { email, username, password } = body;
|
|
130
|
+
|
|
131
|
+
const { pm, entity } = await findEntityAndCheckPermissions(
|
|
132
|
+
userAbility,
|
|
133
|
+
ACTIONS.edit,
|
|
134
|
+
userModel,
|
|
135
|
+
id
|
|
136
|
+
);
|
|
137
|
+
const user = entity;
|
|
138
|
+
|
|
139
|
+
await validateUpdateUserBody(ctx.request.body);
|
|
140
|
+
|
|
141
|
+
if (_.has(body, 'password') && !password && user.provider === 'local') {
|
|
142
|
+
throw new ValidationError('password.notNull');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (_.has(body, 'username')) {
|
|
146
|
+
const userWithSameUsername = await strapi
|
|
147
|
+
.query('plugin::users-permissions.user')
|
|
148
|
+
.findOne({ where: { username } });
|
|
149
|
+
|
|
150
|
+
if (userWithSameUsername && userWithSameUsername.id != id) {
|
|
151
|
+
throw new ApplicationError('Username already taken');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (_.has(body, 'email') && advancedConfigs.unique_email) {
|
|
156
|
+
const userWithSameEmail = await strapi
|
|
157
|
+
.query('plugin::users-permissions.user')
|
|
158
|
+
.findOne({ where: { email: _.toLower(email) } });
|
|
159
|
+
|
|
160
|
+
if (userWithSameEmail && userWithSameEmail.id != id) {
|
|
161
|
+
throw new ApplicationError('Email already taken');
|
|
162
|
+
}
|
|
163
|
+
body.email = _.toLower(body.email);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const sanitizedData = await pm.pickPermittedFieldsOf(body, { subject: pm.toSubject(user) });
|
|
167
|
+
const updateData = _.omit({ ...sanitizedData, updatedBy: admin.id }, 'createdBy');
|
|
168
|
+
|
|
169
|
+
const data = await strapi
|
|
170
|
+
.service('plugin::content-manager.entity-manager')
|
|
171
|
+
.update({ id }, updateData, userModel);
|
|
172
|
+
|
|
173
|
+
ctx.body = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
174
|
+
},
|
|
175
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const auth = require('./auth');
|
|
4
|
+
const user = require('./user');
|
|
5
|
+
const role = require('./role');
|
|
6
|
+
const permissions = require('./permissions');
|
|
7
|
+
const settings = require('./settings');
|
|
8
|
+
const contentmanageruser = require('./content-manager-user');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
auth,
|
|
12
|
+
user,
|
|
13
|
+
role,
|
|
14
|
+
permissions,
|
|
15
|
+
settings,
|
|
16
|
+
contentmanageruser,
|
|
17
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { getService } = require('../utils');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
async getPermissions(ctx) {
|
|
8
|
+
const permissions = await getService('users-permissions').getActions();
|
|
9
|
+
|
|
10
|
+
ctx.send({ permissions });
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async getPolicies(ctx) {
|
|
14
|
+
const policies = _.keys(strapi.plugin('users-permissions').policies);
|
|
15
|
+
|
|
16
|
+
ctx.send({
|
|
17
|
+
policies: _.without(policies, 'permissions'),
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async getRoutes(ctx) {
|
|
22
|
+
const routes = await getService('users-permissions').getRoutes();
|
|
23
|
+
|
|
24
|
+
ctx.send({ routes });
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { ApplicationError, ValidationError } = require('@strapi/utils').errors;
|
|
5
|
+
const { getService } = require('../utils');
|
|
6
|
+
const { validateDeleteRoleBody } = require('./validation/user');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
/**
|
|
10
|
+
* Default action.
|
|
11
|
+
*
|
|
12
|
+
* @return {Object}
|
|
13
|
+
*/
|
|
14
|
+
async createRole(ctx) {
|
|
15
|
+
if (_.isEmpty(ctx.request.body)) {
|
|
16
|
+
throw new ValidationError('Request body cannot be empty');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await getService('role').createRole(ctx.request.body);
|
|
20
|
+
|
|
21
|
+
ctx.send({ ok: true });
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
async findOne(ctx) {
|
|
25
|
+
const { id } = ctx.params;
|
|
26
|
+
|
|
27
|
+
const role = await getService('role').findOne(id);
|
|
28
|
+
|
|
29
|
+
if (!role) {
|
|
30
|
+
return ctx.notFound();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ctx.send({ role });
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
async find(ctx) {
|
|
37
|
+
const roles = await getService('role').find();
|
|
38
|
+
|
|
39
|
+
ctx.send({ roles });
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async updateRole(ctx) {
|
|
43
|
+
const roleID = ctx.params.role;
|
|
44
|
+
|
|
45
|
+
if (_.isEmpty(ctx.request.body)) {
|
|
46
|
+
throw new ValidationError('Request body cannot be empty');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await getService('role').updateRole(roleID, ctx.request.body);
|
|
50
|
+
|
|
51
|
+
ctx.send({ ok: true });
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
async deleteRole(ctx) {
|
|
55
|
+
const roleID = ctx.params.role;
|
|
56
|
+
|
|
57
|
+
if (!roleID) {
|
|
58
|
+
await validateDeleteRoleBody(ctx.params);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Fetch public role.
|
|
62
|
+
const publicRole = await strapi
|
|
63
|
+
.query('plugin::users-permissions.role')
|
|
64
|
+
.findOne({ where: { type: 'public' } });
|
|
65
|
+
|
|
66
|
+
const publicRoleID = publicRole.id;
|
|
67
|
+
|
|
68
|
+
// Prevent from removing the public role.
|
|
69
|
+
if (roleID.toString() === publicRoleID.toString()) {
|
|
70
|
+
throw new ApplicationError('Cannot delete public role');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await getService('role').deleteRole(roleID, publicRoleID);
|
|
74
|
+
|
|
75
|
+
ctx.send({ ok: true });
|
|
76
|
+
},
|
|
77
|
+
};
|