@strapi/admin 5.47.1 → 5.48.1

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 (126) hide show
  1. package/dist/admin/admin/src/StrapiApp.js +8 -5
  2. package/dist/admin/admin/src/StrapiApp.js.map +1 -1
  3. package/dist/admin/admin/src/StrapiApp.mjs +9 -6
  4. package/dist/admin/admin/src/StrapiApp.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/LeftMenu.js +1 -1
  6. package/dist/admin/admin/src/components/LeftMenu.js.map +1 -1
  7. package/dist/admin/admin/src/components/LeftMenu.mjs +1 -1
  8. package/dist/admin/admin/src/components/LeftMenu.mjs.map +1 -1
  9. package/dist/admin/admin/src/components/UpsellBanner.js +1 -1
  10. package/dist/admin/admin/src/components/UpsellBanner.js.map +1 -1
  11. package/dist/admin/admin/src/components/UpsellBanner.mjs +1 -1
  12. package/dist/admin/admin/src/components/UpsellBanner.mjs.map +1 -1
  13. package/dist/admin/admin/src/hooks/useMenu.js +40 -5
  14. package/dist/admin/admin/src/hooks/useMenu.js.map +1 -1
  15. package/dist/admin/admin/src/hooks/useMenu.mjs +40 -6
  16. package/dist/admin/admin/src/hooks/useMenu.mjs.map +1 -1
  17. package/dist/admin/admin/src/hooks/useSettingsMenu.js +61 -1
  18. package/dist/admin/admin/src/hooks/useSettingsMenu.js.map +1 -1
  19. package/dist/admin/admin/src/hooks/useSettingsMenu.mjs +61 -2
  20. package/dist/admin/admin/src/hooks/useSettingsMenu.mjs.map +1 -1
  21. package/dist/admin/admin/src/pages/Home/HomePage.js +6 -2
  22. package/dist/admin/admin/src/pages/Home/HomePage.js.map +1 -1
  23. package/dist/admin/admin/src/pages/Home/HomePage.mjs +6 -2
  24. package/dist/admin/admin/src/pages/Home/HomePage.mjs.map +1 -1
  25. package/dist/admin/admin/src/pages/Home/components/FreeTrialEndedModal.js +1 -1
  26. package/dist/admin/admin/src/pages/Home/components/FreeTrialEndedModal.js.map +1 -1
  27. package/dist/admin/admin/src/pages/Home/components/FreeTrialEndedModal.mjs +1 -1
  28. package/dist/admin/admin/src/pages/Home/components/FreeTrialEndedModal.mjs.map +1 -1
  29. package/dist/admin/admin/src/pages/Settings/pages/ApplicationInfo/ApplicationInfoPage.js +2 -0
  30. package/dist/admin/admin/src/pages/Settings/pages/ApplicationInfo/ApplicationInfoPage.js.map +1 -1
  31. package/dist/admin/admin/src/pages/Settings/pages/ApplicationInfo/ApplicationInfoPage.mjs +2 -0
  32. package/dist/admin/admin/src/pages/Settings/pages/ApplicationInfo/ApplicationInfoPage.mjs.map +1 -1
  33. package/dist/admin/admin/src/translations/ar.json.js +1 -1
  34. package/dist/admin/admin/src/translations/ar.json.mjs +1 -1
  35. package/dist/admin/admin/src/translations/cs.json.js +1 -1
  36. package/dist/admin/admin/src/translations/cs.json.mjs +1 -1
  37. package/dist/admin/admin/src/translations/de.json.js +1 -1
  38. package/dist/admin/admin/src/translations/de.json.mjs +1 -1
  39. package/dist/admin/admin/src/translations/en.json.js +1 -1
  40. package/dist/admin/admin/src/translations/en.json.mjs +1 -1
  41. package/dist/admin/admin/src/translations/es.json.js +1 -1
  42. package/dist/admin/admin/src/translations/es.json.mjs +1 -1
  43. package/dist/admin/admin/src/translations/fi.json.js +1 -1
  44. package/dist/admin/admin/src/translations/fi.json.mjs +1 -1
  45. package/dist/admin/admin/src/translations/fr.json.js +1 -1
  46. package/dist/admin/admin/src/translations/fr.json.mjs +1 -1
  47. package/dist/admin/admin/src/translations/it.json.js +1 -1
  48. package/dist/admin/admin/src/translations/it.json.mjs +1 -1
  49. package/dist/admin/admin/src/translations/ko.json.js +1 -1
  50. package/dist/admin/admin/src/translations/ko.json.mjs +1 -1
  51. package/dist/admin/admin/src/translations/nl.json.js +1 -1
  52. package/dist/admin/admin/src/translations/nl.json.mjs +1 -1
  53. package/dist/admin/admin/src/translations/pl.json.js +1 -1
  54. package/dist/admin/admin/src/translations/pl.json.mjs +1 -1
  55. package/dist/admin/admin/src/translations/ru.json.js +1 -1
  56. package/dist/admin/admin/src/translations/ru.json.mjs +1 -1
  57. package/dist/admin/admin/src/translations/tr.json.js +1 -1
  58. package/dist/admin/admin/src/translations/tr.json.mjs +1 -1
  59. package/dist/admin/admin/src/translations/uk.json.js +1 -1
  60. package/dist/admin/admin/src/translations/uk.json.mjs +1 -1
  61. package/dist/admin/admin/src/translations/vi.json.js +1 -1
  62. package/dist/admin/admin/src/translations/vi.json.mjs +1 -1
  63. package/dist/admin/admin/src/translations/zh-Hans.json.js +1 -1
  64. package/dist/admin/admin/src/translations/zh-Hans.json.mjs +1 -1
  65. package/dist/admin/admin/src/utils/getFetchClient.js +1 -1
  66. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  67. package/dist/admin/admin/src/utils/getFetchClient.mjs +1 -1
  68. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  69. package/dist/admin/admin/src/utils/widgetVisibility.js +25 -0
  70. package/dist/admin/admin/src/utils/widgetVisibility.js.map +1 -0
  71. package/dist/admin/admin/src/utils/widgetVisibility.mjs +22 -0
  72. package/dist/admin/admin/src/utils/widgetVisibility.mjs.map +1 -0
  73. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.js +6 -4
  74. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.js.map +1 -1
  75. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.mjs +6 -4
  76. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.mjs.map +1 -1
  77. package/dist/admin/src/hooks/useMenu.d.ts +2 -1
  78. package/dist/admin/src/hooks/useSettingsMenu.d.ts +2 -1
  79. package/dist/admin/src/utils/widgetVisibility.d.ts +18 -0
  80. package/dist/ee/server/src/controllers/user.d.ts.map +1 -1
  81. package/dist/server/ee/server/src/controllers/user.js +7 -6
  82. package/dist/server/ee/server/src/controllers/user.js.map +1 -1
  83. package/dist/server/ee/server/src/controllers/user.mjs +7 -6
  84. package/dist/server/ee/server/src/controllers/user.mjs.map +1 -1
  85. package/dist/server/server/src/controllers/authenticated-user.js +25 -4
  86. package/dist/server/server/src/controllers/authenticated-user.js.map +1 -1
  87. package/dist/server/server/src/controllers/authenticated-user.mjs +25 -4
  88. package/dist/server/server/src/controllers/authenticated-user.mjs.map +1 -1
  89. package/dist/server/server/src/controllers/authentication.js +1 -16
  90. package/dist/server/server/src/controllers/authentication.js.map +1 -1
  91. package/dist/server/server/src/controllers/authentication.mjs +1 -16
  92. package/dist/server/server/src/controllers/authentication.mjs.map +1 -1
  93. package/dist/server/server/src/controllers/user.js +6 -5
  94. package/dist/server/server/src/controllers/user.js.map +1 -1
  95. package/dist/server/server/src/controllers/user.mjs +6 -5
  96. package/dist/server/server/src/controllers/user.mjs.map +1 -1
  97. package/dist/server/server/src/routes/authentication.js +4 -1
  98. package/dist/server/server/src/routes/authentication.js.map +1 -1
  99. package/dist/server/server/src/routes/authentication.mjs +4 -1
  100. package/dist/server/server/src/routes/authentication.mjs.map +1 -1
  101. package/dist/server/server/src/services/permission/permissions-manager/permission-fields.js +20 -11
  102. package/dist/server/server/src/services/permission/permissions-manager/permission-fields.js.map +1 -1
  103. package/dist/server/server/src/services/permission/permissions-manager/permission-fields.mjs +21 -12
  104. package/dist/server/server/src/services/permission/permissions-manager/permission-fields.mjs.map +1 -1
  105. package/dist/server/server/src/services/user.js +40 -3
  106. package/dist/server/server/src/services/user.js.map +1 -1
  107. package/dist/server/server/src/services/user.mjs +40 -3
  108. package/dist/server/server/src/services/user.mjs.map +1 -1
  109. package/dist/server/server/src/utils/normalize-email.js +24 -0
  110. package/dist/server/server/src/utils/normalize-email.js.map +1 -0
  111. package/dist/server/server/src/utils/normalize-email.mjs +22 -0
  112. package/dist/server/server/src/utils/normalize-email.mjs.map +1 -0
  113. package/dist/server/src/controllers/authenticated-user.d.ts.map +1 -1
  114. package/dist/server/src/controllers/authentication.d.ts.map +1 -1
  115. package/dist/server/src/controllers/user.d.ts.map +1 -1
  116. package/dist/server/src/index.d.ts +1 -0
  117. package/dist/server/src/index.d.ts.map +1 -1
  118. package/dist/server/src/routes/authentication.d.ts.map +1 -1
  119. package/dist/server/src/services/index.d.ts +1 -0
  120. package/dist/server/src/services/index.d.ts.map +1 -1
  121. package/dist/server/src/services/permission/permissions-manager/permission-fields.d.ts.map +1 -1
  122. package/dist/server/src/services/user.d.ts +1 -0
  123. package/dist/server/src/services/user.d.ts.map +1 -1
  124. package/dist/server/src/utils/normalize-email.d.ts +12 -0
  125. package/dist/server/src/utils/normalize-email.d.ts.map +1 -0
  126. package/package.json +12 -14
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSeatInfo.js","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.tsx"],"sourcesContent":["import { Flex, Tooltip, Typography, Link, Grid } from '@strapi/design-system';\nimport { ExternalLink, WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { useSelector } from 'react-redux';\n\nimport { useRBAC } from '../../../../../../../../admin/src/hooks/useRBAC';\nimport { selectAdminPermissions } from '../../../../../../../../admin/src/selectors';\nimport { useLicenseLimits } from '../../../../../hooks/useLicenseLimits';\n\nconst BILLING_SELF_HOSTED_URL = 'https://strapi.io/billing/request-seats';\nconst MANAGE_SEATS_URL = 'https://strapi.io/billing/manage-seats';\n\nexport const AdminSeatInfoEE = () => {\n const { formatMessage } = useIntl();\n const { settings } = useSelector(selectAdminPermissions);\n const {\n isLoading: isRBACLoading,\n allowedActions: { canRead, canCreate, canUpdate, canDelete },\n } = useRBAC(settings?.users ?? {});\n const {\n license,\n isError,\n isLoading: isLicenseLoading,\n } = useLicenseLimits({\n // TODO: this creates a waterfall which we should avoid to render earlier, but for that\n // we will have to move away from data-fetching hooks to query functions.\n // Short-term we could at least implement a loader, for the user to have visual feedback\n // in case the requests take a while\n enabled: !isRBACLoading && canRead && canCreate && canUpdate && canDelete,\n });\n\n const isLoading = isRBACLoading || isLicenseLoading;\n\n if (isError || isLoading || !license) {\n return null;\n }\n\n const { licenseLimitStatus, enforcementUserCount, permittedSeats, type } = license;\n\n if (!permittedSeats) {\n return null;\n }\n\n return (\n <Grid.Item col={6} xs={12} direction=\"column\" alignItems=\"stretch\">\n <Typography variant=\"sigma\" textColor=\"neutral600\">\n {formatMessage({\n id: 'Settings.application.admin-seats',\n defaultMessage: 'Admin seats',\n })}\n </Typography>\n <Flex gap={2}>\n <Flex>\n <Typography tag=\"p\">\n {formatMessage(\n {\n id: 'Settings.application.ee.admin-seats.count',\n defaultMessage: '<text>{enforcementUserCount}</text>/{permittedSeats}',\n },\n {\n permittedSeats,\n enforcementUserCount,\n text: (chunks) =>\n (\n <Typography\n fontWeight=\"semiBold\"\n textColor={enforcementUserCount > permittedSeats ? 'danger500' : undefined}\n >\n {chunks}\n </Typography>\n ) as any,\n }\n )}\n </Typography>\n </Flex>\n {licenseLimitStatus === 'OVER_LIMIT' && (\n <Tooltip\n label={formatMessage({\n id: 'Settings.application.ee.admin-seats.at-limit-tooltip',\n defaultMessage: 'At limit: add seats to invite more users',\n })}\n >\n <WarningCircle width=\"1.4rem\" height=\"1.4rem\" fill=\"danger500\" />\n </Tooltip>\n )}\n </Flex>\n {type === 'gold' ? (\n <Link href={BILLING_SELF_HOSTED_URL} endIcon={<ExternalLink />}>\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.support',\n defaultMessage: 'Contact sales',\n })}\n </Link>\n ) : (\n <Link href={MANAGE_SEATS_URL} isExternal endIcon={<ExternalLink />}>\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.add-seats',\n defaultMessage: 'Manage seats',\n })}\n </Link>\n )}\n </Grid.Item>\n );\n};\n"],"names":["BILLING_SELF_HOSTED_URL","MANAGE_SEATS_URL","AdminSeatInfoEE","formatMessage","useIntl","settings","useSelector","selectAdminPermissions","isLoading","isRBACLoading","allowedActions","canRead","canCreate","canUpdate","canDelete","useRBAC","users","license","isError","isLicenseLoading","useLicenseLimits","enabled","licenseLimitStatus","enforcementUserCount","permittedSeats","type","_jsxs","Grid","Item","col","xs","direction","alignItems","_jsx","Typography","variant","textColor","id","defaultMessage","Flex","gap","tag","text","chunks","fontWeight","undefined","Tooltip","label","WarningCircle","width","height","fill","Link","href","endIcon","ExternalLink","isExternal"],"mappings":"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"}
1
+ {"version":3,"file":"AdminSeatInfo.js","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.tsx"],"sourcesContent":["import { Flex, Tooltip, Typography, Link, Grid } from '@strapi/design-system';\nimport { ExternalLink, WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { useSelector } from 'react-redux';\n\nimport { useRBAC } from '../../../../../../../../admin/src/hooks/useRBAC';\nimport { selectAdminPermissions } from '../../../../../../../../admin/src/selectors';\nimport { useLicenseLimits } from '../../../../../hooks/useLicenseLimits';\n\nconst BILLING_SELF_HOSTED_URL = 'mailto:sales@strapi.io';\nconst MANAGE_SUBSCRIPTION_URL = 'https://billing.strapi.io';\n\nexport const AdminSeatInfoEE = () => {\n const { formatMessage } = useIntl();\n const { settings } = useSelector(selectAdminPermissions);\n const {\n isLoading: isRBACLoading,\n allowedActions: { canRead, canCreate, canUpdate, canDelete },\n } = useRBAC(settings?.users ?? {});\n const {\n license,\n isError,\n isLoading: isLicenseLoading,\n } = useLicenseLimits({\n // TODO: this creates a waterfall which we should avoid to render earlier, but for that\n // we will have to move away from data-fetching hooks to query functions.\n // Short-term we could at least implement a loader, for the user to have visual feedback\n // in case the requests take a while\n enabled: !isRBACLoading && canRead && canCreate && canUpdate && canDelete,\n });\n\n const isLoading = isRBACLoading || isLicenseLoading;\n\n if (isError || isLoading || !license) {\n return null;\n }\n\n const { licenseLimitStatus, enforcementUserCount, permittedSeats, type } = license;\n\n if (!permittedSeats) {\n return null;\n }\n\n return (\n <Grid.Item col={6} xs={12} direction=\"column\" alignItems=\"stretch\">\n <Typography variant=\"sigma\" textColor=\"neutral600\">\n {formatMessage({\n id: 'Settings.application.admin-seats',\n defaultMessage: 'Admin seats',\n })}\n </Typography>\n <Flex gap={2}>\n <Flex>\n <Typography tag=\"p\">\n {formatMessage(\n {\n id: 'Settings.application.ee.admin-seats.count',\n defaultMessage: '<text>{enforcementUserCount}</text>/{permittedSeats}',\n },\n {\n permittedSeats,\n enforcementUserCount,\n text: (chunks) =>\n (\n <Typography\n fontWeight=\"semiBold\"\n textColor={enforcementUserCount > permittedSeats ? 'danger500' : undefined}\n >\n {chunks}\n </Typography>\n ) as any,\n }\n )}\n </Typography>\n </Flex>\n {licenseLimitStatus === 'OVER_LIMIT' && (\n <Tooltip\n label={formatMessage({\n id: 'Settings.application.ee.admin-seats.at-limit-tooltip',\n defaultMessage: 'At limit: add seats to invite more users',\n })}\n >\n <WarningCircle width=\"1.4rem\" height=\"1.4rem\" fill=\"danger500\" />\n </Tooltip>\n )}\n </Flex>\n {type === 'gold' ? (\n <Link href={BILLING_SELF_HOSTED_URL} endIcon={<ExternalLink />} target=\"_blank\">\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.support',\n defaultMessage: 'Contact sales',\n })}\n </Link>\n ) : (\n <Link href={MANAGE_SUBSCRIPTION_URL} isExternal endIcon={<ExternalLink />} target=\"_blank\">\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.add-seats',\n defaultMessage: 'Manage subscription',\n })}\n </Link>\n )}\n </Grid.Item>\n );\n};\n"],"names":["BILLING_SELF_HOSTED_URL","MANAGE_SUBSCRIPTION_URL","AdminSeatInfoEE","formatMessage","useIntl","settings","useSelector","selectAdminPermissions","isLoading","isRBACLoading","allowedActions","canRead","canCreate","canUpdate","canDelete","useRBAC","users","license","isError","isLicenseLoading","useLicenseLimits","enabled","licenseLimitStatus","enforcementUserCount","permittedSeats","type","_jsxs","Grid","Item","col","xs","direction","alignItems","_jsx","Typography","variant","textColor","id","defaultMessage","Flex","gap","tag","text","chunks","fontWeight","undefined","Tooltip","label","WarningCircle","width","height","fill","Link","href","endIcon","ExternalLink","target","isExternal"],"mappings":"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"}
@@ -7,8 +7,8 @@ import { useRBAC } from '../../../../../../../../admin/src/hooks/useRBAC.mjs';
7
7
  import { selectAdminPermissions } from '../../../../../../../../admin/src/selectors.mjs';
8
8
  import { useLicenseLimits } from '../../../../../hooks/useLicenseLimits.mjs';
9
9
 
10
- const BILLING_SELF_HOSTED_URL = 'https://strapi.io/billing/request-seats';
11
- const MANAGE_SEATS_URL = 'https://strapi.io/billing/manage-seats';
10
+ const BILLING_SELF_HOSTED_URL = 'mailto:sales@strapi.io';
11
+ const MANAGE_SUBSCRIPTION_URL = 'https://billing.strapi.io';
12
12
  const AdminSeatInfoEE = ()=>{
13
13
  const { formatMessage } = useIntl();
14
14
  const { settings } = useSelector(selectAdminPermissions);
@@ -78,17 +78,19 @@ const AdminSeatInfoEE = ()=>{
78
78
  type === 'gold' ? /*#__PURE__*/ jsx(Link, {
79
79
  href: BILLING_SELF_HOSTED_URL,
80
80
  endIcon: /*#__PURE__*/ jsx(ExternalLink, {}),
81
+ target: "_blank",
81
82
  children: formatMessage({
82
83
  id: 'Settings.application.ee.admin-seats.support',
83
84
  defaultMessage: 'Contact sales'
84
85
  })
85
86
  }) : /*#__PURE__*/ jsx(Link, {
86
- href: MANAGE_SEATS_URL,
87
+ href: MANAGE_SUBSCRIPTION_URL,
87
88
  isExternal: true,
88
89
  endIcon: /*#__PURE__*/ jsx(ExternalLink, {}),
90
+ target: "_blank",
89
91
  children: formatMessage({
90
92
  id: 'Settings.application.ee.admin-seats.add-seats',
91
- defaultMessage: 'Manage seats'
93
+ defaultMessage: 'Manage subscription'
92
94
  })
93
95
  })
94
96
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSeatInfo.mjs","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.tsx"],"sourcesContent":["import { Flex, Tooltip, Typography, Link, Grid } from '@strapi/design-system';\nimport { ExternalLink, WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { useSelector } from 'react-redux';\n\nimport { useRBAC } from '../../../../../../../../admin/src/hooks/useRBAC';\nimport { selectAdminPermissions } from '../../../../../../../../admin/src/selectors';\nimport { useLicenseLimits } from '../../../../../hooks/useLicenseLimits';\n\nconst BILLING_SELF_HOSTED_URL = 'https://strapi.io/billing/request-seats';\nconst MANAGE_SEATS_URL = 'https://strapi.io/billing/manage-seats';\n\nexport const AdminSeatInfoEE = () => {\n const { formatMessage } = useIntl();\n const { settings } = useSelector(selectAdminPermissions);\n const {\n isLoading: isRBACLoading,\n allowedActions: { canRead, canCreate, canUpdate, canDelete },\n } = useRBAC(settings?.users ?? {});\n const {\n license,\n isError,\n isLoading: isLicenseLoading,\n } = useLicenseLimits({\n // TODO: this creates a waterfall which we should avoid to render earlier, but for that\n // we will have to move away from data-fetching hooks to query functions.\n // Short-term we could at least implement a loader, for the user to have visual feedback\n // in case the requests take a while\n enabled: !isRBACLoading && canRead && canCreate && canUpdate && canDelete,\n });\n\n const isLoading = isRBACLoading || isLicenseLoading;\n\n if (isError || isLoading || !license) {\n return null;\n }\n\n const { licenseLimitStatus, enforcementUserCount, permittedSeats, type } = license;\n\n if (!permittedSeats) {\n return null;\n }\n\n return (\n <Grid.Item col={6} xs={12} direction=\"column\" alignItems=\"stretch\">\n <Typography variant=\"sigma\" textColor=\"neutral600\">\n {formatMessage({\n id: 'Settings.application.admin-seats',\n defaultMessage: 'Admin seats',\n })}\n </Typography>\n <Flex gap={2}>\n <Flex>\n <Typography tag=\"p\">\n {formatMessage(\n {\n id: 'Settings.application.ee.admin-seats.count',\n defaultMessage: '<text>{enforcementUserCount}</text>/{permittedSeats}',\n },\n {\n permittedSeats,\n enforcementUserCount,\n text: (chunks) =>\n (\n <Typography\n fontWeight=\"semiBold\"\n textColor={enforcementUserCount > permittedSeats ? 'danger500' : undefined}\n >\n {chunks}\n </Typography>\n ) as any,\n }\n )}\n </Typography>\n </Flex>\n {licenseLimitStatus === 'OVER_LIMIT' && (\n <Tooltip\n label={formatMessage({\n id: 'Settings.application.ee.admin-seats.at-limit-tooltip',\n defaultMessage: 'At limit: add seats to invite more users',\n })}\n >\n <WarningCircle width=\"1.4rem\" height=\"1.4rem\" fill=\"danger500\" />\n </Tooltip>\n )}\n </Flex>\n {type === 'gold' ? (\n <Link href={BILLING_SELF_HOSTED_URL} endIcon={<ExternalLink />}>\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.support',\n defaultMessage: 'Contact sales',\n })}\n </Link>\n ) : (\n <Link href={MANAGE_SEATS_URL} isExternal endIcon={<ExternalLink />}>\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.add-seats',\n defaultMessage: 'Manage seats',\n })}\n </Link>\n )}\n </Grid.Item>\n );\n};\n"],"names":["BILLING_SELF_HOSTED_URL","MANAGE_SEATS_URL","AdminSeatInfoEE","formatMessage","useIntl","settings","useSelector","selectAdminPermissions","isLoading","isRBACLoading","allowedActions","canRead","canCreate","canUpdate","canDelete","useRBAC","users","license","isError","isLicenseLoading","useLicenseLimits","enabled","licenseLimitStatus","enforcementUserCount","permittedSeats","type","_jsxs","Grid","Item","col","xs","direction","alignItems","_jsx","Typography","variant","textColor","id","defaultMessage","Flex","gap","tag","text","chunks","fontWeight","undefined","Tooltip","label","WarningCircle","width","height","fill","Link","href","endIcon","ExternalLink","isExternal"],"mappings":"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"}
1
+ {"version":3,"file":"AdminSeatInfo.mjs","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/ApplicationInfoPage/components/AdminSeatInfo.tsx"],"sourcesContent":["import { Flex, Tooltip, Typography, Link, Grid } from '@strapi/design-system';\nimport { ExternalLink, WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { useSelector } from 'react-redux';\n\nimport { useRBAC } from '../../../../../../../../admin/src/hooks/useRBAC';\nimport { selectAdminPermissions } from '../../../../../../../../admin/src/selectors';\nimport { useLicenseLimits } from '../../../../../hooks/useLicenseLimits';\n\nconst BILLING_SELF_HOSTED_URL = 'mailto:sales@strapi.io';\nconst MANAGE_SUBSCRIPTION_URL = 'https://billing.strapi.io';\n\nexport const AdminSeatInfoEE = () => {\n const { formatMessage } = useIntl();\n const { settings } = useSelector(selectAdminPermissions);\n const {\n isLoading: isRBACLoading,\n allowedActions: { canRead, canCreate, canUpdate, canDelete },\n } = useRBAC(settings?.users ?? {});\n const {\n license,\n isError,\n isLoading: isLicenseLoading,\n } = useLicenseLimits({\n // TODO: this creates a waterfall which we should avoid to render earlier, but for that\n // we will have to move away from data-fetching hooks to query functions.\n // Short-term we could at least implement a loader, for the user to have visual feedback\n // in case the requests take a while\n enabled: !isRBACLoading && canRead && canCreate && canUpdate && canDelete,\n });\n\n const isLoading = isRBACLoading || isLicenseLoading;\n\n if (isError || isLoading || !license) {\n return null;\n }\n\n const { licenseLimitStatus, enforcementUserCount, permittedSeats, type } = license;\n\n if (!permittedSeats) {\n return null;\n }\n\n return (\n <Grid.Item col={6} xs={12} direction=\"column\" alignItems=\"stretch\">\n <Typography variant=\"sigma\" textColor=\"neutral600\">\n {formatMessage({\n id: 'Settings.application.admin-seats',\n defaultMessage: 'Admin seats',\n })}\n </Typography>\n <Flex gap={2}>\n <Flex>\n <Typography tag=\"p\">\n {formatMessage(\n {\n id: 'Settings.application.ee.admin-seats.count',\n defaultMessage: '<text>{enforcementUserCount}</text>/{permittedSeats}',\n },\n {\n permittedSeats,\n enforcementUserCount,\n text: (chunks) =>\n (\n <Typography\n fontWeight=\"semiBold\"\n textColor={enforcementUserCount > permittedSeats ? 'danger500' : undefined}\n >\n {chunks}\n </Typography>\n ) as any,\n }\n )}\n </Typography>\n </Flex>\n {licenseLimitStatus === 'OVER_LIMIT' && (\n <Tooltip\n label={formatMessage({\n id: 'Settings.application.ee.admin-seats.at-limit-tooltip',\n defaultMessage: 'At limit: add seats to invite more users',\n })}\n >\n <WarningCircle width=\"1.4rem\" height=\"1.4rem\" fill=\"danger500\" />\n </Tooltip>\n )}\n </Flex>\n {type === 'gold' ? (\n <Link href={BILLING_SELF_HOSTED_URL} endIcon={<ExternalLink />} target=\"_blank\">\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.support',\n defaultMessage: 'Contact sales',\n })}\n </Link>\n ) : (\n <Link href={MANAGE_SUBSCRIPTION_URL} isExternal endIcon={<ExternalLink />} target=\"_blank\">\n {formatMessage({\n id: 'Settings.application.ee.admin-seats.add-seats',\n defaultMessage: 'Manage subscription',\n })}\n </Link>\n )}\n </Grid.Item>\n );\n};\n"],"names":["BILLING_SELF_HOSTED_URL","MANAGE_SUBSCRIPTION_URL","AdminSeatInfoEE","formatMessage","useIntl","settings","useSelector","selectAdminPermissions","isLoading","isRBACLoading","allowedActions","canRead","canCreate","canUpdate","canDelete","useRBAC","users","license","isError","isLicenseLoading","useLicenseLimits","enabled","licenseLimitStatus","enforcementUserCount","permittedSeats","type","_jsxs","Grid","Item","col","xs","direction","alignItems","_jsx","Typography","variant","textColor","id","defaultMessage","Flex","gap","tag","text","chunks","fontWeight","undefined","Tooltip","label","WarningCircle","width","height","fill","Link","href","endIcon","ExternalLink","target","isExternal"],"mappings":"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"}
@@ -15,4 +15,5 @@ export interface Menu {
15
15
  isLoading: boolean;
16
16
  }
17
17
  declare const useMenu: (shouldUpdateStrapi: boolean) => Menu;
18
- export { useMenu };
18
+ declare const normalizeMenuLinks: (links: unknown) => MenuItem[];
19
+ export { useMenu, normalizeMenuLinks };
@@ -25,5 +25,6 @@ declare const useSettingsMenu: () => {
25
25
  isLoading: boolean;
26
26
  menu: SettingsMenu;
27
27
  };
28
- export { useSettingsMenu };
28
+ declare const normalizeSettings: (settings: unknown) => Record<string, SettingsMenuSection>;
29
+ export { useSettingsMenu, normalizeSettings };
29
30
  export type { SettingsMenu };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Helpers that decide which widgets should be displayed (when/where), as opposed to
3
+ * `widgetLayout.ts`, which handles how widgets are sized and positioned.
4
+ */
5
+ import type { WidgetWithUID } from '../core/apis/Widgets';
6
+ /**
7
+ * UID of the deploy-now widget, a Strapi Cloud upsell promoting a first deployment.
8
+ */
9
+ export declare const DEPLOY_NOW_WIDGET_UID = "plugin::admin.deploy-now";
10
+ /**
11
+ * Hides the deploy-now upsell widget when the project runs in production.
12
+ *
13
+ * The widget promotes deploying the project for the first time, which is no longer
14
+ * relevant once the project is live. This relies on the server-reported environment
15
+ * (`currentEnvironment` from `/admin/information`) rather than any frontend
16
+ * production heuristic, and keeps the widget visible when the environment is unknown.
17
+ */
18
+ export declare const hideDeployNowWidgetInProduction: (widgets: WidgetWithUID[], currentEnvironment?: string) => WidgetWithUID[];
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../../ee/server/src/controllers/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;;gBAgCf,OAAO;gBAiCP,OAAO;qBAkCF,OAAO;;AApEhC,wBA8EE"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../../ee/server/src/controllers/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;;gBAiCf,OAAO;gBAiCP,OAAO;qBAkCF,OAAO;;AApEhC,wBA8EE"}
@@ -5,6 +5,7 @@ var fp = require('lodash/fp');
5
5
  var utils = require('@strapi/utils');
6
6
  var user$2 = require('../validation/user.js');
7
7
  var user$1 = require('../../../../server/src/validation/user.js');
8
+ var normalizeEmail = require('../../../../server/src/utils/normalize-email.js');
8
9
  var index = require('../utils/index.js');
9
10
  var ssoLock = require('../utils/sso-lock.js');
10
11
 
@@ -66,24 +67,24 @@ var user = {
66
67
  },
67
68
  async update (ctx) {
68
69
  const { id } = ctx.params;
69
- const { body: input } = ctx.request;
70
- await user$1.validateUserUpdateInput(input);
71
- if (_.has(input, 'email')) {
70
+ const data = normalizeEmail.normalizeEmail(ctx.request.body);
71
+ await user$1.validateUserUpdateInput(data);
72
+ if (_.has(data, 'email')) {
72
73
  const uniqueEmailCheck = await index.getService('user').exists({
73
74
  id: {
74
75
  $ne: id
75
76
  },
76
- email: input.email
77
+ email: data.email
77
78
  });
78
79
  if (uniqueEmailCheck) {
79
80
  throw new ApplicationError('A user with this email address already exists');
80
81
  }
81
82
  }
82
83
  const user = await index.getService('user').findOne(id, null);
83
- if (!await hasAdminSeatsAvaialble() && !user.isActive && input.isActive) {
84
+ if (!await hasAdminSeatsAvaialble() && !user.isActive && data.isActive) {
84
85
  throw new ForbiddenError('License seat limit reached. You cannot active this user');
85
86
  }
86
- const updatedUser = await index.getService('user').updateById(id, input);
87
+ const updatedUser = await index.getService('user').updateById(id, data);
87
88
  if (!updatedUser) {
88
89
  return ctx.notFound('User does not exist');
89
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","sources":["../../../../../../ee/server/src/controllers/user.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport _ from 'lodash';\nimport { pick, isNil } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport { validateUserCreationInput } from '../validation/user';\nimport { validateUserUpdateInput } from '../../../../server/src/validation/user';\nimport { getService } from '../utils';\nimport { isSsoLocked } from '../utils/sso-lock';\n\nconst { ApplicationError, ForbiddenError } = errors;\n\nconst pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);\n\nconst hasAdminSeatsAvaialble = async () => {\n if (!strapi.EE) {\n return true;\n }\n\n const permittedSeats = strapi.ee.seats as any;\n if (isNil(permittedSeats)) {\n return true;\n }\n\n const userCount = await strapi.service('admin::user').getCurrentActiveUserCount();\n\n if (userCount < permittedSeats) {\n return true;\n }\n};\n\nexport default {\n async create(ctx: Context) {\n if (!(await hasAdminSeatsAvaialble())) {\n throw new ForbiddenError('License seat limit reached. You cannot create a new user');\n }\n\n const { body } = ctx.request;\n const cleanData = { ...body, email: _.get(body, `email`, ``).toLowerCase() };\n\n await validateUserCreationInput(cleanData);\n\n const attributes = pickUserCreationAttributes(cleanData);\n const { useSSORegistration } = cleanData;\n\n const userAlreadyExists = await getService('user').exists({ email: attributes.email });\n\n if (userAlreadyExists) {\n throw new ApplicationError('Email already taken');\n }\n\n if (useSSORegistration) {\n Object.assign(attributes, { registrationToken: null, isActive: true });\n }\n\n const createdUser = await getService('user').create(attributes);\n const userInfo = getService('user').sanitizeUser(createdUser);\n\n // Note: We need to assign manually the registrationToken to the\n // final user payload so that it's not removed in the sanitation process.\n Object.assign(userInfo, { registrationToken: createdUser.registrationToken });\n\n ctx.created({ data: userInfo });\n },\n\n async update(ctx: Context) {\n const { id } = ctx.params;\n const { body: input } = ctx.request;\n\n await validateUserUpdateInput(input);\n\n if (_.has(input, 'email')) {\n const uniqueEmailCheck = await getService('user').exists({\n id: { $ne: id },\n email: input.email,\n });\n\n if (uniqueEmailCheck) {\n throw new ApplicationError('A user with this email address already exists');\n }\n }\n\n const user = await getService('user').findOne(id, null);\n\n if (!(await hasAdminSeatsAvaialble()) && !user.isActive && input.isActive) {\n throw new ForbiddenError('License seat limit reached. You cannot active this user');\n }\n\n const updatedUser = await getService('user').updateById(id, input);\n\n if (!updatedUser) {\n return ctx.notFound('User does not exist');\n }\n\n ctx.body = {\n data: getService('user').sanitizeUser(updatedUser),\n };\n },\n\n async isSSOLocked(ctx: Context) {\n const { user } = ctx.state;\n const isSSOLocked = await isSsoLocked(user);\n\n ctx.body = {\n data: {\n isSSOLocked,\n },\n };\n },\n};\n"],"names":["ApplicationError","ForbiddenError","errors","pickUserCreationAttributes","pick","hasAdminSeatsAvaialble","strapi","EE","permittedSeats","ee","seats","isNil","userCount","service","getCurrentActiveUserCount","create","ctx","body","request","cleanData","email","_","get","toLowerCase","validateUserCreationInput","attributes","useSSORegistration","userAlreadyExists","getService","exists","Object","assign","registrationToken","isActive","createdUser","userInfo","sanitizeUser","created","data","update","id","params","input","validateUserUpdateInput","has","uniqueEmailCheck","$ne","user","findOne","updatedUser","updateById","notFound","isSSOLocked","state","isSsoLocked"],"mappings":"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"}
1
+ {"version":3,"file":"user.js","sources":["../../../../../../ee/server/src/controllers/user.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport _ from 'lodash';\nimport { pick, isNil } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport { validateUserCreationInput } from '../validation/user';\nimport { validateUserUpdateInput } from '../../../../server/src/validation/user';\nimport { normalizeEmail } from '../../../../server/src/utils/normalize-email';\nimport { getService } from '../utils';\nimport { isSsoLocked } from '../utils/sso-lock';\n\nconst { ApplicationError, ForbiddenError } = errors;\n\nconst pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);\n\nconst hasAdminSeatsAvaialble = async () => {\n if (!strapi.EE) {\n return true;\n }\n\n const permittedSeats = strapi.ee.seats as any;\n if (isNil(permittedSeats)) {\n return true;\n }\n\n const userCount = await strapi.service('admin::user').getCurrentActiveUserCount();\n\n if (userCount < permittedSeats) {\n return true;\n }\n};\n\nexport default {\n async create(ctx: Context) {\n if (!(await hasAdminSeatsAvaialble())) {\n throw new ForbiddenError('License seat limit reached. You cannot create a new user');\n }\n\n const { body } = ctx.request;\n const cleanData = { ...body, email: _.get(body, `email`, ``).toLowerCase() };\n\n await validateUserCreationInput(cleanData);\n\n const attributes = pickUserCreationAttributes(cleanData);\n const { useSSORegistration } = cleanData;\n\n const userAlreadyExists = await getService('user').exists({ email: attributes.email });\n\n if (userAlreadyExists) {\n throw new ApplicationError('Email already taken');\n }\n\n if (useSSORegistration) {\n Object.assign(attributes, { registrationToken: null, isActive: true });\n }\n\n const createdUser = await getService('user').create(attributes);\n const userInfo = getService('user').sanitizeUser(createdUser);\n\n // Note: We need to assign manually the registrationToken to the\n // final user payload so that it's not removed in the sanitation process.\n Object.assign(userInfo, { registrationToken: createdUser.registrationToken });\n\n ctx.created({ data: userInfo });\n },\n\n async update(ctx: Context) {\n const { id } = ctx.params;\n const data = normalizeEmail(ctx.request.body);\n\n await validateUserUpdateInput(data);\n\n if (_.has(data, 'email')) {\n const uniqueEmailCheck = await getService('user').exists({\n id: { $ne: id },\n email: data.email,\n });\n\n if (uniqueEmailCheck) {\n throw new ApplicationError('A user with this email address already exists');\n }\n }\n\n const user = await getService('user').findOne(id, null);\n\n if (!(await hasAdminSeatsAvaialble()) && !user.isActive && data.isActive) {\n throw new ForbiddenError('License seat limit reached. You cannot active this user');\n }\n\n const updatedUser = await getService('user').updateById(id, data);\n\n if (!updatedUser) {\n return ctx.notFound('User does not exist');\n }\n\n ctx.body = {\n data: getService('user').sanitizeUser(updatedUser),\n };\n },\n\n async isSSOLocked(ctx: Context) {\n const { user } = ctx.state;\n const isSSOLocked = await isSsoLocked(user);\n\n ctx.body = {\n data: {\n isSSOLocked,\n },\n };\n },\n};\n"],"names":["ApplicationError","ForbiddenError","errors","pickUserCreationAttributes","pick","hasAdminSeatsAvaialble","strapi","EE","permittedSeats","ee","seats","isNil","userCount","service","getCurrentActiveUserCount","create","ctx","body","request","cleanData","email","_","get","toLowerCase","validateUserCreationInput","attributes","useSSORegistration","userAlreadyExists","getService","exists","Object","assign","registrationToken","isActive","createdUser","userInfo","sanitizeUser","created","data","update","id","params","normalizeEmail","validateUserUpdateInput","has","uniqueEmailCheck","$ne","user","findOne","updatedUser","updateById","notFound","isSSOLocked","state","isSsoLocked"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { pick, isNil } from 'lodash/fp';
3
3
  import { errors } from '@strapi/utils';
4
4
  import { validateUserCreationInput } from '../validation/user.mjs';
5
5
  import { validateUserUpdateInput } from '../../../../server/src/validation/user.mjs';
6
+ import { normalizeEmail } from '../../../../server/src/utils/normalize-email.mjs';
6
7
  import { getService } from '../utils/index.mjs';
7
8
  import { isSsoLocked } from '../utils/sso-lock.mjs';
8
9
 
@@ -64,24 +65,24 @@ var user = {
64
65
  },
65
66
  async update (ctx) {
66
67
  const { id } = ctx.params;
67
- const { body: input } = ctx.request;
68
- await validateUserUpdateInput(input);
69
- if (___default.has(input, 'email')) {
68
+ const data = normalizeEmail(ctx.request.body);
69
+ await validateUserUpdateInput(data);
70
+ if (___default.has(data, 'email')) {
70
71
  const uniqueEmailCheck = await getService('user').exists({
71
72
  id: {
72
73
  $ne: id
73
74
  },
74
- email: input.email
75
+ email: data.email
75
76
  });
76
77
  if (uniqueEmailCheck) {
77
78
  throw new ApplicationError('A user with this email address already exists');
78
79
  }
79
80
  }
80
81
  const user = await getService('user').findOne(id, null);
81
- if (!await hasAdminSeatsAvaialble() && !user.isActive && input.isActive) {
82
+ if (!await hasAdminSeatsAvaialble() && !user.isActive && data.isActive) {
82
83
  throw new ForbiddenError('License seat limit reached. You cannot active this user');
83
84
  }
84
- const updatedUser = await getService('user').updateById(id, input);
85
+ const updatedUser = await getService('user').updateById(id, data);
85
86
  if (!updatedUser) {
86
87
  return ctx.notFound('User does not exist');
87
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"user.mjs","sources":["../../../../../../ee/server/src/controllers/user.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport _ from 'lodash';\nimport { pick, isNil } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport { validateUserCreationInput } from '../validation/user';\nimport { validateUserUpdateInput } from '../../../../server/src/validation/user';\nimport { getService } from '../utils';\nimport { isSsoLocked } from '../utils/sso-lock';\n\nconst { ApplicationError, ForbiddenError } = errors;\n\nconst pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);\n\nconst hasAdminSeatsAvaialble = async () => {\n if (!strapi.EE) {\n return true;\n }\n\n const permittedSeats = strapi.ee.seats as any;\n if (isNil(permittedSeats)) {\n return true;\n }\n\n const userCount = await strapi.service('admin::user').getCurrentActiveUserCount();\n\n if (userCount < permittedSeats) {\n return true;\n }\n};\n\nexport default {\n async create(ctx: Context) {\n if (!(await hasAdminSeatsAvaialble())) {\n throw new ForbiddenError('License seat limit reached. You cannot create a new user');\n }\n\n const { body } = ctx.request;\n const cleanData = { ...body, email: _.get(body, `email`, ``).toLowerCase() };\n\n await validateUserCreationInput(cleanData);\n\n const attributes = pickUserCreationAttributes(cleanData);\n const { useSSORegistration } = cleanData;\n\n const userAlreadyExists = await getService('user').exists({ email: attributes.email });\n\n if (userAlreadyExists) {\n throw new ApplicationError('Email already taken');\n }\n\n if (useSSORegistration) {\n Object.assign(attributes, { registrationToken: null, isActive: true });\n }\n\n const createdUser = await getService('user').create(attributes);\n const userInfo = getService('user').sanitizeUser(createdUser);\n\n // Note: We need to assign manually the registrationToken to the\n // final user payload so that it's not removed in the sanitation process.\n Object.assign(userInfo, { registrationToken: createdUser.registrationToken });\n\n ctx.created({ data: userInfo });\n },\n\n async update(ctx: Context) {\n const { id } = ctx.params;\n const { body: input } = ctx.request;\n\n await validateUserUpdateInput(input);\n\n if (_.has(input, 'email')) {\n const uniqueEmailCheck = await getService('user').exists({\n id: { $ne: id },\n email: input.email,\n });\n\n if (uniqueEmailCheck) {\n throw new ApplicationError('A user with this email address already exists');\n }\n }\n\n const user = await getService('user').findOne(id, null);\n\n if (!(await hasAdminSeatsAvaialble()) && !user.isActive && input.isActive) {\n throw new ForbiddenError('License seat limit reached. You cannot active this user');\n }\n\n const updatedUser = await getService('user').updateById(id, input);\n\n if (!updatedUser) {\n return ctx.notFound('User does not exist');\n }\n\n ctx.body = {\n data: getService('user').sanitizeUser(updatedUser),\n };\n },\n\n async isSSOLocked(ctx: Context) {\n const { user } = ctx.state;\n const isSSOLocked = await isSsoLocked(user);\n\n ctx.body = {\n data: {\n isSSOLocked,\n },\n };\n },\n};\n"],"names":["ApplicationError","ForbiddenError","errors","pickUserCreationAttributes","pick","hasAdminSeatsAvaialble","strapi","EE","permittedSeats","ee","seats","isNil","userCount","service","getCurrentActiveUserCount","create","ctx","body","request","cleanData","email","_","get","toLowerCase","validateUserCreationInput","attributes","useSSORegistration","userAlreadyExists","getService","exists","Object","assign","registrationToken","isActive","createdUser","userInfo","sanitizeUser","created","data","update","id","params","input","validateUserUpdateInput","has","uniqueEmailCheck","$ne","user","findOne","updatedUser","updateById","notFound","isSSOLocked","state","isSsoLocked"],"mappings":"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"}
1
+ {"version":3,"file":"user.mjs","sources":["../../../../../../ee/server/src/controllers/user.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport _ from 'lodash';\nimport { pick, isNil } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport { validateUserCreationInput } from '../validation/user';\nimport { validateUserUpdateInput } from '../../../../server/src/validation/user';\nimport { normalizeEmail } from '../../../../server/src/utils/normalize-email';\nimport { getService } from '../utils';\nimport { isSsoLocked } from '../utils/sso-lock';\n\nconst { ApplicationError, ForbiddenError } = errors;\n\nconst pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);\n\nconst hasAdminSeatsAvaialble = async () => {\n if (!strapi.EE) {\n return true;\n }\n\n const permittedSeats = strapi.ee.seats as any;\n if (isNil(permittedSeats)) {\n return true;\n }\n\n const userCount = await strapi.service('admin::user').getCurrentActiveUserCount();\n\n if (userCount < permittedSeats) {\n return true;\n }\n};\n\nexport default {\n async create(ctx: Context) {\n if (!(await hasAdminSeatsAvaialble())) {\n throw new ForbiddenError('License seat limit reached. You cannot create a new user');\n }\n\n const { body } = ctx.request;\n const cleanData = { ...body, email: _.get(body, `email`, ``).toLowerCase() };\n\n await validateUserCreationInput(cleanData);\n\n const attributes = pickUserCreationAttributes(cleanData);\n const { useSSORegistration } = cleanData;\n\n const userAlreadyExists = await getService('user').exists({ email: attributes.email });\n\n if (userAlreadyExists) {\n throw new ApplicationError('Email already taken');\n }\n\n if (useSSORegistration) {\n Object.assign(attributes, { registrationToken: null, isActive: true });\n }\n\n const createdUser = await getService('user').create(attributes);\n const userInfo = getService('user').sanitizeUser(createdUser);\n\n // Note: We need to assign manually the registrationToken to the\n // final user payload so that it's not removed in the sanitation process.\n Object.assign(userInfo, { registrationToken: createdUser.registrationToken });\n\n ctx.created({ data: userInfo });\n },\n\n async update(ctx: Context) {\n const { id } = ctx.params;\n const data = normalizeEmail(ctx.request.body);\n\n await validateUserUpdateInput(data);\n\n if (_.has(data, 'email')) {\n const uniqueEmailCheck = await getService('user').exists({\n id: { $ne: id },\n email: data.email,\n });\n\n if (uniqueEmailCheck) {\n throw new ApplicationError('A user with this email address already exists');\n }\n }\n\n const user = await getService('user').findOne(id, null);\n\n if (!(await hasAdminSeatsAvaialble()) && !user.isActive && data.isActive) {\n throw new ForbiddenError('License seat limit reached. You cannot active this user');\n }\n\n const updatedUser = await getService('user').updateById(id, data);\n\n if (!updatedUser) {\n return ctx.notFound('User does not exist');\n }\n\n ctx.body = {\n data: getService('user').sanitizeUser(updatedUser),\n };\n },\n\n async isSSOLocked(ctx: Context) {\n const { user } = ctx.state;\n const isSSOLocked = await isSsoLocked(user);\n\n ctx.body = {\n data: {\n isSSOLocked,\n },\n };\n },\n};\n"],"names":["ApplicationError","ForbiddenError","errors","pickUserCreationAttributes","pick","hasAdminSeatsAvaialble","strapi","EE","permittedSeats","ee","seats","isNil","userCount","service","getCurrentActiveUserCount","create","ctx","body","request","cleanData","email","_","get","toLowerCase","validateUserCreationInput","attributes","useSSORegistration","userAlreadyExists","getService","exists","Object","assign","registrationToken","isActive","createdUser","userInfo","sanitizeUser","created","data","update","id","params","normalizeEmail","validateUserUpdateInput","has","uniqueEmailCheck","$ne","user","findOne","updatedUser","updateById","notFound","isSSOLocked","state","isSsoLocked"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('../utils/index.js');
4
+ var normalizeEmail = require('../utils/normalize-email.js');
4
5
  var user = require('../validation/user.js');
5
6
  var sessionAuth = require('../../../shared/utils/session-auth.js');
6
7
 
@@ -12,11 +13,12 @@ var authenticatedUser = {
12
13
  };
13
14
  },
14
15
  async updateMe (ctx) {
15
- const input = ctx.request.body;
16
- await user.validateProfileUpdateInput(input);
16
+ const data = normalizeEmail.normalizeEmail(ctx.request.body);
17
+ await user.validateProfileUpdateInput(data);
17
18
  const userService = index.getService('user');
18
19
  const authServer = index.getService('auth');
19
- const { currentPassword, ...userInfo } = input;
20
+ const { currentPassword, ...userInfo } = data;
21
+ const isChangingPassword = Boolean(currentPassword && userInfo.password);
20
22
  if (currentPassword && userInfo.password) {
21
23
  const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);
22
24
  if (!isValid) {
@@ -26,7 +28,26 @@ var authenticatedUser = {
26
28
  ]
27
29
  });
28
30
  }
29
- // Invalidate all sessions when password changes for security
31
+ }
32
+ if (userInfo.email !== undefined) {
33
+ const emailAlreadyTaken = await userService.exists({
34
+ id: {
35
+ $ne: ctx.state.user.id
36
+ },
37
+ email: userInfo.email
38
+ });
39
+ if (emailAlreadyTaken === true) {
40
+ return ctx.badRequest('ValidationError', {
41
+ email: [
42
+ 'Email already taken'
43
+ ]
44
+ });
45
+ }
46
+ }
47
+ // Invalidate all sessions when password changes for security. This must run only once the
48
+ // update is going to be persisted, so a rejected request (e.g. duplicate email) does not log
49
+ // the user out without applying any change.
50
+ if (isChangingPassword) {
30
51
  const sessionManager = sessionAuth.getSessionManager();
31
52
  if (sessionManager && sessionManager.hasOrigin('admin')) {
32
53
  await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));
@@ -1 +1 @@
1
- {"version":3,"file":"authenticated-user.js","sources":["../../../../../server/src/controllers/authenticated-user.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport type { AdminUser } from '../../../shared/contracts/shared';\n\nimport { getService } from '../utils';\nimport { validateProfileUpdateInput } from '../validation/user';\nimport { GetMe, GetOwnPermissions, UpdateMe } from '../../../shared/contracts/users';\nimport { getSessionManager } from '../../../shared/utils/session-auth';\n\nexport default {\n async getMe(ctx: Context) {\n const userInfo = getService('user').sanitizeUser(ctx.state.user as AdminUser);\n\n ctx.body = {\n data: userInfo,\n } satisfies GetMe.Response;\n },\n\n async updateMe(ctx: Context) {\n const input = ctx.request.body as UpdateMe.Request['body'];\n\n await validateProfileUpdateInput(input);\n\n const userService = getService('user');\n const authServer = getService('auth');\n\n const { currentPassword, ...userInfo } = input;\n\n if (currentPassword && userInfo.password) {\n const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);\n\n if (!isValid) {\n return ctx.badRequest('ValidationError', {\n currentPassword: ['Invalid credentials'],\n });\n }\n\n // Invalidate all sessions when password changes for security\n const sessionManager = getSessionManager();\n if (sessionManager && sessionManager.hasOrigin('admin')) {\n await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));\n }\n }\n\n const updatedUser = await userService.updateById(ctx.state.user.id, userInfo);\n\n ctx.body = {\n data: userService.sanitizeUser(updatedUser),\n } satisfies UpdateMe.Response;\n },\n\n async getOwnPermissions(ctx: Context) {\n const { findUserPermissions, sanitizePermission } = getService('permission');\n const { user } = ctx.state;\n\n const userPermissions = await findUserPermissions(user as AdminUser);\n\n ctx.body = {\n // @ts-expect-error - transform response type to sanitized permission\n data: userPermissions.map(sanitizePermission),\n } satisfies GetOwnPermissions.Response;\n },\n};\n"],"names":["getMe","ctx","userInfo","getService","sanitizeUser","state","user","body","data","updateMe","input","request","validateProfileUpdateInput","userService","authServer","currentPassword","password","isValid","validatePassword","badRequest","sessionManager","getSessionManager","hasOrigin","invalidateRefreshToken","String","id","updatedUser","updateById","getOwnPermissions","findUserPermissions","sanitizePermission","userPermissions","map"],"mappings":";;;;;;AAQA,wBAAe;AACb,IAAA,MAAMA,OAAMC,GAAY,EAAA;QACtB,MAAMC,QAAAA,GAAWC,iBAAW,MAAA,CAAA,CAAQC,YAAY,CAACH,GAAAA,CAAII,KAAK,CAACC,IAAI,CAAA;AAE/DL,QAAAA,GAAAA,CAAIM,IAAI,GAAG;YACTC,IAAAA,EAAMN;AACR,SAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMO,UAASR,GAAY,EAAA;AACzB,QAAA,MAAMS,KAAAA,GAAQT,GAAAA,CAAIU,OAAO,CAACJ,IAAI;AAE9B,QAAA,MAAMK,+BAAAA,CAA2BF,KAAAA,CAAAA;AAEjC,QAAA,MAAMG,cAAcV,gBAAAA,CAAW,MAAA,CAAA;AAC/B,QAAA,MAAMW,aAAaX,gBAAAA,CAAW,MAAA,CAAA;AAE9B,QAAA,MAAM,EAAEY,eAAe,EAAE,GAAGb,UAAU,GAAGQ,KAAAA;QAEzC,IAAIK,eAAAA,IAAmBb,QAAAA,CAASc,QAAQ,EAAE;YACxC,MAAMC,OAAAA,GAAU,MAAMH,UAAAA,CAAWI,gBAAgB,CAACH,eAAAA,EAAiBd,GAAAA,CAAII,KAAK,CAACC,IAAI,CAACU,QAAQ,CAAA;AAE1F,YAAA,IAAI,CAACC,OAAAA,EAAS;gBACZ,OAAOhB,GAAAA,CAAIkB,UAAU,CAAC,iBAAA,EAAmB;oBACvCJ,eAAAA,EAAiB;AAAC,wBAAA;AAAsB;AAC1C,iBAAA,CAAA;AACF,YAAA;;AAGA,YAAA,MAAMK,cAAAA,GAAiBC,6BAAAA,EAAAA;AACvB,YAAA,IAAID,cAAAA,IAAkBA,cAAAA,CAAeE,SAAS,CAAC,OAAA,CAAA,EAAU;gBACvD,MAAMF,cAAAA,CAAe,OAAA,CAAA,CAASG,sBAAsB,CAACC,MAAAA,CAAOvB,IAAII,KAAK,CAACC,IAAI,CAACmB,EAAE,CAAA,CAAA;AAC/E,YAAA;AACF,QAAA;QAEA,MAAMC,WAAAA,GAAc,MAAMb,WAAAA,CAAYc,UAAU,CAAC1B,GAAAA,CAAII,KAAK,CAACC,IAAI,CAACmB,EAAE,EAAEvB,QAAAA,CAAAA;AAEpED,QAAAA,GAAAA,CAAIM,IAAI,GAAG;YACTC,IAAAA,EAAMK,WAAAA,CAAYT,YAAY,CAACsB,WAAAA;AACjC,SAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAME,mBAAkB3B,GAAY,EAAA;AAClC,QAAA,MAAM,EAAE4B,mBAAmB,EAAEC,kBAAkB,EAAE,GAAG3B,gBAAAA,CAAW,YAAA,CAAA;AAC/D,QAAA,MAAM,EAAEG,IAAI,EAAE,GAAGL,IAAII,KAAK;QAE1B,MAAM0B,eAAAA,GAAkB,MAAMF,mBAAAA,CAAoBvB,IAAAA,CAAAA;AAElDL,QAAAA,GAAAA,CAAIM,IAAI,GAAG;;YAETC,IAAAA,EAAMuB,eAAAA,CAAgBC,GAAG,CAACF,kBAAAA;AAC5B,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"authenticated-user.js","sources":["../../../../../server/src/controllers/authenticated-user.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport type { AdminUser } from '../../../shared/contracts/shared';\n\nimport { getService } from '../utils';\nimport { normalizeEmail } from '../utils/normalize-email';\nimport { validateProfileUpdateInput } from '../validation/user';\nimport { GetMe, GetOwnPermissions, UpdateMe } from '../../../shared/contracts/users';\nimport { getSessionManager } from '../../../shared/utils/session-auth';\n\nexport default {\n async getMe(ctx: Context) {\n const userInfo = getService('user').sanitizeUser(ctx.state.user as AdminUser);\n\n ctx.body = {\n data: userInfo,\n } satisfies GetMe.Response;\n },\n\n async updateMe(ctx: Context) {\n const data = normalizeEmail(ctx.request.body as UpdateMe.Request['body']);\n\n await validateProfileUpdateInput(data);\n\n const userService = getService('user');\n const authServer = getService('auth');\n\n const { currentPassword, ...userInfo } = data;\n\n const isChangingPassword = Boolean(currentPassword && userInfo.password);\n\n if (currentPassword && userInfo.password) {\n const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);\n\n if (!isValid) {\n return ctx.badRequest('ValidationError', {\n currentPassword: ['Invalid credentials'],\n });\n }\n }\n\n if (userInfo.email !== undefined) {\n const emailAlreadyTaken = await userService.exists({\n id: { $ne: ctx.state.user.id },\n email: userInfo.email,\n });\n\n if (emailAlreadyTaken === true) {\n return ctx.badRequest('ValidationError', {\n email: ['Email already taken'],\n });\n }\n }\n\n // Invalidate all sessions when password changes for security. This must run only once the\n // update is going to be persisted, so a rejected request (e.g. duplicate email) does not log\n // the user out without applying any change.\n if (isChangingPassword) {\n const sessionManager = getSessionManager();\n if (sessionManager && sessionManager.hasOrigin('admin')) {\n await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));\n }\n }\n\n const updatedUser = await userService.updateById(ctx.state.user.id, userInfo);\n\n ctx.body = {\n data: userService.sanitizeUser(updatedUser),\n } satisfies UpdateMe.Response;\n },\n\n async getOwnPermissions(ctx: Context) {\n const { findUserPermissions, sanitizePermission } = getService('permission');\n const { user } = ctx.state;\n\n const userPermissions = await findUserPermissions(user as AdminUser);\n\n ctx.body = {\n // @ts-expect-error - transform response type to sanitized permission\n data: userPermissions.map(sanitizePermission),\n } satisfies GetOwnPermissions.Response;\n },\n};\n"],"names":["getMe","ctx","userInfo","getService","sanitizeUser","state","user","body","data","updateMe","normalizeEmail","request","validateProfileUpdateInput","userService","authServer","currentPassword","isChangingPassword","Boolean","password","isValid","validatePassword","badRequest","email","undefined","emailAlreadyTaken","exists","id","$ne","sessionManager","getSessionManager","hasOrigin","invalidateRefreshToken","String","updatedUser","updateById","getOwnPermissions","findUserPermissions","sanitizePermission","userPermissions","map"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { getService } from '../utils/index.mjs';
2
+ import { normalizeEmail } from '../utils/normalize-email.mjs';
2
3
  import { validateProfileUpdateInput } from '../validation/user.mjs';
3
4
  import { getSessionManager } from '../../../shared/utils/session-auth.mjs';
4
5
 
@@ -10,11 +11,12 @@ var authenticatedUser = {
10
11
  };
11
12
  },
12
13
  async updateMe (ctx) {
13
- const input = ctx.request.body;
14
- await validateProfileUpdateInput(input);
14
+ const data = normalizeEmail(ctx.request.body);
15
+ await validateProfileUpdateInput(data);
15
16
  const userService = getService('user');
16
17
  const authServer = getService('auth');
17
- const { currentPassword, ...userInfo } = input;
18
+ const { currentPassword, ...userInfo } = data;
19
+ const isChangingPassword = Boolean(currentPassword && userInfo.password);
18
20
  if (currentPassword && userInfo.password) {
19
21
  const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);
20
22
  if (!isValid) {
@@ -24,7 +26,26 @@ var authenticatedUser = {
24
26
  ]
25
27
  });
26
28
  }
27
- // Invalidate all sessions when password changes for security
29
+ }
30
+ if (userInfo.email !== undefined) {
31
+ const emailAlreadyTaken = await userService.exists({
32
+ id: {
33
+ $ne: ctx.state.user.id
34
+ },
35
+ email: userInfo.email
36
+ });
37
+ if (emailAlreadyTaken === true) {
38
+ return ctx.badRequest('ValidationError', {
39
+ email: [
40
+ 'Email already taken'
41
+ ]
42
+ });
43
+ }
44
+ }
45
+ // Invalidate all sessions when password changes for security. This must run only once the
46
+ // update is going to be persisted, so a rejected request (e.g. duplicate email) does not log
47
+ // the user out without applying any change.
48
+ if (isChangingPassword) {
28
49
  const sessionManager = getSessionManager();
29
50
  if (sessionManager && sessionManager.hasOrigin('admin')) {
30
51
  await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));
@@ -1 +1 @@
1
- {"version":3,"file":"authenticated-user.mjs","sources":["../../../../../server/src/controllers/authenticated-user.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport type { AdminUser } from '../../../shared/contracts/shared';\n\nimport { getService } from '../utils';\nimport { validateProfileUpdateInput } from '../validation/user';\nimport { GetMe, GetOwnPermissions, UpdateMe } from '../../../shared/contracts/users';\nimport { getSessionManager } from '../../../shared/utils/session-auth';\n\nexport default {\n async getMe(ctx: Context) {\n const userInfo = getService('user').sanitizeUser(ctx.state.user as AdminUser);\n\n ctx.body = {\n data: userInfo,\n } satisfies GetMe.Response;\n },\n\n async updateMe(ctx: Context) {\n const input = ctx.request.body as UpdateMe.Request['body'];\n\n await validateProfileUpdateInput(input);\n\n const userService = getService('user');\n const authServer = getService('auth');\n\n const { currentPassword, ...userInfo } = input;\n\n if (currentPassword && userInfo.password) {\n const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);\n\n if (!isValid) {\n return ctx.badRequest('ValidationError', {\n currentPassword: ['Invalid credentials'],\n });\n }\n\n // Invalidate all sessions when password changes for security\n const sessionManager = getSessionManager();\n if (sessionManager && sessionManager.hasOrigin('admin')) {\n await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));\n }\n }\n\n const updatedUser = await userService.updateById(ctx.state.user.id, userInfo);\n\n ctx.body = {\n data: userService.sanitizeUser(updatedUser),\n } satisfies UpdateMe.Response;\n },\n\n async getOwnPermissions(ctx: Context) {\n const { findUserPermissions, sanitizePermission } = getService('permission');\n const { user } = ctx.state;\n\n const userPermissions = await findUserPermissions(user as AdminUser);\n\n ctx.body = {\n // @ts-expect-error - transform response type to sanitized permission\n data: userPermissions.map(sanitizePermission),\n } satisfies GetOwnPermissions.Response;\n },\n};\n"],"names":["getMe","ctx","userInfo","getService","sanitizeUser","state","user","body","data","updateMe","input","request","validateProfileUpdateInput","userService","authServer","currentPassword","password","isValid","validatePassword","badRequest","sessionManager","getSessionManager","hasOrigin","invalidateRefreshToken","String","id","updatedUser","updateById","getOwnPermissions","findUserPermissions","sanitizePermission","userPermissions","map"],"mappings":";;;;AAQA,wBAAe;AACb,IAAA,MAAMA,OAAMC,GAAY,EAAA;QACtB,MAAMC,QAAAA,GAAWC,WAAW,MAAA,CAAA,CAAQC,YAAY,CAACH,GAAAA,CAAII,KAAK,CAACC,IAAI,CAAA;AAE/DL,QAAAA,GAAAA,CAAIM,IAAI,GAAG;YACTC,IAAAA,EAAMN;AACR,SAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMO,UAASR,GAAY,EAAA;AACzB,QAAA,MAAMS,KAAAA,GAAQT,GAAAA,CAAIU,OAAO,CAACJ,IAAI;AAE9B,QAAA,MAAMK,0BAAAA,CAA2BF,KAAAA,CAAAA;AAEjC,QAAA,MAAMG,cAAcV,UAAAA,CAAW,MAAA,CAAA;AAC/B,QAAA,MAAMW,aAAaX,UAAAA,CAAW,MAAA,CAAA;AAE9B,QAAA,MAAM,EAAEY,eAAe,EAAE,GAAGb,UAAU,GAAGQ,KAAAA;QAEzC,IAAIK,eAAAA,IAAmBb,QAAAA,CAASc,QAAQ,EAAE;YACxC,MAAMC,OAAAA,GAAU,MAAMH,UAAAA,CAAWI,gBAAgB,CAACH,eAAAA,EAAiBd,GAAAA,CAAII,KAAK,CAACC,IAAI,CAACU,QAAQ,CAAA;AAE1F,YAAA,IAAI,CAACC,OAAAA,EAAS;gBACZ,OAAOhB,GAAAA,CAAIkB,UAAU,CAAC,iBAAA,EAAmB;oBACvCJ,eAAAA,EAAiB;AAAC,wBAAA;AAAsB;AAC1C,iBAAA,CAAA;AACF,YAAA;;AAGA,YAAA,MAAMK,cAAAA,GAAiBC,iBAAAA,EAAAA;AACvB,YAAA,IAAID,cAAAA,IAAkBA,cAAAA,CAAeE,SAAS,CAAC,OAAA,CAAA,EAAU;gBACvD,MAAMF,cAAAA,CAAe,OAAA,CAAA,CAASG,sBAAsB,CAACC,MAAAA,CAAOvB,IAAII,KAAK,CAACC,IAAI,CAACmB,EAAE,CAAA,CAAA;AAC/E,YAAA;AACF,QAAA;QAEA,MAAMC,WAAAA,GAAc,MAAMb,WAAAA,CAAYc,UAAU,CAAC1B,GAAAA,CAAII,KAAK,CAACC,IAAI,CAACmB,EAAE,EAAEvB,QAAAA,CAAAA;AAEpED,QAAAA,GAAAA,CAAIM,IAAI,GAAG;YACTC,IAAAA,EAAMK,WAAAA,CAAYT,YAAY,CAACsB,WAAAA;AACjC,SAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAME,mBAAkB3B,GAAY,EAAA;AAClC,QAAA,MAAM,EAAE4B,mBAAmB,EAAEC,kBAAkB,EAAE,GAAG3B,UAAAA,CAAW,YAAA,CAAA;AAC/D,QAAA,MAAM,EAAEG,IAAI,EAAE,GAAGL,IAAII,KAAK;QAE1B,MAAM0B,eAAAA,GAAkB,MAAMF,mBAAAA,CAAoBvB,IAAAA,CAAAA;AAElDL,QAAAA,GAAAA,CAAIM,IAAI,GAAG;;YAETC,IAAAA,EAAMuB,eAAAA,CAAgBC,GAAG,CAACF,kBAAAA;AAC5B,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"authenticated-user.mjs","sources":["../../../../../server/src/controllers/authenticated-user.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport type { AdminUser } from '../../../shared/contracts/shared';\n\nimport { getService } from '../utils';\nimport { normalizeEmail } from '../utils/normalize-email';\nimport { validateProfileUpdateInput } from '../validation/user';\nimport { GetMe, GetOwnPermissions, UpdateMe } from '../../../shared/contracts/users';\nimport { getSessionManager } from '../../../shared/utils/session-auth';\n\nexport default {\n async getMe(ctx: Context) {\n const userInfo = getService('user').sanitizeUser(ctx.state.user as AdminUser);\n\n ctx.body = {\n data: userInfo,\n } satisfies GetMe.Response;\n },\n\n async updateMe(ctx: Context) {\n const data = normalizeEmail(ctx.request.body as UpdateMe.Request['body']);\n\n await validateProfileUpdateInput(data);\n\n const userService = getService('user');\n const authServer = getService('auth');\n\n const { currentPassword, ...userInfo } = data;\n\n const isChangingPassword = Boolean(currentPassword && userInfo.password);\n\n if (currentPassword && userInfo.password) {\n const isValid = await authServer.validatePassword(currentPassword, ctx.state.user.password);\n\n if (!isValid) {\n return ctx.badRequest('ValidationError', {\n currentPassword: ['Invalid credentials'],\n });\n }\n }\n\n if (userInfo.email !== undefined) {\n const emailAlreadyTaken = await userService.exists({\n id: { $ne: ctx.state.user.id },\n email: userInfo.email,\n });\n\n if (emailAlreadyTaken === true) {\n return ctx.badRequest('ValidationError', {\n email: ['Email already taken'],\n });\n }\n }\n\n // Invalidate all sessions when password changes for security. This must run only once the\n // update is going to be persisted, so a rejected request (e.g. duplicate email) does not log\n // the user out without applying any change.\n if (isChangingPassword) {\n const sessionManager = getSessionManager();\n if (sessionManager && sessionManager.hasOrigin('admin')) {\n await sessionManager('admin').invalidateRefreshToken(String(ctx.state.user.id));\n }\n }\n\n const updatedUser = await userService.updateById(ctx.state.user.id, userInfo);\n\n ctx.body = {\n data: userService.sanitizeUser(updatedUser),\n } satisfies UpdateMe.Response;\n },\n\n async getOwnPermissions(ctx: Context) {\n const { findUserPermissions, sanitizePermission } = getService('permission');\n const { user } = ctx.state;\n\n const userPermissions = await findUserPermissions(user as AdminUser);\n\n ctx.body = {\n // @ts-expect-error - transform response type to sanitized permission\n data: userPermissions.map(sanitizePermission),\n } satisfies GetOwnPermissions.Response;\n },\n};\n"],"names":["getMe","ctx","userInfo","getService","sanitizeUser","state","user","body","data","updateMe","normalizeEmail","request","validateProfileUpdateInput","userService","authServer","currentPassword","isChangingPassword","Boolean","password","isValid","validatePassword","badRequest","email","undefined","emailAlreadyTaken","exists","id","$ne","sessionManager","getSessionManager","hasOrigin","invalidateRefreshToken","String","updatedUser","updateById","getOwnPermissions","findUserPermissions","sanitizePermission","userPermissions","map"],"mappings":"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"}
@@ -130,22 +130,7 @@ var authentication = {
130
130
  async registerAdmin (ctx) {
131
131
  const input = ctx.request.body;
132
132
  await register.validateAdminRegistrationInput(input);
133
- const hasAdmin = await index.getService('user').exists();
134
- if (hasAdmin) {
135
- throw new ApplicationError('You cannot register a new super admin');
136
- }
137
- const superAdminRole = await index.getService('role').getSuperAdmin();
138
- if (!superAdminRole) {
139
- throw new ApplicationError("Cannot register the first admin because the super admin role doesn't exist.");
140
- }
141
- const user = await index.getService('user').create({
142
- ...input,
143
- registrationToken: null,
144
- isActive: true,
145
- roles: superAdminRole ? [
146
- superAdminRole.id
147
- ] : []
148
- });
133
+ const user = await index.getService('user').createFirstAdmin(input);
149
134
  strapi.telemetry.send('didCreateFirstAdmin');
150
135
  try {
151
136
  const sessionManager = sessionAuth.getSessionManager();