strapi-plugin-oidc 1.5.3 → 1.6.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.
@@ -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-CqgjBmJ5.js");
10
+ const index = require("./index-f3cmU_tE.js");
11
11
  const styled = require("styled-components");
12
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
13
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
@@ -31,6 +31,20 @@ function Role({ oidcRoles, roles, onChangeRole }) {
31
31
  ) }, oidcRole.oauth_type)) })
32
32
  ] });
33
33
  }
34
+ function LocalizedDate({
35
+ date,
36
+ options
37
+ }) {
38
+ const userLocale = navigator.language || "en-US";
39
+ return new Intl.DateTimeFormat(userLocale, {
40
+ year: "numeric",
41
+ month: "short",
42
+ day: "numeric",
43
+ hour: "2-digit",
44
+ minute: "2-digit",
45
+ ...options
46
+ }).format(new Date(date));
47
+ }
34
48
  const CustomTable = styled__default.default(designSystem.Table)`
35
49
  th,
36
50
  td,
@@ -39,20 +53,96 @@ const CustomTable = styled__default.default(designSystem.Table)`
39
53
  font-size: 1.3rem !important;
40
54
  }
41
55
  `;
42
- function LocalizedDate({ date }) {
43
- const userLocale = navigator.language || "en-US";
44
- return new Intl.DateTimeFormat(userLocale, {
45
- year: "numeric",
46
- month: "long",
47
- day: "numeric",
48
- hour: "2-digit",
49
- minute: "2-digit"
50
- }).format(new Date(date));
56
+ function ConfirmDialog({
57
+ trigger,
58
+ title,
59
+ body,
60
+ confirmLabel,
61
+ onConfirm,
62
+ confirmVariant = "danger"
63
+ }) {
64
+ const { formatMessage } = reactIntl.useIntl();
65
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Root, { children: [
66
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Trigger, { children: trigger }),
67
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
68
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
69
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.WarningCircle, { fill: "danger600" }), children: body }),
70
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
71
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: formatMessage(index.getTrad("page.cancel")) }) }),
72
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: confirmVariant, onClick: onConfirm, children: confirmLabel }) })
73
+ ] })
74
+ ] })
75
+ ] });
76
+ }
77
+ function TablePagination({ page, pageCount, onPageChange }) {
78
+ const { formatMessage } = reactIntl.useIntl();
79
+ if (pageCount <= 1) return null;
80
+ const pageLink = (num) => /* @__PURE__ */ jsxRuntime.jsx(
81
+ designSystem.PageLink,
82
+ {
83
+ number: num,
84
+ href: "#",
85
+ onClick: (e) => {
86
+ e.preventDefault();
87
+ onPageChange(num);
88
+ },
89
+ children: formatMessage(index.getTrad("pagination.page"), { page: num })
90
+ },
91
+ num
92
+ );
93
+ const Ellipsis = () => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", paddingLeft: 2, paddingRight: 2, children: "…" });
94
+ let pages;
95
+ if (pageCount <= 10) {
96
+ pages = Array.from({ length: pageCount }, (_, i) => pageLink(i + 1));
97
+ } else if (page <= 6) {
98
+ pages = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
99
+ Array.from({ length: 9 }, (_, i) => pageLink(i + 1)),
100
+ /* @__PURE__ */ jsxRuntime.jsx(Ellipsis, {}),
101
+ pageLink(pageCount)
102
+ ] });
103
+ } else if (page >= pageCount - 5) {
104
+ pages = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
105
+ pageLink(1),
106
+ /* @__PURE__ */ jsxRuntime.jsx(Ellipsis, {}),
107
+ Array.from({ length: 9 }, (_, i) => pageLink(pageCount - 8 + i))
108
+ ] });
109
+ } else {
110
+ pages = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
111
+ pageLink(1),
112
+ /* @__PURE__ */ jsxRuntime.jsx(Ellipsis, {}),
113
+ Array.from({ length: 7 }, (_, i) => pageLink(page - 3 + i)),
114
+ /* @__PURE__ */ jsxRuntime.jsx(Ellipsis, {}),
115
+ pageLink(pageCount)
116
+ ] });
117
+ }
118
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Pagination, { activePage: page, pageCount, children: [
119
+ /* @__PURE__ */ jsxRuntime.jsx(
120
+ designSystem.PreviousLink,
121
+ {
122
+ href: "#",
123
+ onClick: (e) => {
124
+ e.preventDefault();
125
+ onPageChange(Math.max(1, page - 1));
126
+ },
127
+ children: formatMessage(index.getTrad("pagination.previous"))
128
+ }
129
+ ),
130
+ pages,
131
+ /* @__PURE__ */ jsxRuntime.jsx(
132
+ designSystem.NextLink,
133
+ {
134
+ href: "#",
135
+ onClick: (e) => {
136
+ e.preventDefault();
137
+ onPageChange(Math.min(pageCount, page + 1));
138
+ },
139
+ children: formatMessage(index.getTrad("pagination.next"))
140
+ }
141
+ )
142
+ ] }) }) });
51
143
  }
52
144
  function Whitelist({
53
145
  users,
54
- roles,
55
- oidcRoles = [],
56
146
  useWhitelist,
57
147
  loading,
58
148
  onSave,
@@ -62,19 +152,13 @@ function Whitelist({
62
152
  onExport
63
153
  }) {
64
154
  const [email, setEmail] = react.useState("");
65
- const [selectedRoles, setSelectedRoles] = react.useState([]);
66
155
  const [page, setPage] = react.useState(1);
67
156
  const { formatMessage } = reactIntl.useIntl();
68
157
  const { toggleNotification } = admin.useNotification();
69
158
  const fileInputRef = react.useRef(null);
70
- const PAGE_SIZE = 10;
71
- const pageCount = Math.ceil(users.length / PAGE_SIZE) || 1;
72
- const paginatedUsers = users.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
73
- const getRoleNames = (roleIds) => roleIds.map((roleId) => {
74
- const r = roles.find((ro) => String(ro.id) === String(roleId));
75
- return r ? r.name : roleId;
76
- }).join(", ");
77
- const defaultRoleNames = getRoleNames(oidcRoles.flatMap((oidc) => oidc.role ?? []));
159
+ const PAGE_SIZE2 = 10;
160
+ const pageCount = Math.ceil(users.length / PAGE_SIZE2) || 1;
161
+ const paginatedUsers = users.slice((page - 1) * PAGE_SIZE2, page * PAGE_SIZE2);
78
162
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
79
163
  const isValidEmail = emailRegex.test(email);
80
164
  const onSaveEmail = react.useCallback(() => {
@@ -85,11 +169,10 @@ function Whitelist({
85
169
  message: formatMessage(index.getTrad("whitelist.error.unique"))
86
170
  });
87
171
  } else {
88
- onSave(emailText, selectedRoles);
172
+ onSave(emailText);
89
173
  setEmail("");
90
- setSelectedRoles([]);
91
174
  }
92
- }, [email, selectedRoles, users, onSave, formatMessage, toggleNotification]);
175
+ }, [email, users, onSave, formatMessage, toggleNotification]);
93
176
  const handleImport = react.useCallback(
94
177
  async (e) => {
95
178
  const file = e.target.files?.[0];
@@ -100,11 +183,8 @@ function Whitelist({
100
183
  const text = await file.text();
101
184
  const parsed = JSON.parse(text);
102
185
  if (!Array.isArray(parsed)) throw new Error();
103
- const entries = parsed.filter((item) => item?.email).map((item) => ({
104
- email: String(item.email),
105
- roles: Array.isArray(item.roles) ? item.roles : []
106
- }));
107
- const count = await onImport(entries);
186
+ const emails = parsed.filter((item) => item?.email).map((item) => String(item.email).trim().toLowerCase()).filter((email2) => emailRegex.test(email2));
187
+ const count = await onImport(emails);
108
188
  if (count === 0) {
109
189
  toggleNotification({
110
190
  type: "info",
@@ -162,19 +242,18 @@ function Whitelist({
162
242
  onChange: handleImport
163
243
  }
164
244
  ),
165
- users.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Root, { children: [
166
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}), children: formatMessage(index.getTrad("whitelist.delete.all.label")) }) }),
167
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
168
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage(index.getTrad("whitelist.delete.all.title")) }),
169
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.WarningCircle, { fill: "danger600" }), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", textAlign: "center", children: formatMessage(index.getTrad("whitelist.delete.all.description"), {
245
+ users.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
246
+ ConfirmDialog,
247
+ {
248
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}), children: formatMessage(index.getTrad("whitelist.delete.all.label")) }),
249
+ title: formatMessage(index.getTrad("whitelist.delete.all.title")),
250
+ body: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", textAlign: "center", children: formatMessage(index.getTrad("whitelist.delete.all.description"), {
170
251
  count: users.length
171
- }) }) }) }),
172
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
173
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: formatMessage(index.getTrad("page.cancel")) }) }),
174
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "danger", onClick: onDeleteAll, children: formatMessage(index.getTrad("whitelist.delete.all.label")) }) })
175
- ] })
176
- ] })
177
- ] })
252
+ }) }) }),
253
+ confirmLabel: formatMessage(index.getTrad("whitelist.delete.all.label")),
254
+ onConfirm: onDeleteAll
255
+ }
256
+ )
178
257
  ] })
179
258
  ] }),
180
259
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, marginTop: 5, marginBottom: 5, alignItems: "flex-start", children: [
@@ -189,18 +268,6 @@ function Whitelist({
189
268
  placeholder: formatMessage(index.getTrad("whitelist.email.placeholder"))
190
269
  }
191
270
  ) }) }),
192
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
193
- designSystem.MultiSelect,
194
- {
195
- withTags: true,
196
- placeholder: formatMessage(index.getTrad("whitelist.roles.placeholder")),
197
- value: selectedRoles,
198
- onChange: (value) => {
199
- setSelectedRoles(value || []);
200
- },
201
- children: roles.map((role) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.MultiSelectOption, { value: role.id.toString(), children: role.name }, role.id))
202
- }
203
- ) }) }),
204
271
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
205
272
  designSystem.Button,
206
273
  {
@@ -214,105 +281,216 @@ function Whitelist({
214
281
  ) })
215
282
  ] }),
216
283
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
217
- /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: users.length, children: [
284
+ /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 4, rowCount: users.length, children: [
218
285
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
219
286
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("whitelist.table.no")) }),
220
287
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("whitelist.table.email")) }),
221
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("whitelist.table.roles")) }),
222
288
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("whitelist.table.created")) }),
223
289
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { paddingRight: 0 }, children: " " })
224
290
  ] }) }),
225
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index$1) => {
226
- const explicitRoleNames = getRoleNames(user.roles || []);
227
- const isDefault = !explicitRoleNames && Boolean(defaultRoleNames);
228
- const userRolesNames = explicitRoleNames || defaultRoleNames;
229
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
230
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: index$1 + 1 + (page - 1) * PAGE_SIZE }),
231
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.email }),
232
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: userRolesNames ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
233
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: userRolesNames }),
234
- isDefault && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage(index.getTrad("whitelist.table.roles.default")) })
235
- ] }) : "-" }),
236
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: user.createdAt }) }),
237
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { paddingRight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
238
- designSystem.Flex,
239
- {
240
- justifyContent: "flex-end",
241
- onClick: (e) => e.stopPropagation(),
242
- style: { width: "100%" },
243
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Root, { children: [
244
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
291
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index$1) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
292
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: index$1 + 1 + (page - 1) * PAGE_SIZE2 }),
293
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.email }),
294
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: user.createdAt, options: { month: "long" } }) }),
295
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { paddingRight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
296
+ designSystem.Flex,
297
+ {
298
+ justifyContent: "flex-end",
299
+ onClick: (e) => e.stopPropagation(),
300
+ style: { width: "100%" },
301
+ children: /* @__PURE__ */ jsxRuntime.jsx(
302
+ ConfirmDialog,
303
+ {
304
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
245
305
  designSystem.IconButton,
246
306
  {
247
307
  label: formatMessage(index.getTrad("whitelist.delete.label")),
248
308
  withTooltip: false,
249
309
  children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
250
310
  }
251
- ) }),
252
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
253
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage(index.getTrad("whitelist.delete.title")) }),
254
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.WarningCircle, { fill: "danger600" }), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 2, children: [
255
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", children: formatMessage(index.getTrad("whitelist.delete.description")) }) }),
256
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: user.email }) }),
257
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.delete.note")) }) })
258
- ] }) }),
259
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
260
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: formatMessage(index.getTrad("page.cancel")) }) }),
261
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(
262
- designSystem.Button,
263
- {
264
- fullWidth: true,
265
- variant: "danger-light",
266
- onClick: () => onDelete(user.email),
267
- children: formatMessage(index.getTrad("page.ok"))
268
- }
269
- ) })
270
- ] })
271
- ] })
272
- ] })
273
- }
274
- ) })
275
- ] }, user.email);
276
- }) })
311
+ ),
312
+ title: formatMessage(index.getTrad("whitelist.delete.title")),
313
+ body: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 2, children: [
314
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", children: formatMessage(index.getTrad("whitelist.delete.description")) }) }),
315
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: user.email }) }),
316
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.delete.note")) }) })
317
+ ] }),
318
+ confirmLabel: formatMessage(index.getTrad("page.ok")),
319
+ onConfirm: () => onDelete(user.email),
320
+ confirmVariant: "danger-light"
321
+ }
322
+ )
323
+ }
324
+ ) })
325
+ ] }, user.email)) })
277
326
  ] }),
278
- pageCount > 1 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Pagination, { activePage: page, pageCount, children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx(TablePagination, { page, pageCount, onPageChange: setPage })
328
+ ] })
329
+ ] });
330
+ }
331
+ const PAGE_SIZE = 10;
332
+ const DETAILS_TEXT_STYLE = {
333
+ display: "block",
334
+ overflow: "hidden",
335
+ textOverflow: "ellipsis",
336
+ whiteSpace: "nowrap",
337
+ maxWidth: "180px",
338
+ cursor: "help"
339
+ };
340
+ function AuditLog() {
341
+ const { formatMessage } = reactIntl.useIntl();
342
+ const { get, del } = admin.useFetchClient();
343
+ const { toggleNotification } = admin.useNotification();
344
+ const [records, setRecords] = react.useState([]);
345
+ const [pagination, setPagination] = react.useState({
346
+ page: 1,
347
+ pageSize: PAGE_SIZE,
348
+ total: 0,
349
+ pageCount: 1
350
+ });
351
+ const [page, setPage] = react.useState(1);
352
+ const [loading, setLoading] = react.useState(false);
353
+ const fetchLogs = react.useCallback(
354
+ async (p) => {
355
+ setLoading(true);
356
+ try {
357
+ const response = await get(
358
+ `/strapi-plugin-oidc/audit-logs?page=${p}&pageSize=${PAGE_SIZE}`
359
+ );
360
+ setRecords(response.data.results ?? []);
361
+ setPagination(
362
+ response.data.pagination ?? { page: p, pageSize: PAGE_SIZE, total: 0, pageCount: 1 }
363
+ );
364
+ } catch {
365
+ setRecords([]);
366
+ } finally {
367
+ setLoading(false);
368
+ }
369
+ },
370
+ [get]
371
+ );
372
+ react.useEffect(() => {
373
+ fetchLogs(page);
374
+ }, [fetchLogs, page]);
375
+ const handleClearAll = async () => {
376
+ try {
377
+ await del("/strapi-plugin-oidc/audit-logs");
378
+ toggleNotification({
379
+ type: "success",
380
+ message: formatMessage(index.getTrad("auditlog.clear.success"))
381
+ });
382
+ fetchLogs(1);
383
+ } catch {
384
+ toggleNotification({
385
+ type: "danger",
386
+ message: formatMessage(index.getTrad("auditlog.clear.error"))
387
+ });
388
+ }
389
+ };
390
+ const handleExport = async () => {
391
+ try {
392
+ const cookieMatch = document.cookie.match(/(?:^|;\s*)jwtToken=([^;]+)/);
393
+ const token = cookieMatch ? decodeURIComponent(cookieMatch[1]) : "";
394
+ const response = await fetch("/strapi-plugin-oidc/audit-logs/export", {
395
+ headers: { Authorization: `Bearer ${token}` }
396
+ });
397
+ if (!response.ok) {
398
+ toggleNotification({
399
+ type: "danger",
400
+ message: formatMessage(index.getTrad("auditlog.export.error"))
401
+ });
402
+ return;
403
+ }
404
+ const text = await response.text();
405
+ const blob = new Blob([text], { type: "application/x-ndjson" });
406
+ const url = URL.createObjectURL(blob);
407
+ const a = document.createElement("a");
408
+ a.href = url;
409
+ a.download = `oidc-audit-log-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.ndjson`;
410
+ a.click();
411
+ URL.revokeObjectURL(url);
412
+ } catch {
413
+ toggleNotification({
414
+ type: "danger",
415
+ message: formatMessage(index.getTrad("auditlog.export.error"))
416
+ });
417
+ }
418
+ };
419
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
420
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, children: [
421
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
422
+ pagination.total,
423
+ " ",
424
+ pagination.total === 1 ? "entry" : "entries"
425
+ ] }),
426
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
279
427
  /* @__PURE__ */ jsxRuntime.jsx(
280
- designSystem.PreviousLink,
428
+ designSystem.Button,
281
429
  {
282
- href: "#",
283
- onClick: (e) => {
284
- e.preventDefault();
285
- setPage((p) => Math.max(1, p - 1));
286
- },
287
- children: formatMessage(index.getTrad("pagination.previous"))
430
+ size: "S",
431
+ variant: "tertiary",
432
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
433
+ onClick: handleExport,
434
+ disabled: pagination.total === 0,
435
+ children: formatMessage(index.getTrad("auditlog.export"))
288
436
  }
289
437
  ),
290
- Array.from({ length: pageCount }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
291
- designSystem.PageLink,
292
- {
293
- number: i + 1,
294
- href: "#",
295
- onClick: (e) => {
296
- e.preventDefault();
297
- setPage(i + 1);
298
- },
299
- children: formatMessage(index.getTrad("pagination.page"), { page: i + 1 })
300
- },
301
- i + 1
302
- )),
303
438
  /* @__PURE__ */ jsxRuntime.jsx(
304
- designSystem.NextLink,
439
+ ConfirmDialog,
305
440
  {
306
- href: "#",
307
- onClick: (e) => {
308
- e.preventDefault();
309
- setPage((p) => Math.min(pageCount, p + 1));
310
- },
311
- children: formatMessage(index.getTrad("pagination.next"))
441
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
442
+ designSystem.Button,
443
+ {
444
+ size: "S",
445
+ variant: "danger-light",
446
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}),
447
+ disabled: pagination.total === 0,
448
+ children: formatMessage(index.getTrad("auditlog.clear"))
449
+ }
450
+ ),
451
+ title: formatMessage(index.getTrad("auditlog.clear.title")),
452
+ body: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", textAlign: "center", children: formatMessage(index.getTrad("auditlog.clear.description"), {
453
+ count: pagination.total
454
+ }) }) }),
455
+ confirmLabel: formatMessage(index.getTrad("auditlog.clear")),
456
+ onConfirm: handleClearAll
312
457
  }
313
458
  )
314
- ] }) }) })
315
- ] })
459
+ ] })
460
+ ] }),
461
+ /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
462
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
463
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
464
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
465
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
466
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
467
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
468
+ ] }) }),
469
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
470
+ loading && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "Loading…" }) }) }) }),
471
+ !loading && records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
472
+ !loading && records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
473
+ /* @__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" } }) }) }),
474
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
475
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
476
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
477
+ icons.Information,
478
+ {
479
+ "aria-hidden": true,
480
+ style: { cursor: "help" },
481
+ width: "1.4rem",
482
+ height: "1.4rem",
483
+ fill: "primary600"
484
+ }
485
+ ) })
486
+ ] }) }),
487
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
488
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
489
+ /* @__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: "—" }) })
490
+ ] }, record.id))
491
+ ] })
492
+ ] }),
493
+ /* @__PURE__ */ jsxRuntime.jsx(TablePagination, { page, pageCount: pagination.pageCount, onPageChange: setPage })
316
494
  ] });
317
495
  }
318
496
  const AlertMessage = styled__default.default.div`
@@ -349,23 +527,6 @@ function ErrorAlertMessage({ onClose }) {
349
527
  }
350
528
  ) });
351
529
  }
352
- function MatchedUserAlertMessage({
353
- onClose,
354
- count
355
- }) {
356
- const { formatMessage } = reactIntl.useIntl();
357
- const id = count > 1 ? "whitelist.users_exists" : "whitelist.user_exists";
358
- return /* @__PURE__ */ jsxRuntime.jsx(AlertMessage, { children: /* @__PURE__ */ jsxRuntime.jsx(
359
- designSystem.Alert,
360
- {
361
- title: formatMessage(index.getTrad("alert.title.info")),
362
- variant: "default",
363
- closeLabel: "",
364
- onClose,
365
- children: formatMessage(index.getTrad(id))
366
- }
367
- ) });
368
- }
369
530
  const SwitchContainer = styled__default.default.label`
370
531
  position: relative;
371
532
  display: inline-block;
@@ -435,6 +596,15 @@ function CustomSwitch({ checked, onChange, label, disabled }) {
435
596
  label && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: disabled ? "neutral500" : "neutral800", children: label })
436
597
  ] });
437
598
  }
599
+ function formatDatetimeForFilename(date) {
600
+ const year = date.getFullYear();
601
+ const month = String(date.getMonth() + 1).padStart(2, "0");
602
+ const day = String(date.getDate()).padStart(2, "0");
603
+ const hours = String(date.getHours()).padStart(2, "0");
604
+ const minutes = String(date.getMinutes()).padStart(2, "0");
605
+ const seconds = String(date.getSeconds()).padStart(2, "0");
606
+ return `${year}${month}${day}_${hours}${minutes}${seconds}`;
607
+ }
438
608
  function deepClone(value) {
439
609
  return JSON.parse(JSON.stringify(value));
440
610
  }
@@ -443,7 +613,6 @@ function useOidcSettings() {
443
613
  const [loading, setLoading] = react.useState(false);
444
614
  const [showSuccess, setSuccess] = react.useState(false);
445
615
  const [showError, setError] = react.useState(false);
446
- const [showMatched, setMatched] = react.useState(0);
447
616
  const [initialOidcRoles, setInitialOIDCRoles] = react.useState([]);
448
617
  const [oidcRoles, setOIDCRoles] = react.useState([]);
449
618
  const [roles, setRoles] = react.useState([]);
@@ -454,6 +623,7 @@ function useOidcSettings() {
454
623
  const [enforceOIDCConfig, setEnforceOIDCConfig] = react.useState(null);
455
624
  const [initialUsers, setInitialUsers] = react.useState([]);
456
625
  const [users, setUsers] = react.useState([]);
626
+ const [whitelistResponse, setWhitelistResponse] = react.useState({});
457
627
  react.useEffect(() => {
458
628
  get(`/strapi-plugin-oidc/oidc-roles`).then((response) => {
459
629
  setOIDCRoles(response.data);
@@ -463,6 +633,7 @@ function useOidcSettings() {
463
633
  setRoles(response.data.data);
464
634
  });
465
635
  get("/strapi-plugin-oidc/whitelist").then((response) => {
636
+ setWhitelistResponse(response.data);
466
637
  setUsers(response.data.whitelistUsers);
467
638
  setInitialUsers(deepClone(response.data.whitelistUsers));
468
639
  setUseWhitelist(response.data.useWhitelist);
@@ -478,8 +649,8 @@ function useOidcSettings() {
478
649
  );
479
650
  setOIDCRoles(updatedRoles);
480
651
  };
481
- const onRegisterWhitelist = (email, selectedRoles) => {
482
- const newUser = { email, roles: selectedRoles, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
652
+ const onRegisterWhitelist = (email) => {
653
+ const newUser = { email, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
483
654
  setUsers([...users, newUser]);
484
655
  };
485
656
  const onDeleteWhitelist = (email) => {
@@ -493,21 +664,19 @@ function useOidcSettings() {
493
664
  setUsers([]);
494
665
  if (useWhitelist) setEnforceOIDC(false);
495
666
  };
496
- const onImport = async (entries) => {
497
- const response = await post("/strapi-plugin-oidc/whitelist/import", { users: entries });
667
+ const onImport = async (emails) => {
668
+ const response = await post("/strapi-plugin-oidc/whitelist/import", {
669
+ users: emails.map((e) => ({ email: e }))
670
+ });
498
671
  const refreshed = await get("/strapi-plugin-oidc/whitelist");
499
672
  setUsers(refreshed.data.whitelistUsers);
500
673
  setInitialUsers(deepClone(refreshed.data.whitelistUsers));
501
674
  return response.data.importedCount;
502
675
  };
503
- const onExport = () => {
504
- const roleMap = new Map(roles.map((r) => [String(r.id), r.name]));
505
- const data = users.map(({ email, roles: userRoles }) => ({
506
- email,
507
- roles: (userRoles || []).map((id) => roleMap.get(String(id)) ?? id)
508
- }));
509
- const now = /* @__PURE__ */ new Date();
510
- const datetime = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
676
+ const onExport = async () => {
677
+ const response = await get("/strapi-plugin-oidc/whitelist/export");
678
+ const data = response.data;
679
+ const datetime = formatDatetimeForFilename(/* @__PURE__ */ new Date());
511
680
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
512
681
  const url = URL.createObjectURL(blob);
513
682
  const a = document.createElement("a");
@@ -536,8 +705,8 @@ function useOidcSettings() {
536
705
  role: role.role
537
706
  }))
538
707
  });
539
- const syncResponse = await put("/strapi-plugin-oidc/whitelist/sync", {
540
- users: users.map((u) => ({ email: u.email, roles: u.roles }))
708
+ await put("/strapi-plugin-oidc/whitelist/sync", {
709
+ users: users.map((u) => ({ email: u.email }))
541
710
  });
542
711
  await put("/strapi-plugin-oidc/whitelist/settings", {
543
712
  useWhitelist,
@@ -547,16 +716,12 @@ function useOidcSettings() {
547
716
  setInitialUseWhitelist(useWhitelist);
548
717
  setInitialEnforceOIDC(enforceOIDC);
549
718
  get("/strapi-plugin-oidc/whitelist").then((getResponse) => {
719
+ setWhitelistResponse(getResponse.data);
550
720
  setUsers(getResponse.data.whitelistUsers);
551
721
  setInitialUsers(deepClone(getResponse.data.whitelistUsers));
552
722
  });
553
- if (syncResponse.data?.matchedExistingUsersCount > 0) {
554
- setMatched(syncResponse.data.matchedExistingUsersCount);
555
- setTimeout(() => setMatched(0), 3e3);
556
- } else {
557
- setSuccess(true);
558
- setTimeout(() => setSuccess(false), 3e3);
559
- }
723
+ setSuccess(true);
724
+ setTimeout(() => setSuccess(false), 3e3);
560
725
  } catch (e) {
561
726
  console.error(e);
562
727
  setError(true);
@@ -570,7 +735,6 @@ function useOidcSettings() {
570
735
  loading,
571
736
  showSuccess,
572
737
  showError,
573
- showMatched,
574
738
  oidcRoles,
575
739
  roles,
576
740
  useWhitelist,
@@ -578,12 +742,12 @@ function useOidcSettings() {
578
742
  enforceOIDCConfig,
579
743
  initialEnforceOIDC,
580
744
  users,
581
- isDirty
745
+ isDirty,
746
+ auditLogEnabled: whitelistResponse.auditLogEnabled ?? true
582
747
  },
583
748
  actions: {
584
749
  setSuccess,
585
750
  setError,
586
- setMatched,
587
751
  onChangeRole,
588
752
  onRegisterWhitelist,
589
753
  onDeleteWhitelist,
@@ -610,7 +774,6 @@ function HomePage$1() {
610
774
  ),
611
775
  state.showSuccess && /* @__PURE__ */ jsxRuntime.jsx(SuccessAlertMessage, { onClose: () => actions.setSuccess(false) }),
612
776
  state.showError && /* @__PURE__ */ jsxRuntime.jsx(ErrorAlertMessage, { onClose: () => actions.setError(false) }),
613
- state.showMatched > 0 && /* @__PURE__ */ jsxRuntime.jsx(MatchedUserAlertMessage, { count: state.showMatched, onClose: () => actions.setMatched(0) }),
614
777
  /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
615
778
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "filterShadow", padding: 6, children: [
616
779
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", children: formatMessage(index.getTrad("roles.title")) }) }),
@@ -640,8 +803,6 @@ function HomePage$1() {
640
803
  {
641
804
  loading: state.loading,
642
805
  users: state.users,
643
- roles: state.roles,
644
- oidcRoles: state.oidcRoles,
645
806
  useWhitelist: state.useWhitelist,
646
807
  onSave: actions.onRegisterWhitelist,
647
808
  onDelete: actions.onDeleteWhitelist,
@@ -685,7 +846,11 @@ function HomePage$1() {
685
846
  loading: state.loading,
686
847
  children: formatMessage(index.getTrad("page.save"))
687
848
  }
688
- ) })
849
+ ) }),
850
+ state.auditLogEnabled && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "filterShadow", padding: 6, children: [
851
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", children: formatMessage(index.getTrad("auditlog.title")) }) }),
852
+ /* @__PURE__ */ jsxRuntime.jsx(AuditLog, {})
853
+ ] })
689
854
  ] }) }),
690
855
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: blocker.state === "blocked", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
691
856
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage(index.getTrad("unsaved.title")) }),