@strapi/admin 5.44.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin/src/components/Table.js.map +1 -1
- package/dist/admin/admin/src/components/Table.mjs.map +1 -1
- package/dist/admin/admin/src/constants.js +49 -0
- package/dist/admin/admin/src/constants.js.map +1 -1
- package/dist/admin/admin/src/constants.mjs +49 -0
- package/dist/admin/admin/src/constants.mjs.map +1 -1
- package/dist/admin/admin/src/core/apis/router.js +4 -4
- package/dist/admin/admin/src/core/apis/router.js.map +1 -1
- package/dist/admin/admin/src/core/apis/router.mjs +4 -4
- package/dist/admin/admin/src/core/apis/router.mjs.map +1 -1
- package/dist/admin/admin/src/features/Tracking.js.map +1 -1
- package/dist/admin/admin/src/features/Tracking.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/{pages/ApiTokens/EditView/components → components/Tokens}/FormApiTokenContainer.js +48 -11
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormApiTokenContainer.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/{pages/ApiTokens/EditView/components → components/Tokens}/FormApiTokenContainer.mjs +49 -12
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormApiTokenContainer.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.js +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.mjs +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/LifeSpanInput.js +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/LifeSpanInput.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/LifeSpanInput.mjs +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/LifeSpanInput.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/Table.js +21 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/Table.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/Table.mjs +21 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/Table.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.js +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.mjs +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/constants.js +33 -0
- package/dist/admin/admin/src/pages/Settings/components/Tokens/constants.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/constants.mjs +14 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/constants.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/{pages/ApiTokens/EditView → components/Tokens}/utils/getDateOfExpiration.js +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/utils/getDateOfExpiration.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/{pages/ApiTokens/EditView → components/Tokens}/utils/getDateOfExpiration.mjs +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/utils/getDateOfExpiration.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/constants.js +182 -151
- package/dist/admin/admin/src/pages/Settings/constants.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/constants.mjs +182 -151
- package/dist/admin/admin/src/pages/Settings/constants.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/CreateView.js +17 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/CreateView.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/CreateView.mjs +15 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/CreateView.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/EditViewPage.js +314 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/EditViewPage.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/EditViewPage.mjs +292 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/EditViewPage.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/components/AdminPermissions.js +70 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/components/AdminPermissions.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/components/AdminPermissions.mjs +49 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/EditView/components/AdminPermissions.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/ListView.js +254 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/ListView.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/ListView.mjs +231 -0
- package/dist/admin/admin/src/pages/Settings/pages/AdminTokens/ListView.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.js +42 -33
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.mjs +43 -34
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/ListView.js +3 -2
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/ListView.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/ListView.mjs +3 -2
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/ListView.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/CollapsePropertyMatrix.js +23 -12
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/CollapsePropertyMatrix.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/CollapsePropertyMatrix.mjs +23 -12
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/CollapsePropertyMatrix.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ConditionsModal.js +124 -35
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ConditionsModal.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ConditionsModal.mjs +126 -37
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ConditionsModal.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ContentTypeCollapses.js +24 -9
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ContentTypeCollapses.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ContentTypeCollapses.mjs +24 -9
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/ContentTypeCollapses.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/GlobalActions.js +5 -3
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/GlobalActions.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/GlobalActions.mjs +5 -3
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/GlobalActions.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/Permissions.js +171 -36
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/Permissions.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/Permissions.mjs +172 -37
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/Permissions.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/PluginsAndSettings.js +5 -3
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/PluginsAndSettings.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/PluginsAndSettings.mjs +5 -3
- package/dist/admin/admin/src/pages/Settings/pages/Roles/components/PluginsAndSettings.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/hooks/usePermissionsDataManager.js +59 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/hooks/usePermissionsDataManager.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/hooks/usePermissionsDataManager.mjs +40 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/hooks/usePermissionsDataManager.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/createPermissionChecker.js +89 -0
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/createPermissionChecker.js.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/createPermissionChecker.mjs +86 -0
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/createPermissionChecker.mjs.map +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/updateValues.js +35 -9
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/updateValues.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/updateValues.mjs +35 -10
- package/dist/admin/admin/src/pages/Settings/pages/Roles/utils/updateValues.mjs.map +1 -1
- package/dist/admin/admin/src/render.js +6 -3
- package/dist/admin/admin/src/render.js.map +1 -1
- package/dist/admin/admin/src/render.mjs +6 -3
- package/dist/admin/admin/src/render.mjs.map +1 -1
- package/dist/admin/admin/src/router.js +4 -4
- package/dist/admin/admin/src/router.js.map +1 -1
- package/dist/admin/admin/src/router.mjs +1 -1
- package/dist/admin/admin/src/router.mjs.map +1 -1
- package/dist/admin/admin/src/services/apiTokens.js +85 -2
- package/dist/admin/admin/src/services/apiTokens.js.map +1 -1
- package/dist/admin/admin/src/services/apiTokens.mjs +80 -3
- package/dist/admin/admin/src/services/apiTokens.mjs.map +1 -1
- package/dist/admin/admin/src/translations/en.json.js +16 -1
- package/dist/admin/admin/src/translations/en.json.js.map +1 -1
- package/dist/admin/admin/src/translations/en.json.mjs +16 -1
- package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.js +4 -1
- package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.mjs +4 -1
- package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
- package/dist/admin/admin/tests/server.js +99 -21
- package/dist/admin/admin/tests/server.js.map +1 -1
- package/dist/admin/admin/tests/server.mjs +99 -21
- package/dist/admin/admin/tests/server.mjs.map +1 -1
- package/dist/admin/src/constants.d.ts +26 -0
- package/dist/admin/src/core/apis/router.d.ts +1 -1
- package/dist/admin/src/features/Tracking.d.ts +2 -1
- package/dist/admin/src/pages/Settings/components/Tokens/FormApiTokenContainer.d.ts +24 -0
- package/dist/admin/src/pages/Settings/components/Tokens/Table.d.ts +2 -1
- package/dist/admin/src/pages/Settings/components/Tokens/constants.d.ts +17 -0
- package/dist/admin/src/pages/Settings/constants.d.ts +1 -1
- package/dist/admin/src/pages/Settings/pages/AdminTokens/CreateView.d.ts +1 -0
- package/dist/admin/src/pages/Settings/pages/AdminTokens/EditView/EditViewPage.d.ts +2 -0
- package/dist/admin/src/pages/Settings/pages/AdminTokens/EditView/components/AdminPermissions.d.ts +13 -0
- package/dist/admin/src/pages/Settings/pages/AdminTokens/ListView.d.ts +2 -0
- package/dist/admin/src/pages/Settings/pages/Roles/components/CollapsePropertyMatrix.d.ts +4 -3
- package/dist/admin/src/pages/Settings/pages/Roles/components/ConditionsModal.d.ts +3 -1
- package/dist/admin/src/pages/Settings/pages/Roles/components/ContentTypeCollapses.d.ts +1 -0
- package/dist/admin/src/pages/Settings/pages/Roles/components/Permissions.d.ts +5 -0
- package/dist/admin/src/pages/Settings/pages/Roles/hooks/usePermissionsDataManager.d.ts +8 -7
- package/dist/admin/src/pages/Settings/pages/Roles/utils/createPermissionChecker.d.ts +27 -0
- package/dist/admin/src/pages/Settings/pages/Roles/utils/updateValues.d.ts +8 -2
- package/dist/admin/src/services/apiTokens.d.ts +5 -2
- package/dist/admin/src/types/permissions.d.ts +1 -1
- package/dist/server/server/src/bootstrap.js +37 -5
- package/dist/server/server/src/bootstrap.js.map +1 -1
- package/dist/server/server/src/bootstrap.mjs +37 -5
- package/dist/server/server/src/bootstrap.mjs.map +1 -1
- package/dist/server/server/src/config/admin-actions.js +48 -0
- package/dist/server/server/src/config/admin-actions.js.map +1 -1
- package/dist/server/server/src/config/admin-actions.mjs +48 -0
- package/dist/server/server/src/config/admin-actions.mjs.map +1 -1
- package/dist/server/server/src/content-types/Permission.js +10 -1
- package/dist/server/server/src/content-types/Permission.js.map +1 -1
- package/dist/server/server/src/content-types/Permission.mjs +10 -1
- package/dist/server/server/src/content-types/Permission.mjs.map +1 -1
- package/dist/server/server/src/content-types/User.js +8 -0
- package/dist/server/server/src/content-types/User.js.map +1 -1
- package/dist/server/server/src/content-types/User.mjs +8 -0
- package/dist/server/server/src/content-types/User.mjs.map +1 -1
- package/dist/server/server/src/content-types/api-token.js +27 -1
- package/dist/server/server/src/content-types/api-token.js.map +1 -1
- package/dist/server/server/src/content-types/api-token.mjs +27 -1
- package/dist/server/server/src/content-types/api-token.mjs.map +1 -1
- package/dist/server/server/src/controllers/admin-token.js +194 -0
- package/dist/server/server/src/controllers/admin-token.js.map +1 -0
- package/dist/server/server/src/controllers/admin-token.mjs +192 -0
- package/dist/server/server/src/controllers/admin-token.mjs.map +1 -0
- package/dist/server/server/src/controllers/api-token.js +48 -47
- package/dist/server/server/src/controllers/api-token.js.map +1 -1
- package/dist/server/server/src/controllers/api-token.mjs +48 -47
- package/dist/server/server/src/controllers/api-token.mjs.map +1 -1
- package/dist/server/server/src/controllers/index.js +2 -0
- package/dist/server/server/src/controllers/index.js.map +1 -1
- package/dist/server/server/src/controllers/index.mjs +2 -0
- package/dist/server/server/src/controllers/index.mjs.map +1 -1
- package/dist/server/server/src/domain/permission/index.js +2 -1
- package/dist/server/server/src/domain/permission/index.js.map +1 -1
- package/dist/server/server/src/domain/permission/index.mjs +2 -1
- package/dist/server/server/src/domain/permission/index.mjs.map +1 -1
- package/dist/server/server/src/policies/index.js +2 -0
- package/dist/server/server/src/policies/index.js.map +1 -1
- package/dist/server/server/src/policies/index.mjs +2 -0
- package/dist/server/server/src/policies/index.mjs.map +1 -1
- package/dist/server/server/src/policies/isAdminTokensEnabled.js +16 -0
- package/dist/server/server/src/policies/isAdminTokensEnabled.js.map +1 -0
- package/dist/server/server/src/policies/isAdminTokensEnabled.mjs +14 -0
- package/dist/server/server/src/policies/isAdminTokensEnabled.mjs.map +1 -0
- package/dist/server/server/src/register.js +4 -2
- package/dist/server/server/src/register.js.map +1 -1
- package/dist/server/server/src/register.mjs +4 -2
- package/dist/server/server/src/register.mjs.map +1 -1
- package/dist/server/server/src/routes/admin-tokens.js +140 -0
- package/dist/server/server/src/routes/admin-tokens.js.map +1 -0
- package/dist/server/server/src/routes/admin-tokens.mjs +138 -0
- package/dist/server/server/src/routes/admin-tokens.mjs.map +1 -0
- package/dist/server/server/src/routes/index.js +2 -0
- package/dist/server/server/src/routes/index.js.map +1 -1
- package/dist/server/server/src/routes/index.mjs +2 -0
- package/dist/server/server/src/routes/index.mjs.map +1 -1
- package/dist/server/server/src/services/api-token.js +805 -101
- package/dist/server/server/src/services/api-token.js.map +1 -1
- package/dist/server/server/src/services/api-token.mjs +800 -101
- package/dist/server/server/src/services/api-token.mjs.map +1 -1
- package/dist/server/server/src/services/constants.js +2 -0
- package/dist/server/server/src/services/constants.js.map +1 -1
- package/dist/server/server/src/services/constants.mjs +2 -0
- package/dist/server/server/src/services/constants.mjs.map +1 -1
- package/dist/server/server/src/services/homepage.js +1 -1
- package/dist/server/server/src/services/homepage.js.map +1 -1
- package/dist/server/server/src/services/homepage.mjs +1 -1
- package/dist/server/server/src/services/homepage.mjs.map +1 -1
- package/dist/server/server/src/services/index.js +2 -1
- package/dist/server/server/src/services/index.js.map +1 -1
- package/dist/server/server/src/services/index.mjs +3 -2
- package/dist/server/server/src/services/index.mjs.map +1 -1
- package/dist/server/server/src/services/permission/engine.js +6 -0
- package/dist/server/server/src/services/permission/engine.js.map +1 -1
- package/dist/server/server/src/services/permission/engine.mjs +6 -0
- package/dist/server/server/src/services/permission/engine.mjs.map +1 -1
- package/dist/server/server/src/services/permission/queries.js +11 -2
- package/dist/server/server/src/services/permission/queries.js.map +1 -1
- package/dist/server/server/src/services/permission/queries.mjs +12 -3
- package/dist/server/server/src/services/permission/queries.mjs.map +1 -1
- package/dist/server/server/src/services/role.js +3 -0
- package/dist/server/server/src/services/role.js.map +1 -1
- package/dist/server/server/src/services/role.mjs +3 -0
- package/dist/server/server/src/services/role.mjs.map +1 -1
- package/dist/server/server/src/strategies/admin-token.js +110 -0
- package/dist/server/server/src/strategies/admin-token.js.map +1 -0
- package/dist/server/server/src/strategies/admin-token.mjs +104 -0
- package/dist/server/server/src/strategies/admin-token.mjs.map +1 -0
- package/dist/server/server/src/strategies/api-token-utils.js +56 -0
- package/dist/server/server/src/strategies/api-token-utils.js.map +1 -0
- package/dist/server/server/src/strategies/api-token-utils.mjs +52 -0
- package/dist/server/server/src/strategies/api-token-utils.mjs.map +1 -0
- package/dist/server/server/src/strategies/content-api-token.js +104 -0
- package/dist/server/server/src/strategies/content-api-token.js.map +1 -0
- package/dist/server/server/src/strategies/content-api-token.mjs +98 -0
- package/dist/server/server/src/strategies/content-api-token.mjs.map +1 -0
- package/dist/server/server/src/validation/admin-tokens.js +28 -0
- package/dist/server/server/src/validation/admin-tokens.js.map +1 -0
- package/dist/server/server/src/validation/admin-tokens.mjs +25 -0
- package/dist/server/server/src/validation/admin-tokens.mjs.map +1 -0
- package/dist/server/server/src/validation/api-tokens.js +5 -2
- package/dist/server/server/src/validation/api-tokens.js.map +1 -1
- package/dist/server/server/src/validation/api-tokens.mjs +5 -2
- package/dist/server/server/src/validation/api-tokens.mjs.map +1 -1
- package/dist/server/server/src/validation/project-settings.js +15 -16
- package/dist/server/server/src/validation/project-settings.js.map +1 -1
- package/dist/server/server/src/validation/project-settings.mjs +4 -5
- package/dist/server/server/src/validation/project-settings.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/config/admin-actions.d.ts.map +1 -1
- package/dist/server/src/content-types/Permission.d.ts +9 -0
- package/dist/server/src/content-types/Permission.d.ts.map +1 -1
- package/dist/server/src/content-types/User.d.ts +8 -0
- package/dist/server/src/content-types/User.d.ts.map +1 -1
- package/dist/server/src/content-types/api-token.d.ts +23 -0
- package/dist/server/src/content-types/api-token.d.ts.map +1 -1
- package/dist/server/src/content-types/index.d.ts +40 -0
- package/dist/server/src/content-types/index.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-token.d.ts +12 -0
- package/dist/server/src/controllers/admin-token.d.ts.map +1 -0
- package/dist/server/src/controllers/api-token.d.ts +0 -1
- package/dist/server/src/controllers/api-token.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +9 -1
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/domain/permission/index.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +56 -2
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/policies/index.d.ts +5 -0
- package/dist/server/src/policies/index.d.ts.map +1 -1
- package/dist/server/src/policies/isAdminTokensEnabled.d.ts +7 -0
- package/dist/server/src/policies/isAdminTokensEnabled.d.ts.map +1 -0
- package/dist/server/src/register.d.ts.map +1 -1
- package/dist/server/src/routes/admin-tokens.d.ts +15 -0
- package/dist/server/src/routes/admin-tokens.d.ts.map +1 -0
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/server/src/services/api-token.d.ts +136 -12
- package/dist/server/src/services/api-token.d.ts.map +1 -1
- package/dist/server/src/services/constants.d.ts +13 -11
- package/dist/server/src/services/constants.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +2 -2
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/permission/engine.d.ts +5 -0
- package/dist/server/src/services/permission/engine.d.ts.map +1 -1
- package/dist/server/src/services/permission/queries.d.ts.map +1 -1
- package/dist/server/src/services/permission.d.ts +1 -0
- package/dist/server/src/services/permission.d.ts.map +1 -1
- package/dist/server/src/services/role.d.ts.map +1 -1
- package/dist/server/src/strategies/admin-token.d.ts +51 -0
- package/dist/server/src/strategies/admin-token.d.ts.map +1 -0
- package/dist/server/src/strategies/api-token-utils.d.ts +13 -0
- package/dist/server/src/strategies/api-token-utils.d.ts.map +1 -0
- package/dist/server/src/strategies/{api-token.d.ts → content-api-token.d.ts} +10 -11
- package/dist/server/src/strategies/content-api-token.d.ts.map +1 -0
- package/dist/server/src/strategies/index.d.ts +2 -1
- package/dist/server/src/strategies/index.d.ts.map +1 -1
- package/dist/server/src/validation/admin-tokens.d.ts +75 -0
- package/dist/server/src/validation/admin-tokens.d.ts.map +1 -0
- package/dist/server/src/validation/api-tokens.d.ts +4 -2
- package/dist/server/src/validation/api-tokens.d.ts.map +1 -1
- package/dist/server/src/validation/project-settings.d.ts +10 -10
- package/dist/server/src/validation/project-settings.d.ts.map +1 -1
- package/dist/shared/contracts/admin-token.d.ts +122 -0
- package/dist/shared/contracts/admin-token.d.ts.map +1 -0
- package/dist/shared/contracts/api-token.d.ts +6 -95
- package/dist/shared/contracts/api-token.d.ts.map +1 -1
- package/dist/shared/contracts/content-api-token.d.ts +97 -0
- package/dist/shared/contracts/content-api-token.d.ts.map +1 -0
- package/dist/shared/contracts/shared.d.ts +1 -0
- package/dist/shared/contracts/shared.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/components/FormApiTokenContainer.js.map +0 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/components/FormApiTokenContainer.mjs.map +0 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/constants.js +0 -37
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/constants.js.map +0 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/constants.mjs +0 -16
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/constants.mjs.map +0 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/utils/getDateOfExpiration.js.map +0 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/utils/getDateOfExpiration.mjs.map +0 -1
- package/dist/admin/src/pages/Settings/pages/ApiTokens/EditView/components/FormApiTokenContainer.d.ts +0 -20
- package/dist/admin/src/pages/Settings/pages/ApiTokens/EditView/constants.d.ts +0 -17
- package/dist/server/server/src/strategies/api-token.js +0 -144
- package/dist/server/server/src/strategies/api-token.js.map +0 -1
- package/dist/server/server/src/strategies/api-token.mjs +0 -138
- package/dist/server/server/src/strategies/api-token.mjs.map +0 -1
- package/dist/server/src/strategies/api-token.d.ts.map +0 -1
- /package/dist/admin/src/pages/Settings/{pages/ApiTokens/EditView → components/Tokens}/utils/getDateOfExpiration.d.ts +0 -0
|
@@ -1,12 +1,58 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { pick, uniq, differenceWith, prop, omit, map, difference, isEqual, isEmpty, isArray, isNumber, isNil } from 'lodash/fp';
|
|
3
3
|
import { errors } from '@strapi/utils';
|
|
4
4
|
import constants from './constants.mjs';
|
|
5
5
|
import { getService } from '../utils/index.mjs';
|
|
6
|
+
import permissionDomain from '../domain/permission/index.mjs';
|
|
7
|
+
import { validatePermissionsExist } from '../validation/permission.mjs';
|
|
6
8
|
|
|
9
|
+
const { SUPER_ADMIN_CODE } = constants;
|
|
7
10
|
const { ValidationError, NotFoundError } = errors;
|
|
11
|
+
const assertOwnerMatchesCallingUser = async (adminUserOwner, callingUser)=>{
|
|
12
|
+
if (callingUser === undefined || callingUser === null) {
|
|
13
|
+
throw new ValidationError('adminUserOwner requires an authenticated admin user');
|
|
14
|
+
}
|
|
15
|
+
const ownerId = String(adminUserOwner);
|
|
16
|
+
const callingUserId = String(callingUser.id);
|
|
17
|
+
if (ownerId !== callingUserId) {
|
|
18
|
+
throw new ValidationError('adminUserOwner must match the authenticated admin user');
|
|
19
|
+
}
|
|
20
|
+
const existingUser = await strapi.db.query('admin::user').findOne({
|
|
21
|
+
select: [
|
|
22
|
+
'id'
|
|
23
|
+
],
|
|
24
|
+
where: {
|
|
25
|
+
id: callingUser.id
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
if (existingUser === null || existingUser === undefined) {
|
|
29
|
+
throw new ValidationError('adminUserOwner must reference an existing admin user');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const isSuperAdmin = (user)=>user?.roles?.some((r)=>r.code === SUPER_ADMIN_CODE) === true;
|
|
33
|
+
const getOwnerId = (token)=>{
|
|
34
|
+
const owner = token.adminUserOwner;
|
|
35
|
+
return String(typeof owner === 'object' ? owner.id : owner);
|
|
36
|
+
};
|
|
37
|
+
const toAdminTokenOwner = (owner)=>{
|
|
38
|
+
if (owner === null || owner === undefined) {
|
|
39
|
+
throw new Error('adminUserOwner is required');
|
|
40
|
+
}
|
|
41
|
+
// Bare id
|
|
42
|
+
if (typeof owner === 'number' || typeof owner === 'string') {
|
|
43
|
+
return owner;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
id: owner.id,
|
|
47
|
+
firstname: owner.firstname,
|
|
48
|
+
lastname: owner.lastname,
|
|
49
|
+
username: owner.username,
|
|
50
|
+
email: owner.email
|
|
51
|
+
};
|
|
52
|
+
};
|
|
8
53
|
const SELECT_FIELDS = [
|
|
9
54
|
'id',
|
|
55
|
+
'kind',
|
|
10
56
|
'name',
|
|
11
57
|
'description',
|
|
12
58
|
'lastUsedAt',
|
|
@@ -17,7 +63,9 @@ const SELECT_FIELDS = [
|
|
|
17
63
|
'updatedAt'
|
|
18
64
|
];
|
|
19
65
|
const POPULATE_FIELDS = [
|
|
20
|
-
'permissions'
|
|
66
|
+
'permissions',
|
|
67
|
+
'adminPermissions',
|
|
68
|
+
'adminUserOwner'
|
|
21
69
|
];
|
|
22
70
|
// TODO: we need to ensure the permissions are actually valid registered permissions!
|
|
23
71
|
/**
|
|
@@ -60,42 +108,381 @@ const POPULATE_FIELDS = [
|
|
|
60
108
|
}
|
|
61
109
|
};
|
|
62
110
|
/**
|
|
63
|
-
*
|
|
64
|
-
*/ const
|
|
65
|
-
|
|
66
|
-
|
|
111
|
+
* Assert that a legacy-kind token body does not carry admin-only fields
|
|
112
|
+
*/ const assertLegacyKindFields = (attributes)=>{
|
|
113
|
+
const raw = attributes;
|
|
114
|
+
if (raw.adminPermissions !== undefined && raw.adminPermissions !== null) {
|
|
115
|
+
throw new ValidationError('Legacy tokens cannot carry admin permissions');
|
|
116
|
+
}
|
|
117
|
+
if (raw.adminUserOwner !== undefined && raw.adminUserOwner !== null) {
|
|
118
|
+
throw new ValidationError('Legacy tokens cannot have an admin user owner');
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Assert that an admin-kind token body does not carry legacy-only fields
|
|
123
|
+
*/ const assertAdminKindFields = (attributes)=>{
|
|
124
|
+
const raw = attributes;
|
|
125
|
+
if (raw.type !== undefined && raw.type !== null) {
|
|
126
|
+
throw new ValidationError('Admin tokens cannot carry a legacy type (custom/read-only/full-access)');
|
|
127
|
+
}
|
|
128
|
+
if (raw.permissions !== undefined && raw.permissions !== null) {
|
|
129
|
+
throw new ValidationError('Admin tokens cannot carry legacy content-API permissions');
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Assert that admin permissions are valid
|
|
134
|
+
*/ const assertAdminPermissionsValidity = async (adminPermissions)=>{
|
|
135
|
+
if (!adminPermissions || adminPermissions.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Validate that all actions exist in the admin action provider
|
|
139
|
+
const validActions = getService('permission').actionProvider.keys();
|
|
140
|
+
for (const perm of adminPermissions){
|
|
141
|
+
if (!validActions.includes(perm.action)) {
|
|
142
|
+
throw new ValidationError(`Unknown admin action: ${perm.action}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Use existing permission validation
|
|
146
|
+
await validatePermissionsExist(adminPermissions);
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Enforce that every requested admin permission stays within the calling
|
|
150
|
+
* user's own permission ceiling, then return the clamped permissions.
|
|
151
|
+
*
|
|
152
|
+
* Super-admins bypass this (they hold every permission).
|
|
153
|
+
* When admin permissions are requested, an authenticated user is required (no bypass when user is missing).
|
|
154
|
+
*
|
|
155
|
+
* For each requested permission:
|
|
156
|
+
* - action + subject must match at least one user permission
|
|
157
|
+
* - properties.fields must be ⊆ user's properties.fields
|
|
158
|
+
* (if the user's permission defines no fields, all fields are allowed)
|
|
159
|
+
* - conditions are inherited from the user's matching permission(s);
|
|
160
|
+
* the caller cannot configure conditions on their own tokens
|
|
161
|
+
*
|
|
162
|
+
* Returns the permissions with conditions enforced from the user's role.
|
|
163
|
+
* Throws ValidationError if any permission exceeds the user's ceiling.
|
|
164
|
+
*
|
|
165
|
+
* Guaranteed postcondition: all returned permissions have conditions filtered to
|
|
166
|
+
* registered conditions only, regardless of the user type.
|
|
167
|
+
*/ const enforceAdminPermissionsCeiling = async (user, requestedPermissions)=>{
|
|
168
|
+
if (!requestedPermissions || requestedPermissions.length === 0) {
|
|
169
|
+
return requestedPermissions || [];
|
|
170
|
+
}
|
|
171
|
+
if (user === undefined || user === null) {
|
|
172
|
+
throw new ValidationError('Admin permission ceiling cannot be enforced without an authenticated user');
|
|
173
|
+
}
|
|
174
|
+
if (isSuperAdmin(user)) {
|
|
175
|
+
// Sanitize conditions even for super-admins so this function is a complete boundary.
|
|
176
|
+
// createApiTokenAdminPermissions also sanitizes, but relying on that downstream
|
|
177
|
+
// is fragile — any future call path that skips it would store invalid conditions.
|
|
178
|
+
const { conditionProvider } = getService('permission');
|
|
179
|
+
const sanitize = permissionDomain.sanitizeConditions(conditionProvider);
|
|
180
|
+
return requestedPermissions.map((perm)=>{
|
|
181
|
+
const sanitized = sanitize({
|
|
182
|
+
...perm,
|
|
183
|
+
actionParameters: {}
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
...perm,
|
|
187
|
+
conditions: sanitized.conditions
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
const userPermissions = await getService('permission').findUserPermissions(user);
|
|
192
|
+
const exceeding = [];
|
|
193
|
+
const clamped = requestedPermissions.map((requested)=>{
|
|
194
|
+
const requestedSubject = requested.subject || null;
|
|
195
|
+
// Find all user permissions matching action + subject
|
|
196
|
+
const matchingUserPerms = userPermissions.filter((userPerm)=>{
|
|
197
|
+
if (userPerm.action !== requested.action) return false;
|
|
198
|
+
const userSubject = userPerm.subject || null;
|
|
199
|
+
return requestedSubject === userSubject;
|
|
200
|
+
});
|
|
201
|
+
const label = requestedSubject !== null ? `${requested.action} on ${requestedSubject}` : requested.action;
|
|
202
|
+
if (matchingUserPerms.length === 0) {
|
|
203
|
+
exceeding.push(label);
|
|
204
|
+
return requested;
|
|
205
|
+
}
|
|
206
|
+
// --- Field-level ceiling ---
|
|
207
|
+
// If any matching user perm has no fields defined → all fields are allowed.
|
|
208
|
+
// Otherwise, effective user fields = union of all matching perms' fields.
|
|
209
|
+
const anyUserPermHasAllFields = matchingUserPerms.some((p)=>!p.properties?.fields || p.properties.fields.length === 0);
|
|
210
|
+
const requestedFields = requested.properties?.fields;
|
|
211
|
+
if (!anyUserPermHasAllFields) {
|
|
212
|
+
const effectiveUserFields = uniq(matchingUserPerms.flatMap((p)=>p.properties?.fields || []));
|
|
213
|
+
// When the owner is field-restricted, omitting fields would widen access to all fields.
|
|
214
|
+
// Force explicit field selection so token scope can't exceed the owner's ceiling.
|
|
215
|
+
if (requestedFields === undefined || requestedFields === null) {
|
|
216
|
+
exceeding.push(`${label} (fields are required due to owner field restrictions)`);
|
|
217
|
+
return requested;
|
|
218
|
+
}
|
|
219
|
+
if (requestedFields.length > 0) {
|
|
220
|
+
const exceedingFields = requestedFields.filter((f)=>!effectiveUserFields.includes(f));
|
|
221
|
+
if (exceedingFields.length > 0) {
|
|
222
|
+
exceeding.push(`${label} (fields: ${exceedingFields.join(', ')})`);
|
|
223
|
+
return requested;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// --- Condition-level ceiling ---
|
|
228
|
+
// Conditions are always inherited from the user's matching permission(s).
|
|
229
|
+
// If any matching user perm is unconditional → token gets no conditions.
|
|
230
|
+
// Otherwise → union of conditions across matching perms.
|
|
231
|
+
const anyUserPermIsUnconditional = matchingUserPerms.some((p)=>!p.conditions || p.conditions.length === 0);
|
|
232
|
+
const enforcedConditions = anyUserPermIsUnconditional ? [] : uniq(matchingUserPerms.flatMap((p)=>p.conditions || []));
|
|
233
|
+
return {
|
|
234
|
+
...requested,
|
|
235
|
+
conditions: enforcedConditions
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
if (exceeding.length > 0) {
|
|
239
|
+
throw new ValidationError(`Cannot assign admin permissions that exceed your own. ` + `Exceeding: ${exceeding.join(', ')}`);
|
|
240
|
+
}
|
|
241
|
+
return clamped;
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Create admin permissions for an API token
|
|
245
|
+
*/ const createApiTokenAdminPermissions = async (tokenId, permissions)=>{
|
|
246
|
+
const { conditionProvider } = getService('permission');
|
|
247
|
+
const sanitizeConditions = permissionDomain.sanitizeConditions(conditionProvider);
|
|
248
|
+
const permissionsWithToken = permissions.map((perm)=>{
|
|
249
|
+
const permAsPermission = {
|
|
250
|
+
...perm,
|
|
251
|
+
actionParameters: {}
|
|
252
|
+
};
|
|
253
|
+
const sanitized = sanitizeConditions(permAsPermission);
|
|
254
|
+
return permissionDomain.create({
|
|
255
|
+
...sanitized,
|
|
256
|
+
apiToken: tokenId,
|
|
257
|
+
role: null
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
const createdPermissions = await getService('permission').createMany(permissionsWithToken);
|
|
261
|
+
return createdPermissions;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Fields to compare when checking if two permissions are equal
|
|
265
|
+
*/ const COMPARABLE_FIELDS = [
|
|
266
|
+
'conditions',
|
|
267
|
+
'properties',
|
|
268
|
+
'subject',
|
|
269
|
+
'action',
|
|
270
|
+
'actionParameters'
|
|
271
|
+
];
|
|
272
|
+
const pickComparableFields = pick(COMPARABLE_FIELDS);
|
|
273
|
+
/**
|
|
274
|
+
* Helper to clean JSON (remove undefined values)
|
|
275
|
+
*/ const jsonClean = (data)=>JSON.parse(JSON.stringify(data));
|
|
276
|
+
/**
|
|
277
|
+
* Compare two permissions for equality
|
|
278
|
+
*/ const arePermissionsEqual = (p1, p2)=>{
|
|
279
|
+
if (p1.action === p2.action) {
|
|
280
|
+
return isEqual(jsonClean(pickComparableFields(p1)), jsonClean(pickComparableFields(p2)));
|
|
67
281
|
}
|
|
282
|
+
return false;
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Assign admin permissions to an API token (similar to role permission assignment).
|
|
286
|
+
* ceilingUser is the user whose permissions act as the ceiling — always the token owner,
|
|
287
|
+
* regardless of who is making the request.
|
|
288
|
+
*/ const assignAdminPermissionsToToken = async (tokenId, permissions, ceilingUser)=>{
|
|
289
|
+
await validatePermissionsExist(permissions);
|
|
290
|
+
const clampedPermissions = await enforceAdminPermissionsCeiling(ceilingUser, permissions);
|
|
291
|
+
const permissionsWithToken = clampedPermissions.map((perm)=>permissionDomain.create({
|
|
292
|
+
...perm,
|
|
293
|
+
apiToken: tokenId,
|
|
294
|
+
role: null
|
|
295
|
+
}));
|
|
296
|
+
const existingPermissions = await getService('permission').findMany({
|
|
297
|
+
where: {
|
|
298
|
+
apiToken: {
|
|
299
|
+
id: tokenId
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
const permissionsToAdd = differenceWith(arePermissionsEqual, permissionsWithToken, existingPermissions);
|
|
304
|
+
const permissionsToDelete = differenceWith(arePermissionsEqual, existingPermissions, permissionsWithToken);
|
|
305
|
+
if (permissionsToDelete.length > 0) {
|
|
306
|
+
await getService('permission').deleteByIds(permissionsToDelete.map(prop('id')));
|
|
307
|
+
}
|
|
308
|
+
if (permissionsToAdd.length > 0) {
|
|
309
|
+
await createApiTokenAdminPermissions(tokenId, permissionsToAdd);
|
|
310
|
+
}
|
|
311
|
+
// Return all current permissions
|
|
312
|
+
const allCurrentPermissions = await getService('permission').findMany({
|
|
313
|
+
where: {
|
|
314
|
+
apiToken: {
|
|
315
|
+
id: tokenId
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return allCurrentPermissions;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* Reconcile a token's admin permissions against the owner's current effective ceiling.
|
|
323
|
+
*
|
|
324
|
+
* Pure / sync — no DB calls. Returns two buckets:
|
|
325
|
+
* toDelete – permissions that are no longer within the user's scope (action/subject missing
|
|
326
|
+
* or requested fields exceed the allowed set)
|
|
327
|
+
* toUpdate – permissions that are still in scope but whose conditions must be re-clamped
|
|
328
|
+
* to the current union of the matching user permissions' conditions
|
|
329
|
+
*/ const reconcileTokenPermissionsToUserCeiling = (userPermissions, tokenPermissions)=>{
|
|
330
|
+
const toDelete = [];
|
|
331
|
+
const toUpdate = [];
|
|
332
|
+
tokenPermissions.forEach((tokenPerm)=>{
|
|
333
|
+
const tokenSubject = tokenPerm.subject || null;
|
|
334
|
+
const matchingUserPerms = userPermissions.filter((userPerm)=>userPerm.action === tokenPerm.action && (userPerm.subject || null) === tokenSubject);
|
|
335
|
+
if (matchingUserPerms.length === 0) {
|
|
336
|
+
toDelete.push(tokenPerm);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Field-level ceiling check (mirrors enforceAdminPermissionsCeiling)
|
|
340
|
+
const anyUserPermHasAllFields = matchingUserPerms.some((p)=>!p.properties?.fields || p.properties.fields.length === 0);
|
|
341
|
+
const tokenFields = tokenPerm.properties?.fields;
|
|
342
|
+
const fieldCeilingExceeded = !anyUserPermHasAllFields && tokenFields !== undefined && tokenFields !== null && tokenFields.length > 0 && (()=>{
|
|
343
|
+
const effectiveUserFields = uniq(matchingUserPerms.flatMap((p)=>p.properties?.fields || []));
|
|
344
|
+
return tokenFields.some((f)=>!effectiveUserFields.includes(f));
|
|
345
|
+
})();
|
|
346
|
+
if (fieldCeilingExceeded) {
|
|
347
|
+
toDelete.push(tokenPerm);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// Condition: force conditions to be the ones of the user permission(s)
|
|
351
|
+
const anyUserPermIsUnconditional = matchingUserPerms.some((p)=>!p.conditions || p.conditions.length === 0);
|
|
352
|
+
const enforcedConditions = anyUserPermIsUnconditional ? [] : uniq(matchingUserPerms.flatMap((p)=>p.conditions || []));
|
|
353
|
+
const currentConditions = tokenPerm.conditions || [];
|
|
354
|
+
const conditionsChanged = enforcedConditions.length !== currentConditions.length || enforcedConditions.some((c)=>!currentConditions.includes(c));
|
|
355
|
+
if (conditionsChanged) {
|
|
356
|
+
toUpdate.push({
|
|
357
|
+
id: tokenPerm.id,
|
|
358
|
+
conditions: enforcedConditions
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
68
362
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
363
|
+
toDelete,
|
|
364
|
+
toUpdate
|
|
71
365
|
};
|
|
72
366
|
};
|
|
73
367
|
/**
|
|
74
|
-
*
|
|
75
|
-
|
|
368
|
+
* Re-sync all admin token permissions for a given user against their current effective ceiling.
|
|
369
|
+
*
|
|
370
|
+
* Skips super-admins (no ceiling). For each admin token owned by the user:
|
|
371
|
+
* - Deletes permissions that are no longer within the user's scope
|
|
372
|
+
* - Updates conditions on permissions whose conditions have drifted from the role's current set
|
|
373
|
+
*/ const syncApiTokenPermissionsForUser = async (userId)=>{
|
|
374
|
+
const user = await strapi.db.query('admin::user').findOne({
|
|
375
|
+
where: {
|
|
376
|
+
id: userId
|
|
377
|
+
},
|
|
378
|
+
populate: [
|
|
379
|
+
'roles'
|
|
380
|
+
]
|
|
381
|
+
});
|
|
382
|
+
if (user === null || user === undefined) return;
|
|
383
|
+
if (isSuperAdmin(user)) return;
|
|
384
|
+
const userEffectivePermissions = await getService('permission').findUserPermissions(user);
|
|
385
|
+
const tokens = await strapi.db.query('admin::api-token').findMany({
|
|
386
|
+
where: {
|
|
387
|
+
kind: 'admin',
|
|
388
|
+
adminUserOwner: {
|
|
389
|
+
id: userId
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
populate: [
|
|
393
|
+
'adminPermissions'
|
|
394
|
+
]
|
|
395
|
+
});
|
|
396
|
+
const tokensWithPermissions = tokens.filter((token)=>Array.isArray(token.adminPermissions) && token.adminPermissions.length > 0);
|
|
397
|
+
for (const token of tokensWithPermissions){
|
|
398
|
+
const tokenPermissions = token.adminPermissions;
|
|
399
|
+
const { toDelete, toUpdate } = reconcileTokenPermissionsToUserCeiling(userEffectivePermissions, tokenPermissions);
|
|
400
|
+
if (toDelete.length > 0) {
|
|
401
|
+
await getService('permission').deleteByIds(toDelete.map((p)=>p.id));
|
|
402
|
+
}
|
|
403
|
+
for (const { id, conditions } of toUpdate){
|
|
404
|
+
await strapi.db.query('admin::permission').update({
|
|
405
|
+
where: {
|
|
406
|
+
id
|
|
407
|
+
},
|
|
408
|
+
data: {
|
|
409
|
+
conditions
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Re-sync admin token permissions for all admin users who hold a given role.
|
|
417
|
+
* Called after role permissions are updated.
|
|
418
|
+
*/ const syncApiTokenPermissionsForRole = async (roleId)=>{
|
|
419
|
+
const users = await strapi.db.query('admin::user').findMany({
|
|
420
|
+
where: {
|
|
421
|
+
roles: {
|
|
422
|
+
id: roleId
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
populate: [
|
|
426
|
+
'roles'
|
|
427
|
+
]
|
|
428
|
+
});
|
|
429
|
+
await Promise.allSettled(users.map((user)=>syncApiTokenPermissionsForUser(user.id)));
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Flatten a token's database permissions objects to an array of strings
|
|
433
|
+
*/ const flattenTokenPermissions = (permissions)=>{
|
|
434
|
+
return isArray(permissions) ? map('action', permissions) : [];
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Get a token.
|
|
438
|
+
* By default the plaintext accessKey is NOT included.
|
|
439
|
+
* Pass { includeDecryptedKey: true } to decrypt and return it (owner-only paths).
|
|
440
|
+
*/ const getBy = async (whereParams = {}, options = {})=>{
|
|
76
441
|
if (Object.keys(whereParams).length === 0) {
|
|
77
442
|
return null;
|
|
78
443
|
}
|
|
444
|
+
const { includeDecryptedKey = false } = options;
|
|
445
|
+
const selectFields = includeDecryptedKey ? [
|
|
446
|
+
...SELECT_FIELDS,
|
|
447
|
+
'encryptedKey'
|
|
448
|
+
] : SELECT_FIELDS;
|
|
79
449
|
const token = await strapi.db.query('admin::api-token').findOne({
|
|
80
|
-
select:
|
|
81
|
-
...SELECT_FIELDS,
|
|
82
|
-
'encryptedKey'
|
|
83
|
-
],
|
|
450
|
+
select: selectFields,
|
|
84
451
|
populate: POPULATE_FIELDS,
|
|
85
452
|
where: whereParams
|
|
86
453
|
});
|
|
87
454
|
if (!token) {
|
|
88
455
|
return token;
|
|
89
456
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
457
|
+
// Tokens created before kind introduction case: force kind to be content-api
|
|
458
|
+
const computedKind = token.kind ?? 'content-api';
|
|
459
|
+
const result = omit([
|
|
460
|
+
'accessKey',
|
|
461
|
+
'encryptedKey',
|
|
462
|
+
'type',
|
|
463
|
+
'permissions',
|
|
464
|
+
'adminPermissions',
|
|
465
|
+
'adminUserOwner'
|
|
466
|
+
], token);
|
|
467
|
+
if (computedKind === 'content-api') {
|
|
468
|
+
Object.assign(result, {
|
|
469
|
+
kind: 'content-api',
|
|
470
|
+
type: token.type,
|
|
471
|
+
permissions: flattenTokenPermissions(token.permissions)
|
|
472
|
+
});
|
|
473
|
+
} else if (computedKind === 'admin') {
|
|
474
|
+
Object.assign(result, {
|
|
475
|
+
kind: 'admin',
|
|
476
|
+
adminPermissions: token.adminPermissions,
|
|
477
|
+
adminUserOwner: toAdminTokenOwner(token.adminUserOwner)
|
|
478
|
+
});
|
|
93
479
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
480
|
+
if (includeDecryptedKey && token.encryptedKey) {
|
|
481
|
+
Object.assign(result, {
|
|
482
|
+
accessKey: getService('encryption').decrypt(token.encryptedKey)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
99
486
|
};
|
|
100
487
|
/**
|
|
101
488
|
* Check if token exists
|
|
@@ -117,54 +504,116 @@ const getExpirationFields = (lifespan)=>{
|
|
|
117
504
|
throw new ValidationError('lifespan must be a positive number or null');
|
|
118
505
|
}
|
|
119
506
|
return {
|
|
120
|
-
lifespan: lifespan
|
|
507
|
+
lifespan: lifespan ?? null,
|
|
121
508
|
expiresAt: lifespan ? Date.now() + lifespan : null
|
|
122
509
|
};
|
|
123
510
|
};
|
|
124
511
|
/**
|
|
125
512
|
* Create a token and its permissions
|
|
126
|
-
*/ const create = async (attributes)=>{
|
|
513
|
+
*/ const create = async (attributes, callingUser)=>{
|
|
127
514
|
const encryptionService = getService('encryption');
|
|
128
515
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
129
516
|
const encryptedKey = encryptionService.encrypt(accessKey);
|
|
130
|
-
assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);
|
|
131
517
|
assertValidLifespan(attributes.lifespan);
|
|
132
|
-
|
|
518
|
+
if (attributes.kind === 'content-api') {
|
|
519
|
+
const castedContentApiApiTokenBody = attributes;
|
|
520
|
+
assertLegacyKindFields(castedContentApiApiTokenBody);
|
|
521
|
+
assertCustomTokenPermissionsValidity(castedContentApiApiTokenBody.type, castedContentApiApiTokenBody.permissions);
|
|
522
|
+
// content api tokens have no owner
|
|
523
|
+
const apiToken = await strapi.db.query('admin::api-token').create({
|
|
524
|
+
select: SELECT_FIELDS,
|
|
525
|
+
populate: POPULATE_FIELDS,
|
|
526
|
+
data: {
|
|
527
|
+
...omit([
|
|
528
|
+
'permissions',
|
|
529
|
+
'adminPermissions',
|
|
530
|
+
'adminUserOwner'
|
|
531
|
+
], attributes),
|
|
532
|
+
accessKey: hash(accessKey),
|
|
533
|
+
encryptedKey,
|
|
534
|
+
adminUserOwner: null,
|
|
535
|
+
...getExpirationFields(castedContentApiApiTokenBody.lifespan ?? null)
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
const result = {
|
|
539
|
+
...apiToken,
|
|
540
|
+
accessKey
|
|
541
|
+
};
|
|
542
|
+
// If this is a custom type token, create the related content-API permissions
|
|
543
|
+
if (castedContentApiApiTokenBody.type === constants.API_TOKEN_TYPE.CUSTOM) {
|
|
544
|
+
// TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries
|
|
545
|
+
await Promise.all(uniq(castedContentApiApiTokenBody.permissions).map((action)=>strapi.db.query('admin::api-token-permission').create({
|
|
546
|
+
data: {
|
|
547
|
+
action,
|
|
548
|
+
token: apiToken
|
|
549
|
+
}
|
|
550
|
+
})));
|
|
551
|
+
const currentPermissions = await strapi.db.query('admin::api-token').load(apiToken, 'permissions');
|
|
552
|
+
if (currentPermissions) {
|
|
553
|
+
Object.assign(result, {
|
|
554
|
+
permissions: flattenTokenPermissions(currentPermissions)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// Casted to any to avoid complex type duplication
|
|
559
|
+
return omit([
|
|
560
|
+
'adminPermissions',
|
|
561
|
+
'adminUserOwner'
|
|
562
|
+
], result);
|
|
563
|
+
}
|
|
564
|
+
// kind === 'admin'
|
|
565
|
+
assertAdminKindFields(attributes);
|
|
566
|
+
const castedAdminTokenBody = attributes;
|
|
567
|
+
await assertAdminPermissionsValidity(castedAdminTokenBody.adminPermissions);
|
|
568
|
+
const clampedAdminPermissions = await enforceAdminPermissionsCeiling(callingUser, castedAdminTokenBody.adminPermissions);
|
|
569
|
+
// Owner: when explicitly provided, it must match the caller.
|
|
570
|
+
// When omitted, always defaults to the calling user (including super admins).
|
|
571
|
+
let ownerId;
|
|
572
|
+
if (castedAdminTokenBody.adminUserOwner !== undefined && castedAdminTokenBody.adminUserOwner !== null) {
|
|
573
|
+
await assertOwnerMatchesCallingUser(castedAdminTokenBody.adminUserOwner, callingUser);
|
|
574
|
+
ownerId = castedAdminTokenBody.adminUserOwner;
|
|
575
|
+
} else {
|
|
576
|
+
if (callingUser === undefined || callingUser === null) {
|
|
577
|
+
throw new ValidationError('Creating an admin token requires an authenticated admin user');
|
|
578
|
+
}
|
|
579
|
+
ownerId = callingUser.id;
|
|
580
|
+
}
|
|
133
581
|
const apiToken = await strapi.db.query('admin::api-token').create({
|
|
134
582
|
select: SELECT_FIELDS,
|
|
135
583
|
populate: POPULATE_FIELDS,
|
|
136
584
|
data: {
|
|
137
|
-
...omit(
|
|
585
|
+
...omit([
|
|
586
|
+
'permissions',
|
|
587
|
+
'adminPermissions',
|
|
588
|
+
'adminUserOwner'
|
|
589
|
+
], attributes),
|
|
138
590
|
accessKey: hash(accessKey),
|
|
139
591
|
encryptedKey,
|
|
140
|
-
|
|
592
|
+
adminUserOwner: ownerId,
|
|
593
|
+
...getExpirationFields(castedAdminTokenBody.lifespan ?? null)
|
|
141
594
|
}
|
|
142
595
|
});
|
|
143
596
|
const result = {
|
|
144
597
|
...apiToken,
|
|
145
598
|
accessKey
|
|
146
599
|
};
|
|
147
|
-
//
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// data: attributes.permissions.map(action => ({ action, token: apiToken })),
|
|
153
|
-
// });
|
|
154
|
-
await Promise.all(uniq(attributes.permissions).map((action)=>strapi.db.query('admin::api-token-permission').create({
|
|
155
|
-
data: {
|
|
156
|
-
action,
|
|
157
|
-
token: apiToken
|
|
158
|
-
}
|
|
159
|
-
})));
|
|
160
|
-
const currentPermissions = await strapi.db.query('admin::api-token').load(apiToken, 'permissions');
|
|
161
|
-
if (currentPermissions) {
|
|
600
|
+
// Handle admin permissions (using ceiling-clamped permissions with inherited conditions)
|
|
601
|
+
if (clampedAdminPermissions.length > 0) {
|
|
602
|
+
await createApiTokenAdminPermissions(apiToken.id, clampedAdminPermissions);
|
|
603
|
+
const currentAdminPermissions = await strapi.db.query('admin::api-token').load(apiToken, 'adminPermissions');
|
|
604
|
+
if (currentAdminPermissions) {
|
|
162
605
|
Object.assign(result, {
|
|
163
|
-
|
|
606
|
+
adminPermissions: currentAdminPermissions
|
|
164
607
|
});
|
|
165
608
|
}
|
|
166
609
|
}
|
|
167
|
-
|
|
610
|
+
// Casted to any to avoid complex type duplication
|
|
611
|
+
return {
|
|
612
|
+
...omit([
|
|
613
|
+
'permissions'
|
|
614
|
+
], result),
|
|
615
|
+
adminUserOwner: toAdminTokenOwner(result.adminUserOwner)
|
|
616
|
+
};
|
|
168
617
|
};
|
|
169
618
|
const regenerate = async (id)=>{
|
|
170
619
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
@@ -206,50 +655,114 @@ For security reasons, prefer storing the secret in an environment variable and r
|
|
|
206
655
|
}
|
|
207
656
|
};
|
|
208
657
|
/**
|
|
209
|
-
* Return a list of
|
|
210
|
-
|
|
658
|
+
* Return a list of tokens visible to the calling user.
|
|
659
|
+
* Super-admins see all tokens; regular admins see only ownerless tokens and their own.
|
|
660
|
+
*/ const list = async (callingUser, { filter } = {})=>{
|
|
661
|
+
const ownershipWhere = isSuperAdmin(callingUser) ? {} : {
|
|
662
|
+
$or: [
|
|
663
|
+
{
|
|
664
|
+
adminUserOwner: null
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
adminUserOwner: {
|
|
668
|
+
id: callingUser.id
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
]
|
|
672
|
+
};
|
|
673
|
+
// Tokens without a persisted kind are content-api tokens (pre-migration rows).
|
|
674
|
+
let kindWhere = {};
|
|
675
|
+
if (filter?.kind === 'content-api') {
|
|
676
|
+
kindWhere = {
|
|
677
|
+
$or: [
|
|
678
|
+
{
|
|
679
|
+
kind: 'content-api'
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
kind: {
|
|
683
|
+
$null: true
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
]
|
|
687
|
+
};
|
|
688
|
+
} else if (filter?.kind !== undefined) {
|
|
689
|
+
kindWhere = {
|
|
690
|
+
kind: filter.kind
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
const where = {
|
|
694
|
+
...ownershipWhere,
|
|
695
|
+
...kindWhere
|
|
696
|
+
};
|
|
211
697
|
const tokens = await strapi.db.query('admin::api-token').findMany({
|
|
212
698
|
select: SELECT_FIELDS,
|
|
213
699
|
populate: POPULATE_FIELDS,
|
|
214
700
|
orderBy: {
|
|
215
701
|
name: 'ASC'
|
|
216
|
-
}
|
|
702
|
+
},
|
|
703
|
+
where
|
|
217
704
|
});
|
|
218
705
|
if (!tokens) {
|
|
219
706
|
return tokens;
|
|
220
707
|
}
|
|
221
|
-
return tokens.map((token)=>
|
|
708
|
+
return tokens.map((token)=>token.kind === null || token.kind === 'content-api' ? omit([
|
|
709
|
+
'adminPermissions',
|
|
710
|
+
'adminUserOwner'
|
|
711
|
+
], {
|
|
712
|
+
...token,
|
|
713
|
+
// Tokens created before kind introduction case: force kind to be content-api
|
|
714
|
+
kind: 'content-api',
|
|
715
|
+
permissions: flattenTokenPermissions(token.permissions)
|
|
716
|
+
}) : {
|
|
717
|
+
...omit([
|
|
718
|
+
'permissions'
|
|
719
|
+
], token),
|
|
720
|
+
adminUserOwner: toAdminTokenOwner(token.adminUserOwner)
|
|
721
|
+
});
|
|
222
722
|
};
|
|
223
723
|
/**
|
|
224
724
|
* Revoke (delete) a token
|
|
225
725
|
*/ const revoke = async (id)=>{
|
|
226
|
-
|
|
726
|
+
const token = await strapi.db.query('admin::api-token').findOne({
|
|
727
|
+
where: {
|
|
728
|
+
id
|
|
729
|
+
},
|
|
730
|
+
select: [
|
|
731
|
+
'id'
|
|
732
|
+
],
|
|
733
|
+
populate: [
|
|
734
|
+
'adminPermissions'
|
|
735
|
+
]
|
|
736
|
+
});
|
|
737
|
+
if (token !== null && token !== undefined) {
|
|
738
|
+
const permissionIds = (token.adminPermissions ?? []).map((p)=>p.id).filter((permId)=>permId !== null && permId !== undefined);
|
|
739
|
+
if (permissionIds.length > 0) {
|
|
740
|
+
await getService('permission').deleteByIds(permissionIds);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
const deletedToken = await strapi.db.query('admin::api-token').delete({
|
|
227
744
|
select: SELECT_FIELDS,
|
|
228
745
|
populate: POPULATE_FIELDS,
|
|
229
746
|
where: {
|
|
230
747
|
id
|
|
231
748
|
}
|
|
232
749
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
/**
|
|
242
|
-
* Retrieve a token by name
|
|
243
|
-
*/ const getByName = async (name)=>{
|
|
244
|
-
return getBy({
|
|
245
|
-
name
|
|
246
|
-
});
|
|
750
|
+
if (deletedToken === null || deletedToken === undefined || deletedToken.kind !== 'admin') {
|
|
751
|
+
return deletedToken;
|
|
752
|
+
}
|
|
753
|
+
return {
|
|
754
|
+
...deletedToken,
|
|
755
|
+
adminUserOwner: toAdminTokenOwner(deletedToken.adminUserOwner)
|
|
756
|
+
};
|
|
247
757
|
};
|
|
248
758
|
/**
|
|
249
759
|
* Update a token and its permissions
|
|
250
760
|
*/ const update = async (id, attributes)=>{
|
|
251
|
-
// retrieve token without permissions
|
|
252
761
|
const originalToken = await strapi.db.query('admin::api-token').findOne({
|
|
762
|
+
select: SELECT_FIELDS,
|
|
763
|
+
populate: [
|
|
764
|
+
'adminUserOwner'
|
|
765
|
+
],
|
|
253
766
|
where: {
|
|
254
767
|
id
|
|
255
768
|
}
|
|
@@ -257,55 +770,112 @@ For security reasons, prefer storing the secret in an environment variable and r
|
|
|
257
770
|
if (!originalToken) {
|
|
258
771
|
throw new NotFoundError('Token not found');
|
|
259
772
|
}
|
|
260
|
-
const
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
assertCustomTokenPermissionsValidity(attributes.type || originalToken.type, attributes.permissions || originalToken.permissions);
|
|
773
|
+
const raw = attributes;
|
|
774
|
+
// kind is immutable after creation
|
|
775
|
+
if (raw.kind !== undefined && raw.kind !== null && raw.kind !== originalToken.kind) {
|
|
776
|
+
throw new ValidationError('kind is immutable after creation');
|
|
265
777
|
}
|
|
266
778
|
assertValidLifespan(attributes.lifespan);
|
|
779
|
+
let clampedAdminPermissions;
|
|
780
|
+
let tokenOwnerUser;
|
|
781
|
+
if (originalToken.kind === 'content-api') {
|
|
782
|
+
assertLegacyKindFields(attributes);
|
|
783
|
+
const incomingType = raw.type;
|
|
784
|
+
const incomingPermissions = raw.permissions;
|
|
785
|
+
const resolvedType = incomingType ?? originalToken.type;
|
|
786
|
+
const changingTypeToCustom = incomingType === constants.API_TOKEN_TYPE.CUSTOM && originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;
|
|
787
|
+
// Only re-validate if permissions or type are being changed
|
|
788
|
+
if (incomingPermissions !== undefined || changingTypeToCustom) {
|
|
789
|
+
assertCustomTokenPermissionsValidity(resolvedType, incomingPermissions ?? originalToken.permissions);
|
|
790
|
+
}
|
|
791
|
+
} else if (originalToken.kind === 'admin') {
|
|
792
|
+
assertAdminKindFields(attributes);
|
|
793
|
+
const incomingAdminPermissions = raw.adminPermissions;
|
|
794
|
+
if (incomingAdminPermissions !== undefined) {
|
|
795
|
+
await assertAdminPermissionsValidity(incomingAdminPermissions);
|
|
796
|
+
// Ceiling is always the owner's permissions, not the calling user's.
|
|
797
|
+
// A super admin editing another user's token must not overflow that user's scope.
|
|
798
|
+
const ownerId = getOwnerId(originalToken);
|
|
799
|
+
const resolvedOwner = await getService('user').findOne(ownerId);
|
|
800
|
+
if (resolvedOwner === null || resolvedOwner === undefined) {
|
|
801
|
+
throw new ValidationError('Token owner no longer exists');
|
|
802
|
+
}
|
|
803
|
+
tokenOwnerUser = resolvedOwner;
|
|
804
|
+
clampedAdminPermissions = await enforceAdminPermissionsCeiling(tokenOwnerUser, incomingAdminPermissions);
|
|
805
|
+
}
|
|
806
|
+
const incomingAdminUserOwner = raw.adminUserOwner;
|
|
807
|
+
if (incomingAdminUserOwner !== undefined) {
|
|
808
|
+
// Owner is immutable; the provided value must match the existing one
|
|
809
|
+
const existingOwner = originalToken.adminUserOwner;
|
|
810
|
+
const existingOwnerId = existingOwner === null || existingOwner === undefined ? null : String(typeof existingOwner === 'object' ? existingOwner.id : existingOwner);
|
|
811
|
+
const requestedOwnerId = incomingAdminUserOwner === null ? null : String(incomingAdminUserOwner);
|
|
812
|
+
if (requestedOwnerId !== existingOwnerId) {
|
|
813
|
+
throw new ValidationError('adminUserOwner cannot be changed on update');
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
267
817
|
const updatedToken = await strapi.db.query('admin::api-token').update({
|
|
268
818
|
select: SELECT_FIELDS,
|
|
269
819
|
where: {
|
|
270
820
|
id
|
|
271
821
|
},
|
|
272
|
-
|
|
822
|
+
// kind is immutable — strip it along with relation fields so the DB write is clean
|
|
823
|
+
data: omit([
|
|
824
|
+
'kind',
|
|
825
|
+
'permissions',
|
|
826
|
+
'adminPermissions',
|
|
827
|
+
'adminUserOwner'
|
|
828
|
+
], attributes)
|
|
273
829
|
});
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
830
|
+
if (originalToken.kind === 'content-api') {
|
|
831
|
+
const incomingPermissions = raw.permissions;
|
|
832
|
+
// custom tokens need to have their permissions updated as well
|
|
833
|
+
if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && incomingPermissions !== undefined) {
|
|
834
|
+
const currentPermissionsResult = await strapi.db.query('admin::api-token').load(updatedToken, 'permissions');
|
|
835
|
+
const currentPermissions = map('action', currentPermissionsResult || []);
|
|
836
|
+
const newPermissions = uniq(incomingPermissions || []);
|
|
837
|
+
const actionsToDelete = difference(currentPermissions, newPermissions);
|
|
838
|
+
const actionsToAdd = difference(newPermissions, currentPermissions);
|
|
839
|
+
// TODO: improve efficiency here
|
|
840
|
+
await Promise.all(actionsToDelete.map((action)=>strapi.db.query('admin::api-token-permission').delete({
|
|
841
|
+
where: {
|
|
842
|
+
action,
|
|
843
|
+
token: id
|
|
844
|
+
}
|
|
845
|
+
})));
|
|
846
|
+
// TODO: improve efficiency here
|
|
847
|
+
await Promise.all(actionsToAdd.map((action)=>strapi.db.query('admin::api-token-permission').create({
|
|
848
|
+
data: {
|
|
849
|
+
action,
|
|
850
|
+
token: id
|
|
851
|
+
}
|
|
852
|
+
})));
|
|
853
|
+
} else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {
|
|
854
|
+
await strapi.db.query('admin::api-token-permission').delete({
|
|
284
855
|
where: {
|
|
285
|
-
action,
|
|
286
856
|
token: id
|
|
287
857
|
}
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
});
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
const permissionsFromDb = await strapi.db.query('admin::api-token').load(updatedToken, 'permissions');
|
|
861
|
+
return {
|
|
862
|
+
...updatedToken,
|
|
863
|
+
permissions: permissionsFromDb ? permissionsFromDb.map((p)=>p.action) : undefined
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
// kind === 'admin'
|
|
867
|
+
if (clampedAdminPermissions !== undefined) {
|
|
868
|
+
if (tokenOwnerUser === undefined) {
|
|
869
|
+
throw new ValidationError('Updating admin permissions requires a resolved token owner');
|
|
870
|
+
}
|
|
871
|
+
await assignAdminPermissionsToToken(id, clampedAdminPermissions, tokenOwnerUser);
|
|
303
872
|
}
|
|
304
|
-
|
|
305
|
-
const
|
|
873
|
+
const adminPermissionsFromDb = await strapi.db.query('admin::api-token').load(updatedToken, 'adminPermissions');
|
|
874
|
+
const adminUserOwnerFromDb = await strapi.db.query('admin::api-token').load(updatedToken, 'adminUserOwner');
|
|
306
875
|
return {
|
|
307
876
|
...updatedToken,
|
|
308
|
-
|
|
877
|
+
adminPermissions: adminPermissionsFromDb || [],
|
|
878
|
+
adminUserOwner: toAdminTokenOwner(adminUserOwnerFromDb)
|
|
309
879
|
};
|
|
310
880
|
};
|
|
311
881
|
const count = async (where = {})=>{
|
|
@@ -313,6 +883,135 @@ const count = async (where = {})=>{
|
|
|
313
883
|
where
|
|
314
884
|
});
|
|
315
885
|
};
|
|
886
|
+
/**
|
|
887
|
+
* Delete all admin API tokens owned by the given user, including their associated admin permissions.
|
|
888
|
+
* Called when the owner user is deleted so tokens don't linger with a dangling owner FK.
|
|
889
|
+
*/ const deleteAdminTokensForUser = async (userId)=>{
|
|
890
|
+
const tokens = await strapi.db.query('admin::api-token').findMany({
|
|
891
|
+
where: {
|
|
892
|
+
kind: 'admin',
|
|
893
|
+
adminUserOwner: {
|
|
894
|
+
id: userId
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
select: [
|
|
898
|
+
'id'
|
|
899
|
+
],
|
|
900
|
+
populate: [
|
|
901
|
+
'adminPermissions'
|
|
902
|
+
]
|
|
903
|
+
});
|
|
904
|
+
for (const token of tokens){
|
|
905
|
+
const permissionIds = (token.adminPermissions ?? []).map((p)=>p.id).filter((id)=>id !== null && id !== undefined);
|
|
906
|
+
if (permissionIds.length > 0) {
|
|
907
|
+
await getService('permission').deleteByIds(permissionIds);
|
|
908
|
+
}
|
|
909
|
+
await strapi.db.query('admin::api-token').delete({
|
|
910
|
+
where: {
|
|
911
|
+
id: token.id
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
function createTokenService(kind) {
|
|
917
|
+
const shared = {
|
|
918
|
+
hash,
|
|
919
|
+
checkSaltIsDefined,
|
|
920
|
+
getByAccessKey: (accessKeyHash, options)=>getBy({
|
|
921
|
+
accessKey: accessKeyHash
|
|
922
|
+
}, options),
|
|
923
|
+
countAll: count,
|
|
924
|
+
reconcileTokenPermissionsToUserCeiling
|
|
925
|
+
};
|
|
926
|
+
if (kind === 'content-api') {
|
|
927
|
+
const svc = {
|
|
928
|
+
...shared,
|
|
929
|
+
create: (attributes, callingUser)=>create({
|
|
930
|
+
...attributes,
|
|
931
|
+
kind: 'content-api'
|
|
932
|
+
}, callingUser),
|
|
933
|
+
list: (callingUser)=>list(callingUser, {
|
|
934
|
+
filter: {
|
|
935
|
+
kind: 'content-api'
|
|
936
|
+
}
|
|
937
|
+
}),
|
|
938
|
+
getById: (id, options)=>getBy({
|
|
939
|
+
$and: [
|
|
940
|
+
{
|
|
941
|
+
id
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
$or: [
|
|
945
|
+
{
|
|
946
|
+
kind: 'content-api'
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
kind: {
|
|
950
|
+
$null: true
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
]
|
|
954
|
+
}
|
|
955
|
+
]
|
|
956
|
+
}, options),
|
|
957
|
+
getByName: (name, options)=>getBy({
|
|
958
|
+
$and: [
|
|
959
|
+
{
|
|
960
|
+
name
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
$or: [
|
|
964
|
+
{
|
|
965
|
+
kind: 'content-api'
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
kind: {
|
|
969
|
+
$null: true
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
]
|
|
973
|
+
}
|
|
974
|
+
]
|
|
975
|
+
}, options),
|
|
976
|
+
update: (id, attributes)=>update(id, attributes),
|
|
977
|
+
revoke: (id)=>revoke(id),
|
|
978
|
+
regenerate: (id)=>regenerate(id),
|
|
979
|
+
exists,
|
|
980
|
+
count
|
|
981
|
+
};
|
|
982
|
+
return svc;
|
|
983
|
+
}
|
|
984
|
+
const svc = {
|
|
985
|
+
...shared,
|
|
986
|
+
create: (attributes, callingUser)=>create({
|
|
987
|
+
...attributes,
|
|
988
|
+
kind: 'admin'
|
|
989
|
+
}, callingUser),
|
|
990
|
+
list: (callingUser)=>list(callingUser, {
|
|
991
|
+
filter: {
|
|
992
|
+
kind: 'admin'
|
|
993
|
+
}
|
|
994
|
+
}),
|
|
995
|
+
getById: (id, options)=>getBy({
|
|
996
|
+
id,
|
|
997
|
+
kind: 'admin'
|
|
998
|
+
}, options),
|
|
999
|
+
getByName: (name, options)=>getBy({
|
|
1000
|
+
name,
|
|
1001
|
+
kind: 'admin'
|
|
1002
|
+
}, options),
|
|
1003
|
+
update: (id, attributes)=>update(id, attributes),
|
|
1004
|
+
revoke: (id)=>revoke(id),
|
|
1005
|
+
regenerate: (id)=>regenerate(id),
|
|
1006
|
+
exists,
|
|
1007
|
+
count,
|
|
1008
|
+
assignAdminPermissionsToToken,
|
|
1009
|
+
syncPermissionsForUser: syncApiTokenPermissionsForUser,
|
|
1010
|
+
syncPermissionsForRole: syncApiTokenPermissionsForRole,
|
|
1011
|
+
deleteTokensForUser: deleteAdminTokensForUser
|
|
1012
|
+
};
|
|
1013
|
+
return svc;
|
|
1014
|
+
}
|
|
316
1015
|
|
|
317
|
-
export { checkSaltIsDefined, count, create,
|
|
1016
|
+
export { assignAdminPermissionsToToken, checkSaltIsDefined, count, create, createTokenService, deleteAdminTokensForUser, enforceAdminPermissionsCeiling, exists, getBy, hash, list, reconcileTokenPermissionsToUserCeiling, regenerate, revoke, syncApiTokenPermissionsForRole, syncApiTokenPermissionsForUser, update };
|
|
318
1017
|
//# sourceMappingURL=api-token.mjs.map
|