@strapi/plugin-users-permissions 0.0.0-next.e21fe90bf2ab9906267ea6e6ca620bdcc729906c → 0.0.0-next.e2e3ca14971ee768e1a227a209362264fd0132d4

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 (144) hide show
  1. package/admin/src/components/Permissions/PermissionRow/SubCategory.jsx +8 -1
  2. package/admin/src/components/Policies/index.jsx +1 -0
  3. package/admin/src/components/UsersPermissions/index.jsx +1 -0
  4. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +2 -1
  5. package/admin/src/pages/Providers/index.jsx +74 -76
  6. package/admin/src/pages/Roles/pages/CreatePage.jsx +20 -7
  7. package/admin/src/pages/Roles/pages/EditPage.jsx +12 -7
  8. package/admin/src/pages/Roles/pages/ListPage/index.jsx +91 -93
  9. package/admin/src/translations/en.json +1 -0
  10. package/admin/src/utils/formatPluginName.js +1 -1
  11. package/dist/admin/components/FormModal/Input/index.js.map +1 -1
  12. package/dist/admin/components/FormModal/Input/index.mjs.map +1 -1
  13. package/dist/admin/components/Permissions/PermissionRow/SubCategory.js +3 -1
  14. package/dist/admin/components/Permissions/PermissionRow/SubCategory.js.map +1 -1
  15. package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs +3 -1
  16. package/dist/admin/components/Permissions/PermissionRow/SubCategory.mjs.map +1 -1
  17. package/dist/admin/components/Permissions/PermissionRow/index.js.map +1 -1
  18. package/dist/admin/components/Permissions/PermissionRow/index.mjs.map +1 -1
  19. package/dist/admin/components/Policies/index.js +1 -0
  20. package/dist/admin/components/Policies/index.js.map +1 -1
  21. package/dist/admin/components/Policies/index.mjs +1 -0
  22. package/dist/admin/components/Policies/index.mjs.map +1 -1
  23. package/dist/admin/components/UsersPermissions/index.js +1 -0
  24. package/dist/admin/components/UsersPermissions/index.js.map +1 -1
  25. package/dist/admin/components/UsersPermissions/index.mjs +1 -0
  26. package/dist/admin/components/UsersPermissions/index.mjs.map +1 -1
  27. package/dist/admin/pages/EmailTemplates/components/EmailForm.js +2 -1
  28. package/dist/admin/pages/EmailTemplates/components/EmailForm.js.map +1 -1
  29. package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs +2 -1
  30. package/dist/admin/pages/EmailTemplates/components/EmailForm.mjs.map +1 -1
  31. package/dist/admin/pages/Providers/index.js +84 -88
  32. package/dist/admin/pages/Providers/index.js.map +1 -1
  33. package/dist/admin/pages/Providers/index.mjs +84 -88
  34. package/dist/admin/pages/Providers/index.mjs.map +1 -1
  35. package/dist/admin/pages/Roles/pages/CreatePage.js +14 -1
  36. package/dist/admin/pages/Roles/pages/CreatePage.js.map +1 -1
  37. package/dist/admin/pages/Roles/pages/CreatePage.mjs +16 -3
  38. package/dist/admin/pages/Roles/pages/CreatePage.mjs.map +1 -1
  39. package/dist/admin/pages/Roles/pages/EditPage.js +13 -3
  40. package/dist/admin/pages/Roles/pages/EditPage.js.map +1 -1
  41. package/dist/admin/pages/Roles/pages/EditPage.mjs +14 -4
  42. package/dist/admin/pages/Roles/pages/EditPage.mjs.map +1 -1
  43. package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.js.map +1 -1
  44. package/dist/admin/pages/Roles/pages/ListPage/components/TableBody.mjs.map +1 -1
  45. package/dist/admin/pages/Roles/pages/ListPage/index.js +95 -99
  46. package/dist/admin/pages/Roles/pages/ListPage/index.js.map +1 -1
  47. package/dist/admin/pages/Roles/pages/ListPage/index.mjs +95 -99
  48. package/dist/admin/pages/Roles/pages/ListPage/index.mjs.map +1 -1
  49. package/dist/admin/translations/en.json.js +1 -0
  50. package/dist/admin/translations/en.json.js.map +1 -1
  51. package/dist/admin/translations/en.json.mjs +1 -0
  52. package/dist/admin/translations/en.json.mjs.map +1 -1
  53. package/dist/admin/utils/formatPluginName.js +1 -1
  54. package/dist/admin/utils/formatPluginName.js.map +1 -1
  55. package/dist/admin/utils/formatPluginName.mjs +1 -1
  56. package/dist/admin/utils/formatPluginName.mjs.map +1 -1
  57. package/dist/admin/utils/getTrad.js.map +1 -1
  58. package/dist/admin/utils/getTrad.mjs.map +1 -1
  59. package/dist/admin/utils/prefixPluginTranslations.js.map +1 -1
  60. package/dist/admin/utils/prefixPluginTranslations.mjs.map +1 -1
  61. package/dist/server/bootstrap/index.js +28 -7
  62. package/dist/server/bootstrap/index.js.map +1 -1
  63. package/dist/server/bootstrap/index.mjs +28 -7
  64. package/dist/server/bootstrap/index.mjs.map +1 -1
  65. package/dist/server/config.js +16 -0
  66. package/dist/server/config.js.map +1 -1
  67. package/dist/server/config.mjs +16 -0
  68. package/dist/server/config.mjs.map +1 -1
  69. package/dist/server/controllers/auth.js +204 -3
  70. package/dist/server/controllers/auth.js.map +1 -1
  71. package/dist/server/controllers/auth.mjs +204 -3
  72. package/dist/server/controllers/auth.mjs.map +1 -1
  73. package/dist/server/controllers/content-manager-user.js +3 -3
  74. package/dist/server/controllers/content-manager-user.js.map +1 -1
  75. package/dist/server/controllers/content-manager-user.mjs +3 -3
  76. package/dist/server/controllers/content-manager-user.mjs.map +1 -1
  77. package/dist/server/controllers/validation/user.js +6 -1
  78. package/dist/server/controllers/validation/user.js.map +1 -1
  79. package/dist/server/controllers/validation/user.mjs +6 -1
  80. package/dist/server/controllers/validation/user.mjs.map +1 -1
  81. package/dist/server/graphql/resolvers-configs.js.map +1 -1
  82. package/dist/server/graphql/resolvers-configs.mjs.map +1 -1
  83. package/dist/server/middlewares/rateLimit.js.map +1 -1
  84. package/dist/server/middlewares/rateLimit.mjs.map +1 -1
  85. package/dist/server/routes/content-api/auth.js +155 -91
  86. package/dist/server/routes/content-api/auth.js.map +1 -1
  87. package/dist/server/routes/content-api/auth.mjs +155 -91
  88. package/dist/server/routes/content-api/auth.mjs.map +1 -1
  89. package/dist/server/routes/content-api/index.js +11 -9
  90. package/dist/server/routes/content-api/index.js.map +1 -1
  91. package/dist/server/routes/content-api/index.mjs +11 -9
  92. package/dist/server/routes/content-api/index.mjs.map +1 -1
  93. package/dist/server/routes/content-api/permissions.js +14 -7
  94. package/dist/server/routes/content-api/permissions.js.map +1 -1
  95. package/dist/server/routes/content-api/permissions.mjs +14 -7
  96. package/dist/server/routes/content-api/permissions.mjs.map +1 -1
  97. package/dist/server/routes/content-api/role.js +61 -27
  98. package/dist/server/routes/content-api/role.js.map +1 -1
  99. package/dist/server/routes/content-api/role.mjs +61 -27
  100. package/dist/server/routes/content-api/role.mjs.map +1 -1
  101. package/dist/server/routes/content-api/user.js +119 -57
  102. package/dist/server/routes/content-api/user.js.map +1 -1
  103. package/dist/server/routes/content-api/user.mjs +119 -57
  104. package/dist/server/routes/content-api/user.mjs.map +1 -1
  105. package/dist/server/routes/content-api/validation.js +217 -0
  106. package/dist/server/routes/content-api/validation.js.map +1 -0
  107. package/dist/server/routes/content-api/validation.mjs +215 -0
  108. package/dist/server/routes/content-api/validation.mjs.map +1 -0
  109. package/dist/server/services/constants.js +19 -0
  110. package/dist/server/services/constants.js.map +1 -0
  111. package/dist/server/services/constants.mjs +17 -0
  112. package/dist/server/services/constants.mjs.map +1 -0
  113. package/dist/server/services/jwt.js +45 -2
  114. package/dist/server/services/jwt.js.map +1 -1
  115. package/dist/server/services/jwt.mjs +45 -2
  116. package/dist/server/services/jwt.mjs.map +1 -1
  117. package/dist/server/services/providers-registry.js.map +1 -1
  118. package/dist/server/services/providers-registry.mjs.map +1 -1
  119. package/dist/server/services/role.js.map +1 -1
  120. package/dist/server/services/role.mjs.map +1 -1
  121. package/dist/server/services/user.js +29 -20
  122. package/dist/server/services/user.js.map +1 -1
  123. package/dist/server/services/user.mjs +29 -20
  124. package/dist/server/services/user.mjs.map +1 -1
  125. package/dist/server/services/users-permissions.js +4 -3
  126. package/dist/server/services/users-permissions.js.map +1 -1
  127. package/dist/server/services/users-permissions.mjs +4 -3
  128. package/dist/server/services/users-permissions.mjs.map +1 -1
  129. package/package.json +9 -8
  130. package/server/bootstrap/index.js +31 -0
  131. package/server/config.js +22 -0
  132. package/server/controllers/auth.js +248 -8
  133. package/server/controllers/content-manager-user.js +3 -4
  134. package/server/controllers/validation/user.js +12 -1
  135. package/server/routes/content-api/auth.js +119 -71
  136. package/server/routes/content-api/index.js +11 -4
  137. package/server/routes/content-api/permissions.js +14 -7
  138. package/server/routes/content-api/role.js +57 -27
  139. package/server/routes/content-api/user.js +108 -51
  140. package/server/routes/content-api/validation.js +250 -0
  141. package/server/services/constants.js +9 -0
  142. package/server/services/jwt.js +50 -2
  143. package/server/services/user.js +11 -0
  144. package/server/services/users-permissions.js +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"users-permissions.mjs","sources":["../../../server/services/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst _ = require('lodash');\nconst { filter, map, pipe, prop } = require('lodash/fp');\nconst urlJoin = require('url-join');\nconst {\n template: { createStrictInterpolationRegExp },\n errors,\n objects,\n} = require('@strapi/utils');\n\nconst { getService } = require('../utils');\n\nconst DEFAULT_PERMISSIONS = [\n { action: 'plugin::users-permissions.auth.callback', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.connect', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.forgotPassword', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.resetPassword', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.register', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.emailConfirmation', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.sendEmailConfirmation', roleType: 'public' },\n { action: 'plugin::users-permissions.user.me', roleType: 'authenticated' },\n { action: 'plugin::users-permissions.auth.changePassword', roleType: 'authenticated' },\n];\n\nconst transformRoutePrefixFor = (pluginName) => (route) => {\n const prefix = route.config && route.config.prefix;\n const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;\n\n return {\n ...route,\n path,\n };\n};\n\nmodule.exports = ({ strapi }) => ({\n getActions({ defaultEnable = false } = {}) {\n const actionMap = {};\n\n const isContentApi = (action) => {\n if (!_.has(action, Symbol.for('__type__'))) {\n return false;\n }\n\n return action[Symbol.for('__type__')].includes('content-api');\n };\n\n _.forEach(strapi.apis, (api, apiName) => {\n const controllers = _.reduce(\n api.controllers,\n (acc, controller, controllerName) => {\n const contentApiActions = _.pickBy(controller, isContentApi);\n\n if (_.isEmpty(contentApiActions)) {\n return acc;\n }\n\n acc[controllerName] = _.mapValues(contentApiActions, () => {\n return {\n enabled: defaultEnable,\n policy: '',\n };\n });\n\n return acc;\n },\n {}\n );\n\n if (!_.isEmpty(controllers)) {\n actionMap[`api::${apiName}`] = { controllers };\n }\n });\n\n _.forEach(strapi.plugins, (plugin, pluginName) => {\n const controllers = _.reduce(\n plugin.controllers,\n (acc, controller, controllerName) => {\n const contentApiActions = _.pickBy(controller, isContentApi);\n\n if (_.isEmpty(contentApiActions)) {\n return acc;\n }\n\n acc[controllerName] = _.mapValues(contentApiActions, () => {\n return {\n enabled: defaultEnable,\n policy: '',\n };\n });\n\n return acc;\n },\n {}\n );\n\n if (!_.isEmpty(controllers)) {\n actionMap[`plugin::${pluginName}`] = { controllers };\n }\n });\n\n return actionMap;\n },\n\n async getRoutes() {\n const routesMap = {};\n\n _.forEach(strapi.apis, (api, apiName) => {\n const routes = _.flatMap(api.routes, (route) => {\n if (_.has(route, 'routes')) {\n return route.routes;\n }\n\n return route;\n }).filter((route) => route.info.type === 'content-api');\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`api::${apiName}`] = routes.map((route) => ({\n ...route,\n path: urlJoin(apiPrefix, route.path),\n }));\n });\n\n _.forEach(strapi.plugins, (plugin, pluginName) => {\n const transformPrefix = transformRoutePrefixFor(pluginName);\n\n const routes = _.flatMap(plugin.routes, (route) => {\n if (_.has(route, 'routes')) {\n return route.routes.map(transformPrefix);\n }\n\n return transformPrefix(route);\n }).filter((route) => route.info.type === 'content-api');\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`plugin::${pluginName}`] = routes.map((route) => ({\n ...route,\n path: urlJoin(apiPrefix, route.path),\n }));\n });\n\n return routesMap;\n },\n\n async syncPermissions() {\n const roles = await strapi.db.query('plugin::users-permissions.role').findMany();\n const dbPermissions = await strapi.db.query('plugin::users-permissions.permission').findMany();\n\n const permissionsFoundInDB = _.uniq(_.map(dbPermissions, 'action'));\n\n const appActions = _.flatMap(strapi.apis, (api, apiName) => {\n return _.flatMap(api.controllers, (controller, controllerName) => {\n return _.keys(controller).map((actionName) => {\n return `api::${apiName}.${controllerName}.${actionName}`;\n });\n });\n });\n\n const pluginsActions = _.flatMap(strapi.plugins, (plugin, pluginName) => {\n return _.flatMap(plugin.controllers, (controller, controllerName) => {\n return _.keys(controller).map((actionName) => {\n return `plugin::${pluginName}.${controllerName}.${actionName}`;\n });\n });\n });\n\n const allActions = [...appActions, ...pluginsActions];\n\n const toDelete = _.difference(permissionsFoundInDB, allActions);\n\n await Promise.all(\n toDelete.map((action) => {\n return strapi.db\n .query('plugin::users-permissions.permission')\n .delete({ where: { action } });\n })\n );\n\n if (permissionsFoundInDB.length === 0) {\n // create default permissions\n for (const role of roles) {\n const toCreate = pipe(\n filter(({ roleType }) => roleType === role.type || roleType === null),\n map(prop('action'))\n )(DEFAULT_PERMISSIONS);\n\n await Promise.all(\n toCreate.map((action) => {\n return strapi.db.query('plugin::users-permissions.permission').create({\n data: {\n action,\n role: role.id,\n },\n });\n })\n );\n }\n }\n },\n\n async initialize() {\n const roleCount = await strapi.db.query('plugin::users-permissions.role').count();\n\n if (roleCount === 0) {\n await strapi.db.query('plugin::users-permissions.role').create({\n data: {\n name: 'Authenticated',\n description: 'Default role given to authenticated user.',\n type: 'authenticated',\n },\n });\n\n await strapi.db.query('plugin::users-permissions.role').create({\n data: {\n name: 'Public',\n description: 'Default role given to unauthenticated user.',\n type: 'public',\n },\n });\n }\n\n return getService('users-permissions').syncPermissions();\n },\n\n async updateUserRole(user, role) {\n return strapi.db\n .query('plugin::users-permissions.user')\n .update({ where: { id: user.id }, data: { role } });\n },\n\n template(layout, data) {\n const allowedTemplateVariables = objects.keysDeep(data);\n\n // Create a strict interpolation RegExp based on possible variable names\n const interpolate = createStrictInterpolationRegExp(allowedTemplateVariables, 'g');\n\n try {\n return _.template(layout, { interpolate, evaluate: false, escape: false })(data);\n } catch (e) {\n throw new errors.ApplicationError('Invalid email template');\n }\n },\n});\n"],"names":["_","require$$0","filter","map","pipe","prop","require$$1","urlJoin","require$$2","template","createStrictInterpolationRegExp","errors","objects","require$$3","getService","require$$4","DEFAULT_PERMISSIONS","action","roleType","transformRoutePrefixFor","pluginName","route","prefix","config","path","undefined","usersPermissions","strapi","getActions","defaultEnable","actionMap","isContentApi","has","Symbol","for","includes","forEach","apis","api","apiName","controllers","reduce","acc","controller","controllerName","contentApiActions","pickBy","isEmpty","mapValues","enabled","policy","plugins","plugin","getRoutes","routesMap","routes","flatMap","info","type","length","apiPrefix","get","transformPrefix","syncPermissions","roles","db","query","findMany","dbPermissions","permissionsFoundInDB","uniq","appActions","keys","actionName","pluginsActions","allActions","toDelete","difference","Promise","all","delete","where","role","toCreate","create","data","id","initialize","roleCount","count","name","description","updateUserRole","user","update","layout","allowedTemplateVariables","keysDeep","interpolate","evaluate","escape","e","ApplicationError"],"mappings":"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"}
1
+ {"version":3,"file":"users-permissions.mjs","sources":["../../../server/services/users-permissions.js"],"sourcesContent":["'use strict';\n\nconst _ = require('lodash');\nconst { filter, map, pipe, prop } = require('lodash/fp');\nconst urlJoin = require('url-join');\nconst {\n template: { createStrictInterpolationRegExp },\n errors,\n objects,\n sanitizeRoutesMapForSerialization,\n} = require('@strapi/utils');\n\nconst { getService } = require('../utils');\n\nconst DEFAULT_PERMISSIONS = [\n { action: 'plugin::users-permissions.auth.callback', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.connect', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.forgotPassword', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.resetPassword', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.register', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.emailConfirmation', roleType: 'public' },\n { action: 'plugin::users-permissions.auth.sendEmailConfirmation', roleType: 'public' },\n { action: 'plugin::users-permissions.user.me', roleType: 'authenticated' },\n { action: 'plugin::users-permissions.auth.changePassword', roleType: 'authenticated' },\n];\n\nconst transformRoutePrefixFor = (pluginName) => (route) => {\n const prefix = route.config && route.config.prefix;\n const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;\n\n return {\n ...route,\n path,\n };\n};\n\nmodule.exports = ({ strapi }) => ({\n getActions({ defaultEnable = false } = {}) {\n const actionMap = {};\n\n const isContentApi = (action) => {\n if (!_.has(action, Symbol.for('__type__'))) {\n return false;\n }\n\n return action[Symbol.for('__type__')].includes('content-api');\n };\n\n _.forEach(strapi.apis, (api, apiName) => {\n const controllers = _.reduce(\n api.controllers,\n (acc, controller, controllerName) => {\n const contentApiActions = _.pickBy(controller, isContentApi);\n\n if (_.isEmpty(contentApiActions)) {\n return acc;\n }\n\n acc[controllerName] = _.mapValues(contentApiActions, () => {\n return {\n enabled: defaultEnable,\n policy: '',\n };\n });\n\n return acc;\n },\n {}\n );\n\n if (!_.isEmpty(controllers)) {\n actionMap[`api::${apiName}`] = { controllers };\n }\n });\n\n _.forEach(strapi.plugins, (plugin, pluginName) => {\n const controllers = _.reduce(\n plugin.controllers,\n (acc, controller, controllerName) => {\n const contentApiActions = _.pickBy(controller, isContentApi);\n\n if (_.isEmpty(contentApiActions)) {\n return acc;\n }\n\n acc[controllerName] = _.mapValues(contentApiActions, () => {\n return {\n enabled: defaultEnable,\n policy: '',\n };\n });\n\n return acc;\n },\n {}\n );\n\n if (!_.isEmpty(controllers)) {\n actionMap[`plugin::${pluginName}`] = { controllers };\n }\n });\n\n // Return a deeply cloned version to avoid circular references\n return _.cloneDeep(actionMap);\n },\n\n async getRoutes() {\n const routesMap = {};\n\n _.forEach(strapi.apis, (api, apiName) => {\n const routes = _.flatMap(api.routes, (route) => {\n if (_.has(route, 'routes')) {\n return route.routes;\n }\n\n return route;\n }).filter((route) => route.info.type === 'content-api');\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`api::${apiName}`] = routes.map((route) => ({\n ...route,\n path: urlJoin(apiPrefix, route.path),\n }));\n });\n\n _.forEach(strapi.plugins, (plugin, pluginName) => {\n const transformPrefix = transformRoutePrefixFor(pluginName);\n\n const routes = _.flatMap(plugin.routes, (route) => {\n if (_.has(route, 'routes')) {\n return route.routes.map(transformPrefix);\n }\n\n return transformPrefix(route);\n }).filter((route) => route.info.type === 'content-api');\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`plugin::${pluginName}`] = routes.map((route) => ({\n ...route,\n path: urlJoin(apiPrefix, route.path),\n }));\n });\n\n return sanitizeRoutesMapForSerialization(routesMap);\n },\n\n async syncPermissions() {\n const roles = await strapi.db.query('plugin::users-permissions.role').findMany();\n const dbPermissions = await strapi.db.query('plugin::users-permissions.permission').findMany();\n\n const permissionsFoundInDB = _.uniq(_.map(dbPermissions, 'action'));\n\n const appActions = _.flatMap(strapi.apis, (api, apiName) => {\n return _.flatMap(api.controllers, (controller, controllerName) => {\n return _.keys(controller).map((actionName) => {\n return `api::${apiName}.${controllerName}.${actionName}`;\n });\n });\n });\n\n const pluginsActions = _.flatMap(strapi.plugins, (plugin, pluginName) => {\n return _.flatMap(plugin.controllers, (controller, controllerName) => {\n return _.keys(controller).map((actionName) => {\n return `plugin::${pluginName}.${controllerName}.${actionName}`;\n });\n });\n });\n\n const allActions = [...appActions, ...pluginsActions];\n\n const toDelete = _.difference(permissionsFoundInDB, allActions);\n\n await Promise.all(\n toDelete.map((action) => {\n return strapi.db\n .query('plugin::users-permissions.permission')\n .delete({ where: { action } });\n })\n );\n\n if (permissionsFoundInDB.length === 0) {\n // create default permissions\n for (const role of roles) {\n const toCreate = pipe(\n filter(({ roleType }) => roleType === role.type || roleType === null),\n map(prop('action'))\n )(DEFAULT_PERMISSIONS);\n\n await Promise.all(\n toCreate.map((action) => {\n return strapi.db.query('plugin::users-permissions.permission').create({\n data: {\n action,\n role: role.id,\n },\n });\n })\n );\n }\n }\n },\n\n async initialize() {\n const roleCount = await strapi.db.query('plugin::users-permissions.role').count();\n\n if (roleCount === 0) {\n await strapi.db.query('plugin::users-permissions.role').create({\n data: {\n name: 'Authenticated',\n description: 'Default role given to authenticated user.',\n type: 'authenticated',\n },\n });\n\n await strapi.db.query('plugin::users-permissions.role').create({\n data: {\n name: 'Public',\n description: 'Default role given to unauthenticated user.',\n type: 'public',\n },\n });\n }\n\n return getService('users-permissions').syncPermissions();\n },\n\n async updateUserRole(user, role) {\n return strapi.db\n .query('plugin::users-permissions.user')\n .update({ where: { id: user.id }, data: { role } });\n },\n\n template(layout, data) {\n const allowedTemplateVariables = objects.keysDeep(data);\n\n // Create a strict interpolation RegExp based on possible variable names\n const interpolate = createStrictInterpolationRegExp(allowedTemplateVariables, 'g');\n\n try {\n return _.template(layout, { interpolate, evaluate: false, escape: false })(data);\n } catch (e) {\n throw new errors.ApplicationError('Invalid email template');\n }\n },\n});\n"],"names":["_","require$$0","filter","map","pipe","prop","require$$1","urlJoin","require$$2","template","createStrictInterpolationRegExp","errors","objects","sanitizeRoutesMapForSerialization","require$$3","getService","require$$4","DEFAULT_PERMISSIONS","action","roleType","transformRoutePrefixFor","pluginName","route","prefix","config","path","undefined","usersPermissions","strapi","getActions","defaultEnable","actionMap","isContentApi","has","Symbol","for","includes","forEach","apis","api","apiName","controllers","reduce","acc","controller","controllerName","contentApiActions","pickBy","isEmpty","mapValues","enabled","policy","plugins","plugin","cloneDeep","getRoutes","routesMap","routes","flatMap","info","type","length","apiPrefix","get","transformPrefix","syncPermissions","roles","db","query","findMany","dbPermissions","permissionsFoundInDB","uniq","appActions","keys","actionName","pluginsActions","allActions","toDelete","difference","Promise","all","delete","where","role","toCreate","create","data","id","initialize","roleCount","count","name","description","updateUserRole","user","update","layout","allowedTemplateVariables","keysDeep","interpolate","evaluate","escape","e","ApplicationError"],"mappings":";;;;;;;;;;;AAEA,IAAA,MAAMA,CAAIC,GAAAA,UAAAA;IACV,MAAM,EAAEC,MAAM,EAAEC,GAAG,EAAEC,IAAI,EAAEC,IAAI,EAAE,GAAGC,YAAAA;AACpC,IAAA,MAAMC,OAAUC,GAAAA,UAAAA;AAChB,IAAA,MAAM,EACJC,QAAAA,EAAU,EAAEC,+BAA+B,EAAE,EAC7CC,MAAM,EACNC,OAAO,EACPC,iCAAiC,EAClC,GAAGC,UAAAA;IAEJ,MAAM,EAAEC,UAAU,EAAE,GAAGC,YAAAA,EAAAA;AAEvB,IAAA,MAAMC,mBAAsB,GAAA;AAC1B,QAAA;YAAEC,MAAQ,EAAA,yCAAA;YAA2CC,QAAU,EAAA;AAAU,SAAA;AACzE,QAAA;YAAED,MAAQ,EAAA,wCAAA;YAA0CC,QAAU,EAAA;AAAU,SAAA;AACxE,QAAA;YAAED,MAAQ,EAAA,+CAAA;YAAiDC,QAAU,EAAA;AAAU,SAAA;AAC/E,QAAA;YAAED,MAAQ,EAAA,8CAAA;YAAgDC,QAAU,EAAA;AAAU,SAAA;AAC9E,QAAA;YAAED,MAAQ,EAAA,yCAAA;YAA2CC,QAAU,EAAA;AAAU,SAAA;AACzE,QAAA;YAAED,MAAQ,EAAA,kDAAA;YAAoDC,QAAU,EAAA;AAAU,SAAA;AAClF,QAAA;YAAED,MAAQ,EAAA,sDAAA;YAAwDC,QAAU,EAAA;AAAU,SAAA;AACtF,QAAA;YAAED,MAAQ,EAAA,mCAAA;YAAqCC,QAAU,EAAA;AAAiB,SAAA;AAC1E,QAAA;YAAED,MAAQ,EAAA,+CAAA;YAAiDC,QAAU,EAAA;AAAiB;AACvF,KAAA;IAED,MAAMC,uBAAAA,GAA0B,CAACC,UAAAA,GAAe,CAACC,KAAAA,GAAAA;AAC/C,YAAA,MAAMC,SAASD,KAAME,CAAAA,MAAM,IAAIF,KAAME,CAAAA,MAAM,CAACD,MAAM;AAClD,YAAA,MAAME,IAAOF,GAAAA,MAAAA,KAAWG,SAAY,GAAA,CAAA,EAAGH,SAASD,KAAMG,CAAAA,IAAI,CAAE,CAAA,GAAG,CAAC,CAAC,EAAEJ,UAAaC,CAAAA,EAAAA,KAAAA,CAAMG,IAAI,CAAE,CAAA;YAE5F,OAAO;AACL,gBAAA,GAAGH,KAAK;AACRG,gBAAAA;AACJ,aAAA;AACA,SAAA;AAEAE,IAAAA,gBAAAA,GAAiB,CAAC,EAAEC,MAAM,EAAE,IAAM;AAChCC,YAAAA,UAAAA,CAAAA,CAAW,EAAEC,aAAgB,GAAA,KAAK,EAAE,GAAG,EAAE,EAAA;AACvC,gBAAA,MAAMC,YAAY,EAAA;AAElB,gBAAA,MAAMC,eAAe,CAACd,MAAAA,GAAAA;oBACpB,IAAI,CAAClB,EAAEiC,GAAG,CAACf,QAAQgB,MAAOC,CAAAA,GAAG,CAAC,UAAc,CAAA,CAAA,EAAA;wBAC1C,OAAO,KAAA;AACR;oBAED,OAAOjB,MAAM,CAACgB,MAAOC,CAAAA,GAAG,CAAC,UAAY,CAAA,CAAA,CAACC,QAAQ,CAAC,aAAA,CAAA;AACrD,iBAAA;AAEIpC,gBAAAA,CAAAA,CAAEqC,OAAO,CAACT,MAAAA,CAAOU,IAAI,EAAE,CAACC,GAAKC,EAAAA,OAAAA,GAAAA;oBAC3B,MAAMC,WAAAA,GAAczC,EAAE0C,MAAM,CAC1BH,IAAIE,WAAW,EACf,CAACE,GAAAA,EAAKC,UAAYC,EAAAA,cAAAA,GAAAA;AAChB,wBAAA,MAAMC,iBAAoB9C,GAAAA,CAAAA,CAAE+C,MAAM,CAACH,UAAYZ,EAAAA,YAAAA,CAAAA;wBAE/C,IAAIhC,CAAAA,CAAEgD,OAAO,CAACF,iBAAoB,CAAA,EAAA;4BAChC,OAAOH,GAAAA;AACR;AAEDA,wBAAAA,GAAG,CAACE,cAAe,CAAA,GAAG7C,CAAEiD,CAAAA,SAAS,CAACH,iBAAmB,EAAA,IAAA;4BACnD,OAAO;gCACLI,OAASpB,EAAAA,aAAAA;gCACTqB,MAAQ,EAAA;AACtB,6BAAA;AACA,yBAAA,CAAA;wBAEU,OAAOR,GAAAA;AACR,qBAAA,EACD,EAAE,CAAA;AAGJ,oBAAA,IAAI,CAAC3C,CAAAA,CAAEgD,OAAO,CAACP,WAAc,CAAA,EAAA;AAC3BV,wBAAAA,SAAS,CAAC,CAAC,KAAK,EAAES,OAAAA,CAAAA,CAAS,CAAC,GAAG;AAAEC,4BAAAA;;AAClC;AACP,iBAAA,CAAA;AAEIzC,gBAAAA,CAAAA,CAAEqC,OAAO,CAACT,MAAAA,CAAOwB,OAAO,EAAE,CAACC,MAAQhC,EAAAA,UAAAA,GAAAA;oBACjC,MAAMoB,WAAAA,GAAczC,EAAE0C,MAAM,CAC1BW,OAAOZ,WAAW,EAClB,CAACE,GAAAA,EAAKC,UAAYC,EAAAA,cAAAA,GAAAA;AAChB,wBAAA,MAAMC,iBAAoB9C,GAAAA,CAAAA,CAAE+C,MAAM,CAACH,UAAYZ,EAAAA,YAAAA,CAAAA;wBAE/C,IAAIhC,CAAAA,CAAEgD,OAAO,CAACF,iBAAoB,CAAA,EAAA;4BAChC,OAAOH,GAAAA;AACR;AAEDA,wBAAAA,GAAG,CAACE,cAAe,CAAA,GAAG7C,CAAEiD,CAAAA,SAAS,CAACH,iBAAmB,EAAA,IAAA;4BACnD,OAAO;gCACLI,OAASpB,EAAAA,aAAAA;gCACTqB,MAAQ,EAAA;AACtB,6BAAA;AACA,yBAAA,CAAA;wBAEU,OAAOR,GAAAA;AACR,qBAAA,EACD,EAAE,CAAA;AAGJ,oBAAA,IAAI,CAAC3C,CAAAA,CAAEgD,OAAO,CAACP,WAAc,CAAA,EAAA;AAC3BV,wBAAAA,SAAS,CAAC,CAAC,QAAQ,EAAEV,UAAAA,CAAAA,CAAY,CAAC,GAAG;AAAEoB,4BAAAA;;AACxC;AACP,iBAAA,CAAA;;gBAGI,OAAOzC,CAAAA,CAAEsD,SAAS,CAACvB,SAAAA,CAAAA;AACpB,aAAA;YAED,MAAMwB,SAAAA,CAAAA,GAAAA;AACJ,gBAAA,MAAMC,YAAY,EAAA;AAElBxD,gBAAAA,CAAAA,CAAEqC,OAAO,CAACT,MAAAA,CAAOU,IAAI,EAAE,CAACC,GAAKC,EAAAA,OAAAA,GAAAA;AAC3B,oBAAA,MAAMiB,SAASzD,CAAE0D,CAAAA,OAAO,CAACnB,GAAIkB,CAAAA,MAAM,EAAE,CAACnC,KAAAA,GAAAA;AACpC,wBAAA,IAAItB,CAAEiC,CAAAA,GAAG,CAACX,KAAAA,EAAO,QAAW,CAAA,EAAA;AAC1B,4BAAA,OAAOA,MAAMmC,MAAM;AACpB;wBAED,OAAOnC,KAAAA;qBACNpB,CAAAA,CAAAA,MAAM,CAAC,CAACoB,KAAAA,GAAUA,MAAMqC,IAAI,CAACC,IAAI,KAAK,aAAA,CAAA;oBAEzC,IAAIH,MAAAA,CAAOI,MAAM,KAAK,CAAG,EAAA;AACvB,wBAAA;AACD;AAED,oBAAA,MAAMC,SAAYlC,GAAAA,MAAAA,CAAOJ,MAAM,CAACuC,GAAG,CAAC,iBAAA,CAAA;AACpCP,oBAAAA,SAAS,CAAC,CAAC,KAAK,EAAEhB,OAAS,CAAA,CAAA,CAAC,GAAGiB,MAAAA,CAAOtD,GAAG,CAAC,CAACmB,KAAAA,IAAW;AACpD,4BAAA,GAAGA,KAAK;4BACRG,IAAMlB,EAAAA,OAAAA,CAAQuD,SAAWxC,EAAAA,KAAAA,CAAMG,IAAI;yBACpC,CAAA,CAAA;AACP,iBAAA,CAAA;AAEIzB,gBAAAA,CAAAA,CAAEqC,OAAO,CAACT,MAAAA,CAAOwB,OAAO,EAAE,CAACC,MAAQhC,EAAAA,UAAAA,GAAAA;AACjC,oBAAA,MAAM2C,kBAAkB5C,uBAAwBC,CAAAA,UAAAA,CAAAA;AAEhD,oBAAA,MAAMoC,SAASzD,CAAE0D,CAAAA,OAAO,CAACL,MAAOI,CAAAA,MAAM,EAAE,CAACnC,KAAAA,GAAAA;AACvC,wBAAA,IAAItB,CAAEiC,CAAAA,GAAG,CAACX,KAAAA,EAAO,QAAW,CAAA,EAAA;AAC1B,4BAAA,OAAOA,KAAMmC,CAAAA,MAAM,CAACtD,GAAG,CAAC6D,eAAAA,CAAAA;AACzB;AAED,wBAAA,OAAOA,eAAgB1C,CAAAA,KAAAA,CAAAA;qBACtBpB,CAAAA,CAAAA,MAAM,CAAC,CAACoB,KAAAA,GAAUA,MAAMqC,IAAI,CAACC,IAAI,KAAK,aAAA,CAAA;oBAEzC,IAAIH,MAAAA,CAAOI,MAAM,KAAK,CAAG,EAAA;AACvB,wBAAA;AACD;AAED,oBAAA,MAAMC,SAAYlC,GAAAA,MAAAA,CAAOJ,MAAM,CAACuC,GAAG,CAAC,iBAAA,CAAA;AACpCP,oBAAAA,SAAS,CAAC,CAAC,QAAQ,EAAEnC,UAAY,CAAA,CAAA,CAAC,GAAGoC,MAAAA,CAAOtD,GAAG,CAAC,CAACmB,KAAAA,IAAW;AAC1D,4BAAA,GAAGA,KAAK;4BACRG,IAAMlB,EAAAA,OAAAA,CAAQuD,SAAWxC,EAAAA,KAAAA,CAAMG,IAAI;yBACpC,CAAA,CAAA;AACP,iBAAA,CAAA;AAEI,gBAAA,OAAOZ,iCAAkC2C,CAAAA,SAAAA,CAAAA;AAC1C,aAAA;YAED,MAAMS,eAAAA,CAAAA,GAAAA;gBACJ,MAAMC,KAAAA,GAAQ,MAAMtC,MAAOuC,CAAAA,EAAE,CAACC,KAAK,CAAC,kCAAkCC,QAAQ,EAAA;gBAC9E,MAAMC,aAAAA,GAAgB,MAAM1C,MAAOuC,CAAAA,EAAE,CAACC,KAAK,CAAC,wCAAwCC,QAAQ,EAAA;AAE5F,gBAAA,MAAME,uBAAuBvE,CAAEwE,CAAAA,IAAI,CAACxE,CAAEG,CAAAA,GAAG,CAACmE,aAAe,EAAA,QAAA,CAAA,CAAA;gBAEzD,MAAMG,UAAAA,GAAazE,EAAE0D,OAAO,CAAC9B,OAAOU,IAAI,EAAE,CAACC,GAAKC,EAAAA,OAAAA,GAAAA;AAC9C,oBAAA,OAAOxC,EAAE0D,OAAO,CAACnB,IAAIE,WAAW,EAAE,CAACG,UAAYC,EAAAA,cAAAA,GAAAA;AAC7C,wBAAA,OAAO7C,EAAE0E,IAAI,CAAC9B,UAAYzC,CAAAA,CAAAA,GAAG,CAAC,CAACwE,UAAAA,GAAAA;4BAC7B,OAAO,CAAC,KAAK,EAAEnC,OAAAA,CAAQ,CAAC,EAAEK,cAAAA,CAAe,CAAC,EAAE8B,UAAY,CAAA,CAAA;AAClE,yBAAA,CAAA;AACA,qBAAA,CAAA;AACA,iBAAA,CAAA;gBAEI,MAAMC,cAAAA,GAAiB5E,EAAE0D,OAAO,CAAC9B,OAAOwB,OAAO,EAAE,CAACC,MAAQhC,EAAAA,UAAAA,GAAAA;AACxD,oBAAA,OAAOrB,EAAE0D,OAAO,CAACL,OAAOZ,WAAW,EAAE,CAACG,UAAYC,EAAAA,cAAAA,GAAAA;AAChD,wBAAA,OAAO7C,EAAE0E,IAAI,CAAC9B,UAAYzC,CAAAA,CAAAA,GAAG,CAAC,CAACwE,UAAAA,GAAAA;4BAC7B,OAAO,CAAC,QAAQ,EAAEtD,UAAAA,CAAW,CAAC,EAAEwB,cAAAA,CAAe,CAAC,EAAE8B,UAAY,CAAA,CAAA;AACxE,yBAAA,CAAA;AACA,qBAAA,CAAA;AACA,iBAAA,CAAA;AAEI,gBAAA,MAAME,UAAa,GAAA;AAAIJ,oBAAAA,GAAAA,UAAAA;AAAeG,oBAAAA,GAAAA;AAAe,iBAAA;AAErD,gBAAA,MAAME,QAAW9E,GAAAA,CAAAA,CAAE+E,UAAU,CAACR,oBAAsBM,EAAAA,UAAAA,CAAAA;AAEpD,gBAAA,MAAMG,QAAQC,GAAG,CACfH,QAAS3E,CAAAA,GAAG,CAAC,CAACe,MAAAA,GAAAA;AACZ,oBAAA,OAAOU,OAAOuC,EAAE,CACbC,KAAK,CAAC,sCAAA,CAAA,CACNc,MAAM,CAAC;wBAAEC,KAAO,EAAA;AAAEjE,4BAAAA;AAAM;AAAI,qBAAA,CAAA;AACvC,iBAAA,CAAA,CAAA;gBAGI,IAAIqD,oBAAAA,CAAqBV,MAAM,KAAK,CAAG,EAAA;;oBAErC,KAAK,MAAMuB,QAAQlB,KAAO,CAAA;AACxB,wBAAA,MAAMmB,WAAWjF,IACfF,CAAAA,MAAAA,CAAO,CAAC,EAAEiB,QAAQ,EAAE,GAAKA,QAAaiE,KAAAA,IAAAA,CAAKxB,IAAI,IAAIzC,QAAAA,KAAa,IAChEhB,CAAAA,EAAAA,GAAAA,CAAIE,KAAK,QACTY,CAAAA,CAAAA,CAAAA,CAAAA,mBAAAA,CAAAA;AAEF,wBAAA,MAAM+D,QAAQC,GAAG,CACfI,QAASlF,CAAAA,GAAG,CAAC,CAACe,MAAAA,GAAAA;AACZ,4BAAA,OAAOU,OAAOuC,EAAE,CAACC,KAAK,CAAC,sCAAA,CAAA,CAAwCkB,MAAM,CAAC;gCACpEC,IAAM,EAAA;AACJrE,oCAAAA,MAAAA;AACAkE,oCAAAA,IAAAA,EAAMA,KAAKI;AACZ;AACf,6BAAA,CAAA;AACA,yBAAA,CAAA,CAAA;AAEO;AACF;AACF,aAAA;YAED,MAAMC,UAAAA,CAAAA,GAAAA;gBACJ,MAAMC,SAAAA,GAAY,MAAM9D,MAAOuC,CAAAA,EAAE,CAACC,KAAK,CAAC,kCAAkCuB,KAAK,EAAA;AAE/E,gBAAA,IAAID,cAAc,CAAG,EAAA;AACnB,oBAAA,MAAM9D,OAAOuC,EAAE,CAACC,KAAK,CAAC,gCAAA,CAAA,CAAkCkB,MAAM,CAAC;wBAC7DC,IAAM,EAAA;4BACJK,IAAM,EAAA,eAAA;4BACNC,WAAa,EAAA,2CAAA;4BACbjC,IAAM,EAAA;AACP;AACT,qBAAA,CAAA;AAEM,oBAAA,MAAMhC,OAAOuC,EAAE,CAACC,KAAK,CAAC,gCAAA,CAAA,CAAkCkB,MAAM,CAAC;wBAC7DC,IAAM,EAAA;4BACJK,IAAM,EAAA,QAAA;4BACNC,WAAa,EAAA,6CAAA;4BACbjC,IAAM,EAAA;AACP;AACT,qBAAA,CAAA;AACK;gBAED,OAAO7C,UAAAA,CAAW,qBAAqBkD,eAAe,EAAA;AACvD,aAAA;YAED,MAAM6B,cAAAA,CAAAA,CAAeC,IAAI,EAAEX,IAAI,EAAA;AAC7B,gBAAA,OAAOxD,OAAOuC,EAAE,CACbC,KAAK,CAAC,gCAAA,CAAA,CACN4B,MAAM,CAAC;oBAAEb,KAAO,EAAA;AAAEK,wBAAAA,EAAAA,EAAIO,KAAKP;AAAI,qBAAA;oBAAED,IAAM,EAAA;AAAEH,wBAAAA;AAAM;AAAA,iBAAA,CAAA;AACnD,aAAA;YAED3E,QAASwF,CAAAA,CAAAA,MAAM,EAAEV,IAAI,EAAA;gBACnB,MAAMW,wBAAAA,GAA2BtF,OAAQuF,CAAAA,QAAQ,CAACZ,IAAAA,CAAAA;;gBAGlD,MAAMa,WAAAA,GAAc1F,gCAAgCwF,wBAA0B,EAAA,GAAA,CAAA;gBAE9E,IAAI;oBACF,OAAOlG,CAAAA,CAAES,QAAQ,CAACwF,MAAQ,EAAA;AAAEG,wBAAAA,WAAAA;wBAAaC,QAAU,EAAA,KAAA;wBAAOC,MAAQ,EAAA;qBAASf,CAAAA,CAAAA,IAAAA,CAAAA;AAC5E,iBAAA,CAAC,OAAOgB,CAAG,EAAA;oBACV,MAAM,IAAI5F,MAAO6F,CAAAA,gBAAgB,CAAC,wBAAA,CAAA;AACnC;AACF;SACH,CAAA;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-users-permissions",
3
- "version": "0.0.0-next.e21fe90bf2ab9906267ea6e6ca620bdcc729906c",
3
+ "version": "0.0.0-next.e2e3ca14971ee768e1a227a209362264fd0132d4",
4
4
  "description": "Protect your API with a full-authentication process based on JWT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,16 +48,16 @@
48
48
  "watch": "run -T rollup -c -w"
49
49
  },
50
50
  "dependencies": {
51
- "@strapi/design-system": "2.0.0-rc.21",
52
- "@strapi/icons": "2.0.0-rc.21",
53
- "@strapi/utils": "0.0.0-next.e21fe90bf2ab9906267ea6e6ca620bdcc729906c",
51
+ "@strapi/design-system": "2.0.1",
52
+ "@strapi/icons": "2.0.1",
53
+ "@strapi/utils": "0.0.0-next.e2e3ca14971ee768e1a227a209362264fd0132d4",
54
54
  "bcryptjs": "2.4.3",
55
55
  "formik": "2.4.5",
56
56
  "grant": "^5.4.8",
57
57
  "immer": "9.0.21",
58
58
  "jsonwebtoken": "9.0.0",
59
59
  "jwk-to-pem": "2.0.5",
60
- "koa": "2.15.4",
60
+ "koa": "2.16.1",
61
61
  "koa2-ratelimit": "^1.1.3",
62
62
  "lodash": "4.17.21",
63
63
  "prop-types": "^15.8.1",
@@ -66,10 +66,11 @@
66
66
  "react-query": "3.39.3",
67
67
  "react-redux": "8.1.3",
68
68
  "url-join": "4.0.1",
69
- "yup": "0.32.9"
69
+ "yup": "0.32.9",
70
+ "zod": "3.25.67"
70
71
  },
71
72
  "devDependencies": {
72
- "@strapi/strapi": "0.0.0-next.e21fe90bf2ab9906267ea6e6ca620bdcc729906c",
73
+ "@strapi/strapi": "0.0.0-next.e2e3ca14971ee768e1a227a209362264fd0132d4",
73
74
  "@testing-library/dom": "10.1.0",
74
75
  "@testing-library/react": "15.0.7",
75
76
  "@testing-library/user-event": "14.5.2",
@@ -87,7 +88,7 @@
87
88
  "styled-components": "^6.0.0"
88
89
  },
89
90
  "engines": {
90
- "node": ">=18.0.0 <=22.x.x",
91
+ "node": ">=20.0.0 <=24.x.x",
91
92
  "npm": ">=6.0.0"
92
93
  },
93
94
  "strapi": {
@@ -11,6 +11,18 @@ const crypto = require('crypto');
11
11
  const _ = require('lodash');
12
12
  const { getService } = require('../utils');
13
13
  const usersPermissionsActions = require('./users-permissions-actions');
14
+ const {
15
+ DEFAULT_ACCESS_TOKEN_LIFESPAN,
16
+ DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
17
+ DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
18
+ DEFAULT_MAX_SESSION_LIFESPAN,
19
+ DEFAULT_IDLE_SESSION_LIFESPAN,
20
+ } = require('../services/constants');
21
+
22
+ const getSessionManager = () => {
23
+ const manager = strapi.sessionManager;
24
+ return manager ?? null;
25
+ };
14
26
 
15
27
  const initGrant = async (pluginStore) => {
16
28
  const allProviders = getService('providers-registry').getAll();
@@ -113,6 +125,25 @@ module.exports = async ({ strapi }) => {
113
125
 
114
126
  await getService('users-permissions').initialize();
115
127
 
128
+ // Define users-permissions origin configuration for sessionManager
129
+ const upConfig = strapi.config.get('plugin::users-permissions');
130
+ const sessionManager = getSessionManager();
131
+
132
+ if (sessionManager) {
133
+ sessionManager.defineOrigin('users-permissions', {
134
+ jwtSecret: upConfig.jwtSecret || strapi.config.get('admin.auth.secret'),
135
+ accessTokenLifespan: upConfig.sessions?.accessTokenLifespan || DEFAULT_ACCESS_TOKEN_LIFESPAN,
136
+ maxRefreshTokenLifespan:
137
+ upConfig.sessions?.maxRefreshTokenLifespan || DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
138
+ idleRefreshTokenLifespan:
139
+ upConfig.sessions?.idleRefreshTokenLifespan || DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
140
+ maxSessionLifespan: upConfig.sessions?.maxSessionLifespan || DEFAULT_MAX_SESSION_LIFESPAN,
141
+ idleSessionLifespan: upConfig.sessions?.idleSessionLifespan || DEFAULT_IDLE_SESSION_LIFESPAN,
142
+ algorithm: upConfig.jwt?.algorithm,
143
+ jwtOptions: upConfig.jwt || {},
144
+ });
145
+ }
146
+
116
147
  if (!strapi.config.get('plugin::users-permissions.jwtSecret')) {
117
148
  if (process.env.NODE_ENV !== 'development') {
118
149
  throw new Error(
package/server/config.js CHANGED
@@ -1,11 +1,33 @@
1
1
  'use strict';
2
2
 
3
+ const {
4
+ DEFAULT_ACCESS_TOKEN_LIFESPAN,
5
+ DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
6
+ DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
7
+ DEFAULT_MAX_SESSION_LIFESPAN,
8
+ DEFAULT_IDLE_SESSION_LIFESPAN,
9
+ } = require('./services/constants');
10
+
3
11
  module.exports = {
4
12
  default: ({ env }) => ({
5
13
  jwtSecret: env('JWT_SECRET'),
6
14
  jwt: {
7
15
  expiresIn: '30d',
8
16
  },
17
+ /**
18
+ * JWT management mode for the Content API authentication
19
+ * - "legacy-support": use plugin JWTs (backward compatible)
20
+ * - "refresh": use SessionManager (access/refresh tokens)
21
+ */
22
+ jwtManagement: 'legacy-support',
23
+ sessions: {
24
+ accessTokenLifespan: DEFAULT_ACCESS_TOKEN_LIFESPAN,
25
+ maxRefreshTokenLifespan: DEFAULT_MAX_REFRESH_TOKEN_LIFESPAN,
26
+ idleRefreshTokenLifespan: DEFAULT_IDLE_REFRESH_TOKEN_LIFESPAN,
27
+ maxSessionLifespan: DEFAULT_MAX_SESSION_LIFESPAN,
28
+ idleSessionLifespan: DEFAULT_IDLE_SESSION_LIFESPAN,
29
+ httpOnly: false,
30
+ },
9
31
  ratelimit: {
10
32
  interval: 60000,
11
33
  max: 10,
@@ -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,37 @@ module.exports = ({ strapi }) => ({
137
231
 
138
232
  await getService('user').edit(user.id, { password });
139
233
 
140
- ctx.send({
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
+ if (deviceId) {
239
+ // Invalidate sessions: specific device if deviceId provided
240
+ await strapi
241
+ .sessionManager('users-permissions')
242
+ .invalidateRefreshToken(String(user.id), deviceId);
243
+ }
244
+
245
+ const newDeviceId = deviceId || crypto.randomUUID();
246
+ const refresh = await strapi
247
+ .sessionManager('users-permissions')
248
+ .generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
249
+
250
+ const access = await strapi
251
+ .sessionManager('users-permissions')
252
+ .generateAccessToken(refresh.token);
253
+ if ('error' in access) {
254
+ throw new ApplicationError('Invalid credentials');
255
+ }
256
+
257
+ return ctx.send({
258
+ jwt: access.token,
259
+ refreshToken: refresh.token,
260
+ user: await sanitizeUser(user, ctx),
261
+ });
262
+ }
263
+
264
+ return ctx.send({
141
265
  jwt: getService('jwt').issue({ id: user.id }),
142
266
  user: await sanitizeUser(user, ctx),
143
267
  });
@@ -168,13 +292,115 @@ module.exports = ({ strapi }) => ({
168
292
  password,
169
293
  });
170
294
 
171
- // Update the user.
172
- ctx.send({
295
+ const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
296
+ if (mode === 'refresh') {
297
+ const deviceId = extractDeviceId(ctx.request.body);
298
+
299
+ if (deviceId) {
300
+ // Invalidate sessions: specific device if deviceId provided
301
+ await strapi
302
+ .sessionManager('users-permissions')
303
+ .invalidateRefreshToken(String(user.id), deviceId);
304
+ }
305
+
306
+ const newDeviceId = deviceId || crypto.randomUUID();
307
+ const refresh = await strapi
308
+ .sessionManager('users-permissions')
309
+ .generateRefreshToken(String(user.id), newDeviceId, { type: 'refresh' });
310
+
311
+ const access = await strapi
312
+ .sessionManager('users-permissions')
313
+ .generateAccessToken(refresh.token);
314
+ if ('error' in access) {
315
+ throw new ApplicationError('Invalid credentials');
316
+ }
317
+
318
+ return ctx.send({
319
+ jwt: access.token,
320
+ refreshToken: refresh.token,
321
+ user: await sanitizeUser(user, ctx),
322
+ });
323
+ }
324
+
325
+ return ctx.send({
173
326
  jwt: getService('jwt').issue({ id: user.id }),
174
327
  user: await sanitizeUser(user, ctx),
175
328
  });
176
329
  },
330
+ async refresh(ctx) {
331
+ const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
332
+ if (mode !== 'refresh') {
333
+ return ctx.notFound();
334
+ }
335
+
336
+ const { refreshToken } = ctx.request.body || {};
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
+ }
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 upSessions = strapi.config.get('plugin::users-permissions.sessions');
356
+ const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
357
+ if (upSessions?.httpOnly || requestHttpOnly) {
358
+ const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
359
+ const isProduction = process.env.NODE_ENV === 'production';
360
+ const isSecure =
361
+ typeof upSessions.cookie?.secure === 'boolean' ? upSessions.cookie?.secure : isProduction;
362
+
363
+ const cookieOptions = {
364
+ httpOnly: true,
365
+ secure: isSecure,
366
+ sameSite: upSessions.cookie?.sameSite ?? 'lax',
367
+ path: upSessions.cookie?.path ?? '/',
368
+ domain: upSessions.cookie?.domain,
369
+ overwrite: true,
370
+ };
371
+ ctx.cookies.set(cookieName, rotation.token, cookieOptions);
372
+ return ctx.send({ jwt: result.token });
373
+ }
374
+ return ctx.send({ jwt: result.token, refreshToken: rotation.token });
375
+ },
376
+ async logout(ctx) {
377
+ const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
378
+ if (mode !== 'refresh') {
379
+ return ctx.notFound();
380
+ }
381
+
382
+ // Invalidate all sessions for the authenticated user, or by deviceId if provided
383
+ if (!ctx.state.user) {
384
+ return ctx.unauthorized('Missing authentication');
385
+ }
386
+
387
+ const deviceId = extractDeviceId(ctx.request.body);
388
+ try {
389
+ await strapi
390
+ .sessionManager('users-permissions')
391
+ .invalidateRefreshToken(String(ctx.state.user.id), deviceId);
392
+ } catch (err) {
393
+ strapi.log.error('UP logout failed', err);
394
+ }
177
395
 
396
+ const upSessions = strapi.config.get('plugin::users-permissions.sessions');
397
+ const requestHttpOnly = ctx.request.header['x-strapi-refresh-cookie'] === 'httpOnly';
398
+ if (upSessions?.httpOnly || requestHttpOnly) {
399
+ const cookieName = upSessions.cookie?.name || 'strapi_up_refresh';
400
+ ctx.cookies.set(cookieName, '', { expires: new Date(0) });
401
+ }
402
+ return ctx.send({ ok: true });
403
+ },
178
404
  async connect(ctx, next) {
179
405
  const grant = require('grant').koa();
180
406
 
@@ -387,12 +613,26 @@ module.exports = ({ strapi }) => ({
387
613
  return ctx.send({ user: sanitizedUser });
388
614
  }
389
615
 
390
- const jwt = getService('jwt').issue(_.pick(user, ['id']));
616
+ const mode = strapi.config.get('plugin::users-permissions.jwtManagement', 'legacy-support');
617
+ if (mode === 'refresh') {
618
+ const deviceId = extractDeviceId(ctx.request.body) || crypto.randomUUID();
391
619
 
392
- return ctx.send({
393
- jwt,
394
- user: sanitizedUser,
395
- });
620
+ const refresh = await strapi
621
+ .sessionManager('users-permissions')
622
+ .generateRefreshToken(String(user.id), deviceId, { type: 'refresh' });
623
+
624
+ const access = await strapi
625
+ .sessionManager('users-permissions')
626
+ .generateAccessToken(refresh.token);
627
+ if ('error' in access) {
628
+ throw new ApplicationError('Invalid credentials');
629
+ }
630
+
631
+ return ctx.send({ jwt: access.token, refreshToken: refresh.token, user: sanitizedUser });
632
+ }
633
+
634
+ const jwt = getService('jwt').issue(_.pick(user, ['id']));
635
+ return ctx.send({ jwt, user: sanitizedUser });
396
636
  },
397
637
 
398
638
  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, ValidationError, NotFoundError, ForbiddenError } =
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') && !password && user.provider === 'local') {
137
- throw new ValidationError('password.notNull');
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.string().min(1),
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({