strapi-plugin-oidc 1.3.2 → 1.4.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-Cse9ex24.js");
10
+ const index = require("./index-CZDixCh4.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);
@@ -56,13 +56,17 @@ function Whitelist({
56
56
  useWhitelist,
57
57
  loading,
58
58
  onSave,
59
- onDelete
59
+ onDelete,
60
+ onDeleteAll,
61
+ onImport,
62
+ onExport
60
63
  }) {
61
64
  const [email, setEmail] = react.useState("");
62
65
  const [selectedRoles, setSelectedRoles] = react.useState([]);
63
66
  const [page, setPage] = react.useState(1);
64
67
  const { formatMessage } = reactIntl.useIntl();
65
68
  const { toggleNotification } = admin.useNotification();
69
+ const fileInputRef = react.useRef(null);
66
70
  const PAGE_SIZE = 10;
67
71
  const pageCount = Math.ceil(users.length / PAGE_SIZE) || 1;
68
72
  const paginatedUsers = users.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
@@ -83,9 +87,93 @@ function Whitelist({
83
87
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
84
88
  return emailRegex.test(email);
85
89
  }, [email]);
90
+ const handleImport = react.useCallback(
91
+ async (e) => {
92
+ const file = e.target.files?.[0];
93
+ if (!fileInputRef.current) return;
94
+ fileInputRef.current.value = "";
95
+ if (!file) return;
96
+ try {
97
+ const text = await file.text();
98
+ const parsed = JSON.parse(text);
99
+ if (!Array.isArray(parsed)) throw new Error();
100
+ const entries = parsed.filter((item) => item?.email).map((item) => ({
101
+ email: String(item.email),
102
+ roles: Array.isArray(item.roles) ? item.roles : []
103
+ }));
104
+ const count = await onImport(entries);
105
+ if (count === 0) {
106
+ toggleNotification({
107
+ type: "info",
108
+ message: formatMessage(index.getTrad("whitelist.import.none"))
109
+ });
110
+ } else {
111
+ toggleNotification({
112
+ type: "success",
113
+ message: formatMessage(index.getTrad("whitelist.import.success"), { count })
114
+ });
115
+ }
116
+ } catch {
117
+ toggleNotification({
118
+ type: "warning",
119
+ message: formatMessage(index.getTrad("whitelist.import.error"))
120
+ });
121
+ }
122
+ },
123
+ [onImport, formatMessage, toggleNotification]
124
+ );
86
125
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
87
126
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textColor: "neutral600", marginBottom: 4, children: formatMessage(index.getTrad("whitelist.description")) }),
88
127
  useWhitelist && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
128
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, children: [
129
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.count"), { count: users.length }) }),
130
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
131
+ /* @__PURE__ */ jsxRuntime.jsx(
132
+ designSystem.Button,
133
+ {
134
+ size: "S",
135
+ variant: "tertiary",
136
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
137
+ onClick: onExport,
138
+ disabled: users.length === 0,
139
+ children: formatMessage(index.getTrad("whitelist.export"))
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsxRuntime.jsx(
143
+ designSystem.Button,
144
+ {
145
+ size: "S",
146
+ variant: "tertiary",
147
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Upload, {}),
148
+ onClick: () => fileInputRef.current?.click(),
149
+ children: formatMessage(index.getTrad("whitelist.import"))
150
+ }
151
+ ),
152
+ /* @__PURE__ */ jsxRuntime.jsx(
153
+ "input",
154
+ {
155
+ ref: fileInputRef,
156
+ type: "file",
157
+ accept: ".json,application/json",
158
+ style: { display: "none" },
159
+ onChange: handleImport
160
+ }
161
+ ),
162
+ users.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Root, { children: [
163
+ /* @__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")) }) }),
164
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage(index.getTrad("whitelist.delete.all.title")) }),
166
+ /* @__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"), {
167
+ count: users.length
168
+ }) }) }) }),
169
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: formatMessage(index.getTrad("page.cancel")) }) }),
171
+ /* @__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")) }) })
172
+ ] })
173
+ ] })
174
+ ] })
175
+ ] })
176
+ ] }),
89
177
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, marginTop: 5, marginBottom: 5, alignItems: "flex-start", children: [
90
178
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
91
179
  designSystem.Field.Input,
@@ -137,17 +225,22 @@ function Whitelist({
137
225
  return r ? r.name : roleId;
138
226
  }).join(", ");
139
227
  let userRolesNames = getRoleNames(user.roles || []);
228
+ let isDefault = false;
140
229
  if (!userRolesNames) {
141
230
  const defaultRolesIds = oidcRoles.reduce((acc, oidc) => {
142
231
  if (oidc.role) acc.push(...oidc.role);
143
232
  return acc;
144
233
  }, []);
145
234
  userRolesNames = getRoleNames(defaultRolesIds);
235
+ isDefault = Boolean(userRolesNames);
146
236
  }
147
237
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
148
238
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: index$1 + 1 + (page - 1) * PAGE_SIZE }),
149
239
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.email }),
150
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: userRolesNames || "-" }),
240
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: userRolesNames ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
241
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: userRolesNames }),
242
+ isDefault && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "(Default)" })
243
+ ] }) : "-" }),
151
244
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: user.createdAt }) }),
152
245
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { paddingRight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
153
246
  designSystem.Flex,
@@ -351,7 +444,7 @@ function CustomSwitch({ checked, onChange, label, disabled }) {
351
444
  ] });
352
445
  }
353
446
  function useOidcSettings() {
354
- const { get, put } = admin.useFetchClient();
447
+ const { get, put, post } = admin.useFetchClient();
355
448
  const [loading, setLoading] = react.useState(false);
356
449
  const [showSuccess, setSuccess] = react.useState(false);
357
450
  const [showError, setError] = react.useState(false);
@@ -401,6 +494,31 @@ function useOidcSettings() {
401
494
  setEnforceOIDC(false);
402
495
  }
403
496
  };
497
+ const onDeleteAll = () => {
498
+ setUsers([]);
499
+ if (useWhitelist) setEnforceOIDC(false);
500
+ };
501
+ const onImport = async (entries) => {
502
+ const response = await post("/strapi-plugin-oidc/whitelist/import", { users: entries });
503
+ const refreshed = await get("/strapi-plugin-oidc/whitelist");
504
+ setUsers(refreshed.data.whitelistUsers);
505
+ setInitialUsers(JSON.parse(JSON.stringify(refreshed.data.whitelistUsers)));
506
+ return response.data.importedCount;
507
+ };
508
+ const onExport = () => {
509
+ const roleMap = new Map(roles.map((r) => [String(r.id), r.name]));
510
+ const data = users.map(({ email, roles: userRoles }) => ({
511
+ email,
512
+ roles: (userRoles || []).map((id) => roleMap.get(String(id)) ?? id)
513
+ }));
514
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
515
+ const url = URL.createObjectURL(blob);
516
+ const a = document.createElement("a");
517
+ a.href = url;
518
+ a.download = "whitelist.json";
519
+ a.click();
520
+ URL.revokeObjectURL(url);
521
+ };
404
522
  const onToggleWhitelist = (e) => {
405
523
  const checked = e.target.checked;
406
524
  setUseWhitelist(checked);
@@ -472,6 +590,9 @@ function useOidcSettings() {
472
590
  onChangeRole,
473
591
  onRegisterWhitelist,
474
592
  onDeleteWhitelist,
593
+ onDeleteAll,
594
+ onImport,
595
+ onExport,
475
596
  onToggleWhitelist,
476
597
  onToggleEnforce,
477
598
  onSaveAll
@@ -481,6 +602,7 @@ function useOidcSettings() {
481
602
  function HomePage$1() {
482
603
  const { formatMessage } = reactIntl.useIntl();
483
604
  const { state, actions } = useOidcSettings();
605
+ const blocker = reactRouterDom.useBlocker(state.isDirty);
484
606
  return /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Protect, { permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }], children: [
485
607
  /* @__PURE__ */ jsxRuntime.jsx(
486
608
  admin.Layouts.Header,
@@ -525,7 +647,10 @@ function HomePage$1() {
525
647
  oidcRoles: state.oidcRoles,
526
648
  useWhitelist: state.useWhitelist,
527
649
  onSave: actions.onRegisterWhitelist,
528
- onDelete: actions.onDeleteWhitelist
650
+ onDelete: actions.onDeleteWhitelist,
651
+ onDeleteAll: actions.onDeleteAll,
652
+ onImport: actions.onImport,
653
+ onExport: actions.onExport
529
654
  }
530
655
  )
531
656
  ] }),
@@ -564,6 +689,14 @@ function HomePage$1() {
564
689
  children: formatMessage(index.getTrad("page.save"))
565
690
  }
566
691
  ) })
692
+ ] }) }),
693
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: blocker.state === "blocked", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
694
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage(index.getTrad("unsaved.title")) }),
695
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: formatMessage(index.getTrad("unsaved.description")) }),
696
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
697
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => blocker.reset?.(), children: formatMessage(index.getTrad("unsaved.cancel")) }) }),
698
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "danger", onClick: () => blocker.proceed?.(), children: formatMessage(index.getTrad("unsaved.confirm")) }) })
699
+ ] })
567
700
  ] }) })
568
701
  ] });
569
702
  }
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const index = require("./index-Cse9ex24.js");
3
+ const index = require("./index-CZDixCh4.js");
4
4
  exports.default = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "./index-D1ypRUlq.mjs";
1
+ import { i } from "./index-8hB6LKml.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -506,6 +506,48 @@ async function removeEmail(ctx) {
506
506
  await whitelistService2.removeUser(id);
507
507
  ctx.body = {};
508
508
  }
509
+ async function deleteAll(ctx) {
510
+ await strapi.db.query("plugin::strapi-plugin-oidc.whitelists").deleteMany({});
511
+ ctx.body = {};
512
+ }
513
+ async function importUsers(ctx) {
514
+ const { users } = ctx.request.body;
515
+ if (!Array.isArray(users)) {
516
+ ctx.status = 400;
517
+ ctx.body = { error: "Expected { users: [{email, roles}] }" };
518
+ return;
519
+ }
520
+ const allRoles = await strapi.query("admin::role").findMany({});
521
+ const roleNameToId = new Map(allRoles.map((r) => [r.name, String(r.id)]));
522
+ const resolveRole = (nameOrId) => roleNameToId.get(nameOrId) ?? nameOrId;
523
+ const normalized = users.filter((u) => u?.email).map((u) => ({
524
+ email: String(u.email).trim().toLowerCase(),
525
+ roles: (Array.isArray(u.roles) ? u.roles : []).map(resolveRole)
526
+ }));
527
+ const seen = /* @__PURE__ */ new Set();
528
+ const deduped = normalized.filter((u) => {
529
+ if (seen.has(u.email)) return false;
530
+ seen.add(u.email);
531
+ return true;
532
+ });
533
+ const strapiUsers = await strapi.query("admin::user").findMany({
534
+ where: { email: { $in: deduped.map((u) => u.email) } },
535
+ populate: ["roles"]
536
+ });
537
+ const strapiUserMap = new Map(strapiUsers.map((u) => [u.email, u]));
538
+ const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
539
+ const existing = await whitelistService2.getUsers();
540
+ const existingEmails = new Set(existing.map((u) => u.email));
541
+ let importedCount = 0;
542
+ for (const user of deduped) {
543
+ if (existingEmails.has(user.email)) continue;
544
+ const strapiUser = strapiUserMap.get(user.email);
545
+ const finalRoles = strapiUser?.roles?.length ? strapiUser.roles.map((r) => String(r.id)) : user.roles;
546
+ await whitelistService2.registerUser(user.email, finalRoles);
547
+ importedCount++;
548
+ }
549
+ ctx.body = { importedCount };
550
+ }
509
551
  async function syncUsers(ctx) {
510
552
  let { users } = ctx.request.body;
511
553
  users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
@@ -547,7 +589,9 @@ const whitelist = {
547
589
  publicSettings,
548
590
  register,
549
591
  removeEmail,
550
- syncUsers
592
+ deleteAll,
593
+ syncUsers,
594
+ importUsers
551
595
  };
552
596
  const controllers = {
553
597
  oidc,
@@ -571,134 +615,133 @@ const rateLimitMiddleware = async (ctx, next) => {
571
615
  rateLimitMap.set(ip, requestStamps);
572
616
  await next();
573
617
  };
574
- const routes = [
575
- {
576
- method: "GET",
577
- path: "/oidc-roles",
578
- handler: "role.find",
579
- config: {
580
- policies: [
581
- "admin::isAuthenticatedAdmin",
582
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
583
- ]
584
- }
585
- },
586
- {
587
- method: "PUT",
588
- path: "/oidc-roles",
589
- handler: "role.update",
590
- config: {
591
- policies: [
592
- "admin::isAuthenticatedAdmin",
593
- {
594
- name: "admin::hasPermissions",
595
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
596
- }
597
- ]
598
- }
599
- },
600
- {
601
- method: "GET",
602
- path: "/oidc",
603
- handler: "oidc.oidcSignIn",
604
- config: {
605
- auth: false,
606
- middlewares: [rateLimitMiddleware]
607
- }
608
- },
609
- {
610
- method: "GET",
611
- path: "/oidc/callback",
612
- handler: "oidc.oidcSignInCallback",
613
- config: {
614
- auth: false,
615
- middlewares: [rateLimitMiddleware]
616
- }
617
- },
618
- {
619
- method: "GET",
620
- path: "/logout",
621
- handler: "oidc.logout",
622
- config: {
623
- auth: false
624
- }
625
- },
626
- {
627
- method: "GET",
628
- path: "/whitelist",
629
- handler: "whitelist.info",
630
- config: {
631
- policies: [
632
- "admin::isAuthenticatedAdmin",
633
- { name: "admin::hasPermissions", config: { actions: ["plugin::strapi-plugin-oidc.read"] } }
634
- ]
635
- }
636
- },
637
- {
638
- method: "PUT",
639
- path: "/whitelist/settings",
640
- handler: "whitelist.updateSettings",
641
- config: {
642
- policies: [
643
- "admin::isAuthenticatedAdmin",
644
- {
645
- name: "admin::hasPermissions",
646
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
647
- }
648
- ]
649
- }
650
- },
651
- {
652
- method: "GET",
653
- path: "/settings/public",
654
- handler: "whitelist.publicSettings",
655
- config: {
656
- auth: false
657
- }
658
- },
659
- {
660
- method: "PUT",
661
- path: "/whitelist/sync",
662
- handler: "whitelist.syncUsers",
663
- config: {
664
- policies: [
665
- "admin::isAuthenticatedAdmin",
666
- {
667
- name: "admin::hasPermissions",
668
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
669
- }
670
- ]
671
- }
672
- },
673
- {
674
- method: "POST",
675
- path: "/whitelist",
676
- handler: "whitelist.register",
677
- config: {
678
- policies: [
679
- "admin::isAuthenticatedAdmin",
680
- {
681
- name: "admin::hasPermissions",
682
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
683
- }
684
- ]
618
+ const adminPolicies = (action) => ({
619
+ policies: [
620
+ "admin::isAuthenticatedAdmin",
621
+ {
622
+ name: "admin::hasPermissions",
623
+ config: { actions: [`plugin::strapi-plugin-oidc.${action}`] }
685
624
  }
625
+ ]
626
+ });
627
+ const routes = {
628
+ admin: {
629
+ type: "admin",
630
+ routes: [
631
+ {
632
+ method: "GET",
633
+ path: "/oidc-roles",
634
+ handler: "role.find",
635
+ config: adminPolicies("read")
636
+ },
637
+ {
638
+ method: "PUT",
639
+ path: "/oidc-roles",
640
+ handler: "role.update",
641
+ config: adminPolicies("update")
642
+ },
643
+ {
644
+ method: "GET",
645
+ path: "/oidc",
646
+ handler: "oidc.oidcSignIn",
647
+ config: { auth: false, middlewares: [rateLimitMiddleware] }
648
+ },
649
+ {
650
+ method: "GET",
651
+ path: "/oidc/callback",
652
+ handler: "oidc.oidcSignInCallback",
653
+ config: { auth: false, middlewares: [rateLimitMiddleware] }
654
+ },
655
+ {
656
+ method: "GET",
657
+ path: "/logout",
658
+ handler: "oidc.logout",
659
+ config: { auth: false }
660
+ },
661
+ {
662
+ method: "GET",
663
+ path: "/whitelist",
664
+ handler: "whitelist.info",
665
+ config: adminPolicies("read")
666
+ },
667
+ {
668
+ method: "PUT",
669
+ path: "/whitelist/settings",
670
+ handler: "whitelist.updateSettings",
671
+ config: adminPolicies("update")
672
+ },
673
+ {
674
+ method: "GET",
675
+ path: "/settings/public",
676
+ handler: "whitelist.publicSettings",
677
+ config: { auth: false }
678
+ },
679
+ {
680
+ method: "PUT",
681
+ path: "/whitelist/sync",
682
+ handler: "whitelist.syncUsers",
683
+ config: adminPolicies("update")
684
+ },
685
+ {
686
+ method: "POST",
687
+ path: "/whitelist/import",
688
+ handler: "whitelist.importUsers",
689
+ config: adminPolicies("update")
690
+ },
691
+ {
692
+ method: "POST",
693
+ path: "/whitelist",
694
+ handler: "whitelist.register",
695
+ config: adminPolicies("update")
696
+ },
697
+ {
698
+ method: "DELETE",
699
+ path: "/whitelist/:id",
700
+ handler: "whitelist.removeEmail",
701
+ config: adminPolicies("update")
702
+ },
703
+ {
704
+ method: "DELETE",
705
+ path: "/whitelist",
706
+ handler: "whitelist.deleteAll",
707
+ config: adminPolicies("update")
708
+ }
709
+ ]
686
710
  },
687
- {
688
- method: "DELETE",
689
- path: "/whitelist/:id",
690
- handler: "whitelist.removeEmail",
691
- config: {
692
- policies: [
693
- "admin::isAuthenticatedAdmin",
694
- {
695
- name: "admin::hasPermissions",
696
- config: { actions: ["plugin::strapi-plugin-oidc.update"] }
697
- }
698
- ]
699
- }
711
+ // API-token-authenticated routes for programmatic whitelist management.
712
+ // Accessible at /strapi-plugin-oidc/... using a Strapi API token
713
+ // (full-access or custom) in the Authorization: Bearer <token> header.
714
+ "content-api": {
715
+ type: "content-api",
716
+ routes: [
717
+ {
718
+ method: "GET",
719
+ path: "/whitelist",
720
+ handler: "whitelist.info"
721
+ },
722
+ {
723
+ method: "POST",
724
+ path: "/whitelist",
725
+ handler: "whitelist.register"
726
+ },
727
+ {
728
+ method: "POST",
729
+ path: "/whitelist/import",
730
+ handler: "whitelist.importUsers"
731
+ },
732
+ {
733
+ method: "DELETE",
734
+ path: "/whitelist/:id",
735
+ handler: "whitelist.removeEmail"
736
+ },
737
+ {
738
+ method: "DELETE",
739
+ path: "/whitelist",
740
+ handler: "whitelist.deleteAll"
741
+ }
742
+ ]
700
743
  }
701
- ];
744
+ };
702
745
  const policies = {};
703
746
  function renderHtmlTemplate(title, content) {
704
747
  return `