@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.
Files changed (168) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1 -0
  3. package/admin/src/components/BoundRoute/getMethodColor.js +41 -0
  4. package/admin/src/components/BoundRoute/index.js +72 -0
  5. package/admin/src/components/FormModal/Input/index.js +121 -0
  6. package/admin/src/components/FormModal/index.js +121 -0
  7. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +30 -0
  8. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +114 -0
  9. package/admin/src/components/Permissions/PermissionRow/index.js +53 -0
  10. package/admin/src/components/Permissions/index.js +56 -0
  11. package/admin/src/components/Permissions/init.js +9 -0
  12. package/admin/src/components/Permissions/reducer.js +27 -0
  13. package/admin/src/components/Policies/index.js +60 -0
  14. package/admin/src/components/UsersPermissions/index.js +94 -0
  15. package/admin/src/components/UsersPermissions/init.js +10 -0
  16. package/admin/src/components/UsersPermissions/reducer.js +60 -0
  17. package/admin/src/contexts/UsersPermissionsContext/index.js +17 -0
  18. package/admin/src/hooks/index.js +5 -0
  19. package/admin/src/hooks/useFetchRole/index.js +64 -0
  20. package/admin/src/hooks/useFetchRole/reducer.js +31 -0
  21. package/admin/src/hooks/useForm/index.js +70 -0
  22. package/admin/src/hooks/useForm/reducer.js +40 -0
  23. package/admin/src/hooks/usePlugins/index.js +65 -0
  24. package/admin/src/hooks/usePlugins/init.js +5 -0
  25. package/admin/src/hooks/usePlugins/reducer.js +34 -0
  26. package/admin/src/hooks/useRolesList/index.js +63 -0
  27. package/admin/src/hooks/useRolesList/init.js +5 -0
  28. package/admin/src/hooks/useRolesList/reducer.js +31 -0
  29. package/admin/src/index.js +123 -0
  30. package/admin/src/pages/AdvancedSettings/index.js +238 -0
  31. package/admin/src/pages/AdvancedSettings/utils/api.js +13 -0
  32. package/admin/src/pages/AdvancedSettings/utils/layout.js +96 -0
  33. package/admin/src/pages/AdvancedSettings/utils/schema.js +19 -0
  34. package/admin/src/pages/EmailTemplates/components/EmailForm.js +173 -0
  35. package/admin/src/pages/EmailTemplates/components/EmailTable.js +121 -0
  36. package/admin/src/pages/EmailTemplates/index.js +162 -0
  37. package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
  38. package/admin/src/pages/EmailTemplates/utils/schema.js +22 -0
  39. package/admin/src/pages/Providers/index.js +274 -0
  40. package/admin/src/pages/Providers/reducer.js +54 -0
  41. package/admin/src/pages/Providers/utils/api.js +21 -0
  42. package/admin/src/pages/Providers/utils/createProvidersArray.js +21 -0
  43. package/admin/src/pages/Providers/utils/forms.js +244 -0
  44. package/admin/src/pages/Roles/CreatePage/index.js +177 -0
  45. package/admin/src/pages/Roles/CreatePage/utils/schema.js +9 -0
  46. package/admin/src/pages/Roles/EditPage/index.js +190 -0
  47. package/admin/src/pages/Roles/EditPage/utils/schema.js +9 -0
  48. package/admin/src/pages/Roles/ListPage/components/TableBody.js +96 -0
  49. package/admin/src/pages/Roles/ListPage/index.js +216 -0
  50. package/admin/src/pages/Roles/ListPage/utils/api.js +28 -0
  51. package/admin/src/pages/Roles/ProtectedCreatePage/index.js +12 -0
  52. package/admin/src/pages/Roles/ProtectedEditPage/index.js +12 -0
  53. package/admin/src/pages/Roles/ProtectedListPage/index.js +15 -0
  54. package/admin/src/pages/Roles/index.js +27 -0
  55. package/admin/src/permissions.js +31 -0
  56. package/admin/src/pluginId.js +5 -0
  57. package/admin/src/translations/ar.json +40 -0
  58. package/admin/src/translations/cs.json +46 -0
  59. package/admin/src/translations/de.json +58 -0
  60. package/admin/src/translations/dk.json +83 -0
  61. package/admin/src/translations/en.json +83 -0
  62. package/admin/src/translations/es.json +83 -0
  63. package/admin/src/translations/fr.json +46 -0
  64. package/admin/src/translations/id.json +58 -0
  65. package/admin/src/translations/it.json +58 -0
  66. package/admin/src/translations/ja.json +44 -0
  67. package/admin/src/translations/ko.json +83 -0
  68. package/admin/src/translations/ms.json +45 -0
  69. package/admin/src/translations/nl.json +44 -0
  70. package/admin/src/translations/pl.json +83 -0
  71. package/admin/src/translations/pt-BR.json +40 -0
  72. package/admin/src/translations/pt.json +44 -0
  73. package/admin/src/translations/ru.json +58 -0
  74. package/admin/src/translations/sk.json +46 -0
  75. package/admin/src/translations/sv.json +58 -0
  76. package/admin/src/translations/th.json +56 -0
  77. package/admin/src/translations/tr.json +44 -0
  78. package/admin/src/translations/uk.json +45 -0
  79. package/admin/src/translations/vi.json +46 -0
  80. package/admin/src/translations/zh-Hans.json +62 -0
  81. package/admin/src/translations/zh.json +44 -0
  82. package/admin/src/utils/axiosInstance.js +36 -0
  83. package/admin/src/utils/cleanPermissions.js +25 -0
  84. package/admin/src/utils/formatPluginName.js +26 -0
  85. package/admin/src/utils/formatPolicies.js +8 -0
  86. package/admin/src/utils/getRequestURL.js +5 -0
  87. package/admin/src/utils/getTrad.js +5 -0
  88. package/admin/src/utils/index.js +5 -0
  89. package/documentation/content-api.yaml +848 -0
  90. package/jest.config.front.js +10 -0
  91. package/package.json +60 -0
  92. package/server/bootstrap/grant-config.js +123 -0
  93. package/server/bootstrap/index.js +133 -0
  94. package/server/bootstrap/users-permissions-actions.js +80 -0
  95. package/server/config.js +23 -0
  96. package/server/content-types/index.js +11 -0
  97. package/server/content-types/permission/index.js +34 -0
  98. package/server/content-types/role/index.js +51 -0
  99. package/server/content-types/user/index.js +72 -0
  100. package/server/content-types/user/schema-config.js +15 -0
  101. package/server/controllers/auth.js +398 -0
  102. package/server/controllers/content-manager-user.js +175 -0
  103. package/server/controllers/index.js +17 -0
  104. package/server/controllers/permissions.js +26 -0
  105. package/server/controllers/role.js +77 -0
  106. package/server/controllers/settings.js +85 -0
  107. package/server/controllers/user.js +198 -0
  108. package/server/controllers/validation/auth.js +57 -0
  109. package/server/controllers/validation/email-template.js +50 -0
  110. package/server/controllers/validation/user.js +26 -0
  111. package/server/graphql/index.js +44 -0
  112. package/server/graphql/mutations/auth/change-password.js +38 -0
  113. package/server/graphql/mutations/auth/email-confirmation.js +39 -0
  114. package/server/graphql/mutations/auth/forgot-password.js +35 -0
  115. package/server/graphql/mutations/auth/login.js +35 -0
  116. package/server/graphql/mutations/auth/register.js +36 -0
  117. package/server/graphql/mutations/auth/reset-password.js +38 -0
  118. package/server/graphql/mutations/crud/role/create-role.js +34 -0
  119. package/server/graphql/mutations/crud/role/delete-role.js +25 -0
  120. package/server/graphql/mutations/crud/role/update-role.js +35 -0
  121. package/server/graphql/mutations/crud/user/create-user.js +45 -0
  122. package/server/graphql/mutations/crud/user/delete-user.js +39 -0
  123. package/server/graphql/mutations/crud/user/update-user.js +46 -0
  124. package/server/graphql/mutations/index.js +43 -0
  125. package/server/graphql/queries/index.js +13 -0
  126. package/server/graphql/queries/me.js +17 -0
  127. package/server/graphql/resolvers-configs.js +42 -0
  128. package/server/graphql/types/create-role-payload.js +11 -0
  129. package/server/graphql/types/delete-role-payload.js +11 -0
  130. package/server/graphql/types/index.js +21 -0
  131. package/server/graphql/types/login-input.js +13 -0
  132. package/server/graphql/types/login-payload.js +12 -0
  133. package/server/graphql/types/me-role.js +14 -0
  134. package/server/graphql/types/me.js +16 -0
  135. package/server/graphql/types/password-payload.js +11 -0
  136. package/server/graphql/types/register-input.js +13 -0
  137. package/server/graphql/types/update-role-payload.js +11 -0
  138. package/server/graphql/utils.js +27 -0
  139. package/server/index.js +21 -0
  140. package/server/middlewares/index.js +7 -0
  141. package/server/middlewares/rateLimit.js +27 -0
  142. package/server/register.js +23 -0
  143. package/server/routes/admin/index.js +10 -0
  144. package/server/routes/admin/permissions.js +20 -0
  145. package/server/routes/admin/role.js +79 -0
  146. package/server/routes/admin/settings.js +95 -0
  147. package/server/routes/content-api/auth.js +82 -0
  148. package/server/routes/content-api/index.js +11 -0
  149. package/server/routes/content-api/permissions.js +9 -0
  150. package/server/routes/content-api/role.js +29 -0
  151. package/server/routes/content-api/user.js +60 -0
  152. package/server/routes/index.js +6 -0
  153. package/server/services/index.js +17 -0
  154. package/server/services/jwt.js +55 -0
  155. package/server/services/providers-registry.js +292 -0
  156. package/server/services/providers.js +115 -0
  157. package/server/services/role.js +177 -0
  158. package/server/services/user.js +140 -0
  159. package/server/services/users-permissions.js +236 -0
  160. package/server/strategies/users-permissions.js +102 -0
  161. package/server/utils/index.d.ts +16 -0
  162. package/server/utils/index.js +12 -0
  163. package/server/utils/sanitize/index.js +9 -0
  164. package/server/utils/sanitize/sanitizers.js +19 -0
  165. package/server/utils/sanitize/visitors/index.js +5 -0
  166. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +11 -0
  167. package/strapi-admin.js +3 -0
  168. package/strapi-server.js +3 -0
@@ -0,0 +1,292 @@
1
+ 'use strict';
2
+
3
+ const { strict: assert } = require('assert');
4
+ const jwt = require('jsonwebtoken');
5
+
6
+ const getInitialProviders = ({ purest }) => ({
7
+ async discord({ accessToken }) {
8
+ const discord = purest({ provider: 'discord' });
9
+ return discord
10
+ .get('users/@me')
11
+ .auth(accessToken)
12
+ .request()
13
+ .then(({ body }) => {
14
+ // Combine username and discriminator because discord username is not unique
15
+ const username = `${body.username}#${body.discriminator}`;
16
+ return {
17
+ username,
18
+ email: body.email,
19
+ };
20
+ });
21
+ },
22
+ async cognito({ query }) {
23
+ // get the id_token
24
+ const idToken = query.id_token;
25
+ // decode the jwt token
26
+ const tokenPayload = jwt.decode(idToken);
27
+ if (!tokenPayload) {
28
+ throw new Error('unable to decode jwt token');
29
+ } else {
30
+ return {
31
+ username: tokenPayload['cognito:username'],
32
+ email: tokenPayload.email,
33
+ };
34
+ }
35
+ },
36
+ async facebook({ accessToken }) {
37
+ const facebook = purest({ provider: 'facebook' });
38
+
39
+ return facebook
40
+ .get('me')
41
+ .auth(accessToken)
42
+ .qs({ fields: 'name,email' })
43
+ .request()
44
+ .then(({ body }) => ({
45
+ username: body.name,
46
+ email: body.email,
47
+ }));
48
+ },
49
+ async google({ accessToken }) {
50
+ const google = purest({ provider: 'google' });
51
+
52
+ return google
53
+ .query('oauth')
54
+ .get('tokeninfo')
55
+ .qs({ accessToken })
56
+ .request()
57
+ .then(({ body }) => ({
58
+ username: body.email.split('@')[0],
59
+ email: body.email,
60
+ }));
61
+ },
62
+ async github({ accessToken }) {
63
+ const github = purest({
64
+ provider: 'github',
65
+ defaults: {
66
+ headers: {
67
+ 'user-agent': 'strapi',
68
+ },
69
+ },
70
+ });
71
+
72
+ const { body: userBody } = await github.get('user').auth(accessToken).request();
73
+
74
+ // This is the public email on the github profile
75
+ if (userBody.email) {
76
+ return {
77
+ username: userBody.login,
78
+ email: userBody.email,
79
+ };
80
+ }
81
+ // Get the email with Github's user/emails API
82
+ const { body: emailBody } = await github.get('user/emails').auth(accessToken).request();
83
+
84
+ return {
85
+ username: userBody.login,
86
+ email: Array.isArray(emailBody)
87
+ ? emailBody.find((email) => email.primary === true).email
88
+ : null,
89
+ };
90
+ },
91
+ async microsoft({ accessToken }) {
92
+ const microsoft = purest({ provider: 'microsoft' });
93
+
94
+ return microsoft
95
+ .get('me')
96
+ .auth(accessToken)
97
+ .request()
98
+ .then(({ body }) => ({
99
+ username: body.userPrincipalName,
100
+ email: body.userPrincipalName,
101
+ }));
102
+ },
103
+ async twitter({ accessToken, query, providers }) {
104
+ const twitter = purest({
105
+ provider: 'twitter',
106
+ defaults: {
107
+ oauth: {
108
+ consumer_key: providers.twitter.key,
109
+ consumer_secret: providers.twitter.secret,
110
+ },
111
+ },
112
+ });
113
+
114
+ return twitter
115
+ .get('account/verify_credentials')
116
+ .auth(accessToken, query.access_secret)
117
+ .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
118
+ .request()
119
+ .then(({ body }) => ({
120
+ username: body.screen_name,
121
+ email: body.email,
122
+ }));
123
+ },
124
+ async instagram({ accessToken }) {
125
+ const instagram = purest({ provider: 'instagram' });
126
+
127
+ return instagram
128
+ .get('me')
129
+ .auth(accessToken)
130
+ .qs({ fields: 'id,username' })
131
+ .request()
132
+ .then(({ body }) => ({
133
+ username: body.username,
134
+ email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
135
+ }));
136
+ },
137
+ async vk({ accessToken, query }) {
138
+ const vk = purest({ provider: 'vk' });
139
+
140
+ return vk
141
+ .get('users.get')
142
+ .auth(accessToken)
143
+ .qs({ id: query.raw.user_id, v: '5.122' })
144
+ .request()
145
+ .then(({ body }) => ({
146
+ username: `${body.response[0].last_name} ${body.response[0].first_name}`,
147
+ email: query.raw.email,
148
+ }));
149
+ },
150
+ async twitch({ accessToken, providers }) {
151
+ const twitch = purest({
152
+ provider: 'twitch',
153
+ config: {
154
+ twitch: {
155
+ default: {
156
+ origin: 'https://api.twitch.tv',
157
+ path: 'helix/{path}',
158
+ headers: {
159
+ Authorization: 'Bearer {auth}',
160
+ 'Client-Id': '{auth}',
161
+ },
162
+ },
163
+ },
164
+ },
165
+ });
166
+
167
+ return twitch
168
+ .get('users')
169
+ .auth(accessToken, providers.twitch.key)
170
+ .request()
171
+ .then(({ body }) => ({
172
+ username: body.data[0].login,
173
+ email: body.data[0].email,
174
+ }));
175
+ },
176
+ async linkedin({ accessToken }) {
177
+ const linkedIn = purest({ provider: 'linkedin' });
178
+ const {
179
+ body: { localizedFirstName },
180
+ } = await linkedIn.get('me').auth(accessToken).request();
181
+ const {
182
+ body: { elements },
183
+ } = await linkedIn
184
+ .get('emailAddress?q=members&projection=(elements*(handle~))')
185
+ .auth(accessToken)
186
+ .request();
187
+
188
+ const email = elements[0]['handle~'];
189
+
190
+ return {
191
+ username: localizedFirstName,
192
+ email: email.emailAddress,
193
+ };
194
+ },
195
+ async reddit({ accessToken }) {
196
+ const reddit = purest({
197
+ provider: 'reddit',
198
+ config: {
199
+ reddit: {
200
+ default: {
201
+ origin: 'https://oauth.reddit.com',
202
+ path: 'api/{version}/{path}',
203
+ version: 'v1',
204
+ headers: {
205
+ Authorization: 'Bearer {auth}',
206
+ 'user-agent': 'strapi',
207
+ },
208
+ },
209
+ },
210
+ },
211
+ });
212
+
213
+ return reddit
214
+ .get('me')
215
+ .auth(accessToken)
216
+ .request()
217
+ .then(({ body }) => ({
218
+ username: body.name,
219
+ email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
220
+ }));
221
+ },
222
+ async auth0({ accessToken, providers }) {
223
+ const auth0 = purest({ provider: 'auth0' });
224
+
225
+ return auth0
226
+ .get('userinfo')
227
+ .subdomain(providers.auth0.subdomain)
228
+ .auth(accessToken)
229
+ .request()
230
+ .then(({ body }) => {
231
+ const username = body.username || body.nickname || body.name || body.email.split('@')[0];
232
+ const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
233
+
234
+ return {
235
+ username,
236
+ email,
237
+ };
238
+ });
239
+ },
240
+ async cas({ accessToken, providers }) {
241
+ const cas = purest({ provider: 'cas' });
242
+
243
+ return cas
244
+ .get('oidc/profile')
245
+ .subdomain(providers.cas.subdomain)
246
+ .auth(accessToken)
247
+ .request()
248
+ .then(({ body }) => {
249
+ // CAS attribute may be in body.attributes or "FLAT", depending on CAS config
250
+ const username = body.attributes
251
+ ? body.attributes.strapiusername || body.id || body.sub
252
+ : body.strapiusername || body.id || body.sub;
253
+ const email = body.attributes
254
+ ? body.attributes.strapiemail || body.attributes.email
255
+ : body.strapiemail || body.email;
256
+ if (!username || !email) {
257
+ strapi.log.warn(
258
+ `CAS Response Body did not contain required attributes: ${JSON.stringify(body)}`
259
+ );
260
+ }
261
+ return {
262
+ username,
263
+ email,
264
+ };
265
+ });
266
+ },
267
+ });
268
+
269
+ module.exports = () => {
270
+ const purest = require('purest');
271
+
272
+ const providersCallbacks = getInitialProviders({ purest });
273
+
274
+ return {
275
+ register(providerName, provider) {
276
+ assert(typeof providerName === 'string', 'Provider name must be a string');
277
+ assert(typeof provider === 'function', 'Provider callback must be a function');
278
+
279
+ providersCallbacks[providerName] = provider({ purest });
280
+ },
281
+
282
+ async run({ provider, accessToken, query, providers }) {
283
+ if (!providersCallbacks[provider]) {
284
+ throw new Error('Unknown provider.');
285
+ }
286
+
287
+ const providerCb = providersCallbacks[provider];
288
+
289
+ return providerCb({ accessToken, query, providers });
290
+ },
291
+ };
292
+ };
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Module dependencies.
5
+ */
6
+
7
+ // Public node modules.
8
+ const _ = require('lodash');
9
+ const urlJoin = require('url-join');
10
+
11
+ const { getAbsoluteServerUrl } = require('@strapi/utils');
12
+ const { getService } = require('../utils');
13
+
14
+ module.exports = ({ strapi }) => {
15
+ /**
16
+ * Helper to get profiles
17
+ *
18
+ * @param {String} provider
19
+ */
20
+
21
+ const getProfile = async (provider, query) => {
22
+ const accessToken = query.access_token || query.code || query.oauth_token;
23
+
24
+ const providers = await strapi
25
+ .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
26
+ .get();
27
+
28
+ return getService('providers-registry').run({
29
+ provider,
30
+ query,
31
+ accessToken,
32
+ providers,
33
+ });
34
+ };
35
+
36
+ /**
37
+ * Connect thanks to a third-party provider.
38
+ *
39
+ *
40
+ * @param {String} provider
41
+ * @param {String} accessToken
42
+ *
43
+ * @return {*}
44
+ */
45
+
46
+ const connect = async (provider, query) => {
47
+ const accessToken = query.access_token || query.code || query.oauth_token;
48
+
49
+ if (!accessToken) {
50
+ throw new Error('No access_token.');
51
+ }
52
+
53
+ // Get the profile.
54
+ const profile = await getProfile(provider, query);
55
+
56
+ const email = _.toLower(profile.email);
57
+
58
+ // We need at least the mail.
59
+ if (!email) {
60
+ throw new Error('Email was not available.');
61
+ }
62
+
63
+ const users = await strapi.query('plugin::users-permissions.user').findMany({
64
+ where: { email },
65
+ });
66
+
67
+ const advancedSettings = await strapi
68
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
69
+ .get();
70
+
71
+ const user = _.find(users, { provider });
72
+
73
+ if (_.isEmpty(user) && !advancedSettings.allow_register) {
74
+ throw new Error('Register action is actually not available.');
75
+ }
76
+
77
+ if (!_.isEmpty(user)) {
78
+ return user;
79
+ }
80
+
81
+ if (users.length > 1 && advancedSettings.unique_email) {
82
+ throw new Error('Email is already taken.');
83
+ }
84
+
85
+ // Retrieve default role.
86
+ const defaultRole = await strapi
87
+ .query('plugin::users-permissions.role')
88
+ .findOne({ where: { type: advancedSettings.default_role } });
89
+
90
+ // Create the new user.
91
+ const newUser = {
92
+ ...profile,
93
+ email, // overwrite with lowercased email
94
+ provider,
95
+ role: defaultRole.id,
96
+ confirmed: true,
97
+ };
98
+
99
+ const createdUser = await strapi
100
+ .query('plugin::users-permissions.user')
101
+ .create({ data: newUser });
102
+
103
+ return createdUser;
104
+ };
105
+
106
+ const buildRedirectUri = (provider = '') => {
107
+ const apiPrefix = strapi.config.get('api.rest.prefix');
108
+ return urlJoin(getAbsoluteServerUrl(strapi.config), apiPrefix, 'connect', provider, 'callback');
109
+ };
110
+
111
+ return {
112
+ connect,
113
+ buildRedirectUri,
114
+ };
115
+ };
@@ -0,0 +1,177 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { NotFoundError } = require('@strapi/utils').errors;
5
+ const { getService } = require('../utils');
6
+
7
+ module.exports = ({ strapi }) => ({
8
+ async createRole(params) {
9
+ if (!params.type) {
10
+ params.type = _.snakeCase(_.deburr(_.toLower(params.name)));
11
+ }
12
+
13
+ const role = await strapi
14
+ .query('plugin::users-permissions.role')
15
+ .create({ data: _.omit(params, ['users', 'permissions']) });
16
+
17
+ const createPromises = _.flatMap(params.permissions, (type, typeName) => {
18
+ return _.flatMap(type.controllers, (controller, controllerName) => {
19
+ return _.reduce(
20
+ controller,
21
+ (acc, action, actionName) => {
22
+ const { enabled /* policy */ } = action;
23
+
24
+ if (enabled) {
25
+ const actionID = `${typeName}.${controllerName}.${actionName}`;
26
+
27
+ acc.push(
28
+ strapi
29
+ .query('plugin::users-permissions.permission')
30
+ .create({ data: { action: actionID, role: role.id } })
31
+ );
32
+ }
33
+
34
+ return acc;
35
+ },
36
+ []
37
+ );
38
+ });
39
+ });
40
+
41
+ await Promise.all(createPromises);
42
+ },
43
+
44
+ async findOne(roleID) {
45
+ const role = await strapi
46
+ .query('plugin::users-permissions.role')
47
+ .findOne({ where: { id: roleID }, populate: ['permissions'] });
48
+
49
+ if (!role) {
50
+ throw new NotFoundError('Role not found');
51
+ }
52
+
53
+ const allActions = getService('users-permissions').getActions();
54
+
55
+ // Group by `type`.
56
+ role.permissions.forEach((permission) => {
57
+ const [type, controller, action] = permission.action.split('.');
58
+
59
+ _.set(allActions, `${type}.controllers.${controller}.${action}`, {
60
+ enabled: true,
61
+ policy: '',
62
+ });
63
+ });
64
+
65
+ return {
66
+ ...role,
67
+ permissions: allActions,
68
+ };
69
+ },
70
+
71
+ async find() {
72
+ const roles = await strapi.query('plugin::users-permissions.role').findMany({ sort: ['name'] });
73
+
74
+ for (const role of roles) {
75
+ role.nb_users = await strapi
76
+ .query('plugin::users-permissions.user')
77
+ .count({ where: { role: { id: role.id } } });
78
+ }
79
+
80
+ return roles;
81
+ },
82
+
83
+ async updateRole(roleID, data) {
84
+ const role = await strapi
85
+ .query('plugin::users-permissions.role')
86
+ .findOne({ where: { id: roleID }, populate: ['permissions'] });
87
+
88
+ if (!role) {
89
+ throw new NotFoundError('Role not found');
90
+ }
91
+
92
+ await strapi.query('plugin::users-permissions.role').update({
93
+ where: { id: roleID },
94
+ data: _.pick(data, ['name', 'description']),
95
+ });
96
+
97
+ const { permissions } = data;
98
+
99
+ const newActions = _.flatMap(permissions, (type, typeName) => {
100
+ return _.flatMap(type.controllers, (controller, controllerName) => {
101
+ return _.reduce(
102
+ controller,
103
+ (acc, action, actionName) => {
104
+ const { enabled /* policy */ } = action;
105
+
106
+ if (enabled) {
107
+ acc.push(`${typeName}.${controllerName}.${actionName}`);
108
+ }
109
+
110
+ return acc;
111
+ },
112
+ []
113
+ );
114
+ });
115
+ });
116
+
117
+ const oldActions = role.permissions.map(({ action }) => action);
118
+
119
+ const toDelete = role.permissions.reduce((acc, permission) => {
120
+ if (!newActions.includes(permission.action)) {
121
+ acc.push(permission);
122
+ }
123
+ return acc;
124
+ }, []);
125
+
126
+ const toCreate = newActions
127
+ .filter((action) => !oldActions.includes(action))
128
+ .map((action) => ({ action, role: role.id }));
129
+
130
+ await Promise.all(
131
+ toDelete.map((permission) =>
132
+ strapi
133
+ .query('plugin::users-permissions.permission')
134
+ .delete({ where: { id: permission.id } })
135
+ )
136
+ );
137
+
138
+ await Promise.all(
139
+ toCreate.map((permissionInfo) =>
140
+ strapi.query('plugin::users-permissions.permission').create({ data: permissionInfo })
141
+ )
142
+ );
143
+ },
144
+
145
+ async deleteRole(roleID, publicRoleID) {
146
+ const role = await strapi
147
+ .query('plugin::users-permissions.role')
148
+ .findOne({ where: { id: roleID }, populate: ['users', 'permissions'] });
149
+
150
+ if (!role) {
151
+ throw new NotFoundError('Role not found');
152
+ }
153
+
154
+ // Move users to guest role.
155
+ await Promise.all(
156
+ role.users.map((user) => {
157
+ return strapi.query('plugin::users-permissions.user').update({
158
+ where: { id: user.id },
159
+ data: { role: publicRoleID },
160
+ });
161
+ })
162
+ );
163
+
164
+ // Remove permissions related to this role.
165
+ // TODO: use delete many
166
+ await Promise.all(
167
+ role.permissions.map((permission) => {
168
+ return strapi.query('plugin::users-permissions.permission').delete({
169
+ where: { id: permission.id },
170
+ });
171
+ })
172
+ );
173
+
174
+ // Delete the role.
175
+ await strapi.query('plugin::users-permissions.role').delete({ where: { id: roleID } });
176
+ },
177
+ });