strapi-plugin-oidc 1.8.4 → 1.9.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/README.md CHANGED
@@ -48,7 +48,7 @@ module.exports = ({ env }) => ({
48
48
  OIDC_GROUP_FIELD: 'groups', // OIDC claim field containing group membership
49
49
  OIDC_GROUP_ROLE_MAP: '{}', // JSON map of group names to Strapi role names
50
50
  OIDC_REQUIRE_EMAIL_VERIFIED: true, // Reject logins when provider does not report email_verified=true (set false to disable)
51
- OIDC_TRUSTED_IP_HEADER: '', // Optional: header set by your CDN/proxy containing the real client IP (see note below); only honoured when server.proxy: true
51
+ OIDC_TRUSTED_IP_HEADER: '', // Optional: header set by your CDN/proxy containing the real client IP (see note below); only honoured when Koa proxy mode is enabled (see below)
52
52
  OIDC_FORCE_SECURE_COOKIES: false, // Set true when behind a trusted HTTPS proxy that Strapi can't auto-detect
53
53
  },
54
54
  },
@@ -67,9 +67,17 @@ module.exports = ({ env }) => ({
67
67
 
68
68
  ### Client IP attribution and reverse proxies
69
69
 
70
- The plugin logs client IPs for rate-limit buckets and audit logs. When Strapi runs behind a reverse proxy, **set `server.proxy: true`** so Koa trusts `X-Forwarded-For`; otherwise all IPs will be the proxy's.
70
+ The plugin logs client IPs for rate-limit buckets and audit logs. When Strapi runs behind a reverse proxy, enable Koa proxy mode so Strapi trusts `X-Forwarded-For`; otherwise all IPs will be the proxy's internal address.
71
71
 
72
- Set `OIDC_TRUSTED_IP_HEADER` to the header your CDN or proxy uses to forward the real client IP. The header is only honoured when `server.proxy: true` is set. Accepted values (all others are silently ignored):
72
+ In `config/server.ts`:
73
+
74
+ ```ts
75
+ proxy: {
76
+ koa: true,
77
+ },
78
+ ```
79
+
80
+ Set `OIDC_TRUSTED_IP_HEADER` to the header your CDN or proxy uses to forward the real client IP. The header is only honoured when Koa proxy mode is enabled. Accepted values (all others are silently ignored):
73
81
 
74
82
  | Header | Provider |
75
83
  | --------------------------- | ------------------------------------------------- |
@@ -7,7 +7,7 @@ const React = require("react");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
9
  const reactIntl = require("react-intl");
10
- const index = require("./index-Dk6TYtio.js");
10
+ const index = require("./index-BqKuUY5d.js");
11
11
  const styled = require("styled-components");
12
12
  const lucideReact = require("lucide-react");
13
13
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -426,7 +426,14 @@ const DayButton = styled__default.default.button`
426
426
  aspect-ratio: 1;
427
427
  border: none;
428
428
  background: ${({ $selected, $inRange, $pending, theme }) => $pending ? theme.colors.warning200 : $selected ? theme.colors.primary600 : $inRange ? theme.colors.primary100 : "transparent"};
429
- color: ${({ $selected, $inRange, $pending, $future, $alreadySelected, theme }) => $pending ? theme.colors.warning600 : $selected ? theme.colors.neutral0 : $inRange ? theme.colors.primary600 : theme.colors.neutral800};
429
+ color: ${({
430
+ $selected,
431
+ $inRange,
432
+ $pending,
433
+ $future: _future,
434
+ $alreadySelected: _alreadySelected,
435
+ theme
436
+ }) => $pending ? theme.colors.warning600 : $selected ? theme.colors.neutral0 : $inRange ? theme.colors.primary600 : theme.colors.neutral800};
430
437
  opacity: ${({ $future, $alreadySelected }) => $future || $alreadySelected ? 0.4 : 1};
431
438
  border-radius: 4px;
432
439
  cursor: pointer;
@@ -852,7 +859,6 @@ function TablePagination({ page, pageCount, onPageChange, total }) {
852
859
  ] }) });
853
860
  }
854
861
  const PAGE_SIZE$1 = 10;
855
- const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
856
862
  function Whitelist({
857
863
  users,
858
864
  useWhitelist,
@@ -893,7 +899,7 @@ function Whitelist({
893
899
  if (!Array.isArray(parsed)) throw new Error();
894
900
  const emails = parsed.filter((item) => item?.email).map(
895
901
  (item) => String(item.email).trim().toLowerCase()
896
- ).filter((email2) => EMAIL_REGEX.test(email2));
902
+ ).filter((email2) => index.EMAIL_REGEX.test(email2));
897
903
  const count = await onImport(emails);
898
904
  if (count === 0) {
899
905
  toggleNotification({
@@ -935,7 +941,7 @@ function Whitelist({
935
941
  type: "text",
936
942
  disabled: loading,
937
943
  value: email,
938
- hasError: Boolean(email && !EMAIL_REGEX.test(email)),
944
+ hasError: Boolean(email && !index.EMAIL_REGEX.test(email)),
939
945
  onChange: (e) => setEmail(e.currentTarget.value),
940
946
  placeholder: formatMessage(index.getTrad("whitelist.email.placeholder")),
941
947
  style: { fontSize: "1.4rem", lineHeight: "2.2rem" }
@@ -946,7 +952,7 @@ function Whitelist({
946
952
  {
947
953
  size: "S",
948
954
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
949
- disabled: loading || email.trim() === "" || !EMAIL_REGEX.test(email),
955
+ disabled: loading || email.trim() === "" || !index.EMAIL_REGEX.test(email),
950
956
  loading,
951
957
  onClick: onSaveEmail,
952
958
  children: formatMessage(index.getTrad("page.add"))
@@ -1066,6 +1072,163 @@ function Whitelist({
1066
1072
  ] })
1067
1073
  ] });
1068
1074
  }
1075
+ const AUDIT_ACTIONS = [
1076
+ "login_success",
1077
+ "login_failure",
1078
+ "missing_code",
1079
+ "state_mismatch",
1080
+ "nonce_mismatch",
1081
+ "token_exchange_failed",
1082
+ "whitelist_rejected",
1083
+ "email_not_verified",
1084
+ "id_token_invalid",
1085
+ "logout",
1086
+ "session_expired",
1087
+ "user_created"
1088
+ ];
1089
+ const IP_REGEX = /^(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::))$/;
1090
+ function FilterBar({
1091
+ filters,
1092
+ hasActiveFilters,
1093
+ onFiltersChange,
1094
+ onResetPage,
1095
+ onClear
1096
+ }) {
1097
+ const { formatMessage } = reactIntl.useIntl();
1098
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1099
+ designSystem.Box,
1100
+ {
1101
+ background: "neutral100",
1102
+ hasRadius: true,
1103
+ padding: 4,
1104
+ marginBottom: 4,
1105
+ borderColor: "neutral200",
1106
+ borderWidth: "1px",
1107
+ borderStyle: "solid",
1108
+ children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 3, children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { size: "1.6rem" }) }),
1111
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", children: formatMessage(index.getTrad("auditlog.filters")) })
1112
+ ] }),
1113
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ TagDateInput,
1116
+ {
1117
+ placeholder: formatMessage(index.getTrad("auditlog.filters.createdAt")),
1118
+ value: filters.createdAt ?? [],
1119
+ onChange: (selections) => {
1120
+ onFiltersChange((prev) => {
1121
+ if (selections.length === 0) {
1122
+ const { createdAt: _createdAt, ...rest } = prev;
1123
+ return rest;
1124
+ }
1125
+ return { ...prev, createdAt: selections };
1126
+ });
1127
+ onResetPage();
1128
+ },
1129
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Calendar, { width: "1.4rem", height: "1.4rem" }) })
1130
+ }
1131
+ ),
1132
+ /* @__PURE__ */ jsxRuntime.jsx(
1133
+ TagInput,
1134
+ {
1135
+ value: filters.action ?? [],
1136
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, action: value })),
1137
+ options: AUDIT_ACTIONS,
1138
+ placeholder: formatMessage(index.getTrad("auditlog.filters.action")),
1139
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { size: "1.4rem" }) })
1140
+ }
1141
+ ),
1142
+ /* @__PURE__ */ jsxRuntime.jsx(
1143
+ TagInput,
1144
+ {
1145
+ value: filters.email ?? [],
1146
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, email: value })),
1147
+ placeholder: formatMessage(index.getTrad("auditlog.filters.email")),
1148
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Mail, { width: "1.4rem", height: "1.4rem" }) }),
1149
+ validate: (v) => index.EMAIL_REGEX.test(v)
1150
+ }
1151
+ ),
1152
+ /* @__PURE__ */ jsxRuntime.jsx(
1153
+ TagInput,
1154
+ {
1155
+ value: filters.ip ?? [],
1156
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, ip: value })),
1157
+ placeholder: formatMessage(index.getTrad("auditlog.filters.ip")),
1158
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Server, { size: "1.4rem" }) }),
1159
+ validate: (v) => IP_REGEX.test(v)
1160
+ }
1161
+ ),
1162
+ hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(SizedButton, { size: "S", variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}), onClick: onClear, children: formatMessage(index.getTrad("auditlog.filters.clear")) })
1163
+ ] })
1164
+ ]
1165
+ }
1166
+ );
1167
+ }
1168
+ const DETAILS_TEXT_STYLE = {
1169
+ display: "block",
1170
+ overflow: "hidden",
1171
+ textOverflow: "ellipsis",
1172
+ whiteSpace: "nowrap",
1173
+ maxWidth: "180px",
1174
+ cursor: "help"
1175
+ };
1176
+ function LogTable({ records, loading, hasActiveFilters }) {
1177
+ const { formatMessage } = reactIntl.useIntl();
1178
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: "100%" }, children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1181
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
1182
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
1183
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
1184
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
1185
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
1186
+ ] }) }),
1187
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
1188
+ records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
1189
+ records.map((record) => {
1190
+ const detailKey = record.detailsKey ? `audit.${record.detailsKey}` : null;
1191
+ const detail = detailKey && index.en[detailKey] ? formatMessage(index.getTrad(detailKey), record.detailsParams ?? {}) : null;
1192
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1193
+ designSystem.Tr,
1194
+ {
1195
+ style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" },
1196
+ children: [
1197
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
1198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1199
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
1200
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
1201
+ icons.Information,
1202
+ {
1203
+ "aria-hidden": true,
1204
+ style: { cursor: "help" },
1205
+ width: "1.4rem",
1206
+ height: "1.4rem",
1207
+ fill: "primary600"
1208
+ }
1209
+ ) })
1210
+ ] }) }),
1211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
1212
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
1213
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: detail ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: detail, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: detail }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
1214
+ ]
1215
+ },
1216
+ record.id
1217
+ );
1218
+ })
1219
+ ] })
1220
+ ] }),
1221
+ loading && records.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1222
+ designSystem.Flex,
1223
+ {
1224
+ justifyContent: "center",
1225
+ alignItems: "center",
1226
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1227
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
1228
+ }
1229
+ )
1230
+ ] });
1231
+ }
1069
1232
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
1070
1233
  function getDefaultExportFromCjs(x) {
1071
1234
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -3607,30 +3770,6 @@ function requireLib() {
3607
3770
  }
3608
3771
  var libExports = /* @__PURE__ */ requireLib();
3609
3772
  const qs = /* @__PURE__ */ getDefaultExportFromCjs(libExports);
3610
- const AUDIT_ACTIONS = [
3611
- "login_success",
3612
- "login_failure",
3613
- "missing_code",
3614
- "state_mismatch",
3615
- "nonce_mismatch",
3616
- "token_exchange_failed",
3617
- "whitelist_rejected",
3618
- "email_not_verified",
3619
- "id_token_invalid",
3620
- "logout",
3621
- "session_expired",
3622
- "user_created"
3623
- ];
3624
- const PAGE_SIZE = 10;
3625
- const MIN_SPINNER_MS = 400;
3626
- const DETAILS_TEXT_STYLE = {
3627
- display: "block",
3628
- overflow: "hidden",
3629
- textOverflow: "ellipsis",
3630
- whiteSpace: "nowrap",
3631
- maxWidth: "180px",
3632
- cursor: "help"
3633
- };
3634
3773
  function toWireFilters(f) {
3635
3774
  const out = {};
3636
3775
  if (f.action?.length) out.action = { $or: f.action.map((v) => ({ $eq: v })) };
@@ -3647,11 +3786,10 @@ function buildQueryString(params) {
3647
3786
  const { filters, ...rest } = params;
3648
3787
  return qs.stringify(
3649
3788
  { ...rest, filters: filters ? toWireFilters(filters) : void 0 },
3650
- {
3651
- encodeValuesOnly: true
3652
- }
3789
+ { encodeValuesOnly: true }
3653
3790
  );
3654
3791
  }
3792
+ const PAGE_SIZE = index.AUDIT_LOG_DEFAULTS.ADMIN_PAGE_SIZE;
3655
3793
  function useDebounced(value, delay = 300) {
3656
3794
  const [debounced, setDebounced] = React.useState(value);
3657
3795
  React.useEffect(() => {
@@ -3660,10 +3798,8 @@ function useDebounced(value, delay = 300) {
3660
3798
  }, [value, delay]);
3661
3799
  return debounced;
3662
3800
  }
3663
- function AuditLog({ title } = {}) {
3664
- const { formatMessage } = reactIntl.useIntl();
3665
- const { get: get2, del } = admin.useFetchClient();
3666
- const { toggleNotification } = admin.useNotification();
3801
+ function useAuditLogs(page, filters) {
3802
+ const { get: get2 } = admin.useFetchClient();
3667
3803
  const [records, setRecords] = React.useState([]);
3668
3804
  const [pagination, setPagination] = React.useState({
3669
3805
  page: 1,
@@ -3671,9 +3807,7 @@ function AuditLog({ title } = {}) {
3671
3807
  total: 0,
3672
3808
  pageCount: 1
3673
3809
  });
3674
- const [page, setPage] = React.useState(1);
3675
3810
  const [loading, setLoading] = React.useState(true);
3676
- const [filters, setFilters] = React.useState({});
3677
3811
  const fetchGenRef = React.useRef(0);
3678
3812
  const debouncedFilters = useDebounced(filters);
3679
3813
  const fetchLogs = React.useCallback(
@@ -3695,7 +3829,7 @@ function AuditLog({ title } = {}) {
3695
3829
  };
3696
3830
  } catch {
3697
3831
  }
3698
- const remaining = MIN_SPINNER_MS - (Date.now() - startTime);
3832
+ const remaining = index.UI_DEFAULTS.MIN_SPINNER_MS - (Date.now() - startTime);
3699
3833
  if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
3700
3834
  if (gen !== fetchGenRef.current) return;
3701
3835
  setRecords(newRecords);
@@ -3707,6 +3841,15 @@ function AuditLog({ title } = {}) {
3707
3841
  React.useEffect(() => {
3708
3842
  fetchLogs(page, debouncedFilters);
3709
3843
  }, [fetchLogs, page, debouncedFilters]);
3844
+ return { records, pagination, loading };
3845
+ }
3846
+ function AuditLog({ title } = {}) {
3847
+ const { formatMessage } = reactIntl.useIntl();
3848
+ const { del } = admin.useFetchClient();
3849
+ const { toggleNotification } = admin.useNotification();
3850
+ const [page, setPage] = React.useState(1);
3851
+ const [filters, setFilters] = React.useState({});
3852
+ const { records, pagination, loading } = useAuditLogs(page, filters);
3710
3853
  const handleClearAll = async () => {
3711
3854
  try {
3712
3855
  await del("/strapi-plugin-oidc/audit-logs");
@@ -3727,9 +3870,7 @@ function AuditLog({ title } = {}) {
3727
3870
  try {
3728
3871
  const cookieMatch = document.cookie.match(/(?:^|;\s*)jwtToken=([^;]+)/);
3729
3872
  const token = cookieMatch ? decodeURIComponent(cookieMatch[1]) : "";
3730
- const queryString = buildQueryString({
3731
- filters
3732
- });
3873
+ const queryString = buildQueryString({ filters });
3733
3874
  const response = await fetch(`/strapi-plugin-oidc/audit-logs/export?${queryString}`, {
3734
3875
  headers: { Authorization: `Bearer ${token}` }
3735
3876
  });
@@ -3797,135 +3938,17 @@ function AuditLog({ title } = {}) {
3797
3938
  )
3798
3939
  ] })
3799
3940
  ] }),
3800
- /* @__PURE__ */ jsxRuntime.jsxs(
3801
- designSystem.Box,
3941
+ /* @__PURE__ */ jsxRuntime.jsx(
3942
+ FilterBar,
3802
3943
  {
3803
- background: "neutral100",
3804
- hasRadius: true,
3805
- padding: 4,
3806
- marginBottom: 4,
3807
- borderColor: "neutral200",
3808
- borderWidth: "1px",
3809
- borderStyle: "solid",
3810
- children: [
3811
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 3, children: [
3812
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { size: "1.6rem" }) }),
3813
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", children: formatMessage(index.getTrad("auditlog.filters")) })
3814
- ] }),
3815
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
3816
- /* @__PURE__ */ jsxRuntime.jsx(
3817
- TagDateInput,
3818
- {
3819
- placeholder: formatMessage(index.getTrad("auditlog.filters.createdAt")),
3820
- value: filters.createdAt ?? [],
3821
- onChange: (selections) => {
3822
- setFilters((prev) => {
3823
- if (selections.length === 0) {
3824
- const { createdAt: _removed, ...rest } = prev;
3825
- return rest;
3826
- }
3827
- return { ...prev, createdAt: selections };
3828
- });
3829
- setPage(1);
3830
- },
3831
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Calendar, { width: "1.4rem", height: "1.4rem" }) })
3832
- }
3833
- ),
3834
- /* @__PURE__ */ jsxRuntime.jsx(
3835
- TagInput,
3836
- {
3837
- value: filters.action ?? [],
3838
- onChange: (value) => setFilters((prev) => ({ ...prev, action: value })),
3839
- options: AUDIT_ACTIONS,
3840
- placeholder: formatMessage(index.getTrad("auditlog.filters.action")),
3841
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { size: "1.4rem" }) })
3842
- }
3843
- ),
3844
- /* @__PURE__ */ jsxRuntime.jsx(
3845
- TagInput,
3846
- {
3847
- value: filters.email ?? [],
3848
- onChange: (value) => setFilters((prev) => ({ ...prev, email: value })),
3849
- placeholder: formatMessage(index.getTrad("auditlog.filters.email")),
3850
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Mail, { width: "1.4rem", height: "1.4rem" }) }),
3851
- validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)
3852
- }
3853
- ),
3854
- /* @__PURE__ */ jsxRuntime.jsx(
3855
- TagInput,
3856
- {
3857
- value: filters.ip ?? [],
3858
- onChange: (value) => setFilters((prev) => ({ ...prev, ip: value })),
3859
- placeholder: formatMessage(index.getTrad("auditlog.filters.ip")),
3860
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Server, { size: "1.4rem" }) }),
3861
- validate: (v) => /^(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::))$/.test(
3862
- v
3863
- )
3864
- }
3865
- ),
3866
- hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
3867
- SizedButton,
3868
- {
3869
- size: "S",
3870
- variant: "danger-light",
3871
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}),
3872
- onClick: clearFilters,
3873
- children: formatMessage(index.getTrad("auditlog.filters.clear"))
3874
- }
3875
- )
3876
- ] })
3877
- ]
3944
+ filters,
3945
+ hasActiveFilters,
3946
+ onFiltersChange: setFilters,
3947
+ onResetPage: () => setPage(1),
3948
+ onClear: clearFilters
3878
3949
  }
3879
3950
  ),
3880
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: "100%" }, children: [
3881
- /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3882
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
3883
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
3884
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
3885
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
3886
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
3887
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
3888
- ] }) }),
3889
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
3890
- records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
3891
- records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(
3892
- designSystem.Tr,
3893
- {
3894
- style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" },
3895
- children: [
3896
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3897
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3898
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
3899
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
3900
- icons.Information,
3901
- {
3902
- "aria-hidden": true,
3903
- style: { cursor: "help" },
3904
- width: "1.4rem",
3905
- height: "1.4rem",
3906
- fill: "primary600"
3907
- }
3908
- ) })
3909
- ] }) }),
3910
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
3911
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3912
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3913
- ]
3914
- },
3915
- record.id
3916
- ))
3917
- ] })
3918
- ] }),
3919
- loading && records.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3920
- designSystem.Flex,
3921
- {
3922
- justifyContent: "center",
3923
- alignItems: "center",
3924
- style: { position: "absolute", inset: 0, pointerEvents: "none" },
3925
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
3926
- }
3927
- )
3928
- ] }),
3951
+ /* @__PURE__ */ jsxRuntime.jsx(LogTable, { records, loading, hasActiveFilters }),
3929
3952
  /* @__PURE__ */ jsxRuntime.jsx(
3930
3953
  TablePagination,
3931
3954
  {
@@ -3995,7 +4018,7 @@ const SwitchInput = styled__default.default.input`
3995
4018
  &:checked + span:before {
3996
4019
  transform: translateX(16px);
3997
4020
  }
3998
-
4021
+
3999
4022
  &:disabled + span {
4000
4023
  pointer-events: none;
4001
4024
  }
@@ -4013,7 +4036,7 @@ const SwitchSlider = styled__default.default.span`
4013
4036
 
4014
4037
  &:before {
4015
4038
  position: absolute;
4016
- content: "";
4039
+ content: '';
4017
4040
  height: 18px;
4018
4041
  width: 18px;
4019
4042
  left: 3px;
@@ -4023,21 +4046,26 @@ const SwitchSlider = styled__default.default.span`
4023
4046
  border-radius: 50%;
4024
4047
  }
4025
4048
  `;
4026
- function CustomSwitch({ checked, onChange, label, disabled }) {
4049
+ function CustomSwitch({
4050
+ checked,
4051
+ onChange,
4052
+ label,
4053
+ disabled
4054
+ }) {
4027
4055
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, children: [
4028
4056
  /* @__PURE__ */ jsxRuntime.jsxs(SwitchContainer, { $disabled: disabled, children: [
4029
- /* @__PURE__ */ jsxRuntime.jsx(
4030
- SwitchInput,
4031
- {
4032
- type: "checkbox",
4033
- checked,
4034
- onChange,
4035
- disabled
4036
- }
4037
- ),
4057
+ /* @__PURE__ */ jsxRuntime.jsx(SwitchInput, { type: "checkbox", checked, onChange, disabled }),
4038
4058
  /* @__PURE__ */ jsxRuntime.jsx(SwitchSlider, {})
4039
4059
  ] }),
4040
- label && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: disabled ? "neutral500" : "neutral800", children: label })
4060
+ label && /* @__PURE__ */ jsxRuntime.jsx(
4061
+ designSystem.Typography,
4062
+ {
4063
+ variant: "pi",
4064
+ fontWeight: "bold",
4065
+ textColor: disabled ? "neutral500" : "neutral800",
4066
+ children: label
4067
+ }
4068
+ )
4041
4069
  ] });
4042
4070
  }
4043
4071
  function formatDatetimeForFilename(date) {
@@ -4059,19 +4087,15 @@ function downloadJson(basename, data) {
4059
4087
  a.click();
4060
4088
  URL.revokeObjectURL(url);
4061
4089
  }
4090
+ const defaultSnapshot = {
4091
+ oidcRoles: [],
4092
+ users: [],
4093
+ useWhitelist: false,
4094
+ enforceOIDC: false
4095
+ };
4062
4096
  const initialState = {
4063
- current: {
4064
- oidcRoles: [],
4065
- users: [],
4066
- useWhitelist: false,
4067
- enforceOIDC: false
4068
- },
4069
- initial: {
4070
- oidcRoles: [],
4071
- users: [],
4072
- useWhitelist: false,
4073
- enforceOIDC: false
4074
- },
4097
+ current: { ...defaultSnapshot },
4098
+ initial: { ...defaultSnapshot },
4075
4099
  roles: [],
4076
4100
  enforceOIDCConfig: null,
4077
4101
  auditLogEnabled: true,
@@ -177,7 +177,10 @@ const en = {
177
177
  "user.invalid_state": "State parameter mismatch. Please restart the login flow.",
178
178
  "user.signInError": "Authentication failed. Please try again.",
179
179
  "settings.section": "OIDC",
180
- "settings.configuration": "Configuration"
180
+ "settings.configuration": "Configuration",
181
+ "audit.login_failure": "Error: {message}",
182
+ "audit.roles_updated": "Roles updated to: {roles}",
183
+ "audit.user_created": "Roles assigned: {roles}"
181
184
  };
182
185
  function getTrad(id) {
183
186
  const pluginIdWithId = `${pluginId}.${id}`;
@@ -189,9 +192,31 @@ function getTrad(id) {
189
192
  function t(id) {
190
193
  return en[id];
191
194
  }
195
+ const PLUGIN_UID = "plugin::strapi-plugin-oidc";
196
+ const PERMISSIONS = {
197
+ READ: `${PLUGIN_UID}.read`
198
+ };
199
+ const AUDIT_LOG_DEFAULTS = {
200
+ ADMIN_PAGE_SIZE: 10
201
+ };
202
+ const UI_DEFAULTS = {
203
+ MIN_SPINNER_MS: 400
204
+ };
205
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
192
206
  const name = pluginPkg.strapi.displayName;
193
207
  const index = {
194
208
  register(app) {
209
+ const AppPage = React__default.default.lazy(() => Promise.resolve().then(() => require("./index-BbgFpvmz.js")));
210
+ const link = {
211
+ id: "configuration",
212
+ to: `/settings/${pluginId}`,
213
+ intlLabel: {
214
+ id: "settings.configuration",
215
+ defaultMessage: "Configuration"
216
+ },
217
+ Component: AppPage,
218
+ permissions: [{ action: PERMISSIONS.READ, subject: null }]
219
+ };
195
220
  app.addSettingsLink(
196
221
  {
197
222
  id: "oidc",
@@ -200,18 +225,7 @@ const index = {
200
225
  defaultMessage: "OIDC"
201
226
  }
202
227
  },
203
- {
204
- id: "configuration",
205
- to: `/settings/${pluginId}`,
206
- intlLabel: {
207
- id: "settings.configuration",
208
- defaultMessage: "Configuration"
209
- },
210
- Component: async () => {
211
- return await Promise.resolve().then(() => require("./index-Bmg4eTYb.js"));
212
- },
213
- permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }]
214
- }
228
+ link
215
229
  );
216
230
  app.registerPlugin({
217
231
  id: pluginId,
@@ -344,5 +358,9 @@ const index = {
344
358
  return importedTrads;
345
359
  }
346
360
  };
361
+ exports.AUDIT_LOG_DEFAULTS = AUDIT_LOG_DEFAULTS;
362
+ exports.EMAIL_REGEX = EMAIL_REGEX;
363
+ exports.UI_DEFAULTS = UI_DEFAULTS;
364
+ exports.en = en;
347
365
  exports.getTrad = getTrad;
348
366
  exports.index = index;