strapi-plugin-oidc 1.0.5 → 1.0.6
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 +16 -2
- package/dist/admin/{index-B525UaV3.js → index-BITZIRCD.js} +1 -1
- package/dist/admin/{index-DlQ8NUBY.js → index-CLDWnBI9.js} +14 -6
- package/dist/admin/{index-D3AvxXlB.mjs → index-YOG9buUz.mjs} +1 -1
- package/dist/admin/{index-BbD-7Z4N.mjs → index-p9ncVp1G.mjs} +14 -6
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +225 -65
- package/dist/server/index.mjs +225 -65
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
<img src="https://raw.githubusercontent.com/edmogeor/strapi-plugin-oidc/main/assets/icon.png" width="140" alt="OIDC Login for Strapi Logo"/>
|
|
3
3
|
<h1>OIDC Login for Strapi</h1>
|
|
4
4
|
<p>
|
|
5
|
+
<a href="https://www.npmjs.com/package/strapi-plugin-oidc">
|
|
6
|
+
<img src="https://img.shields.io/npm/v/strapi-plugin-oidc.svg" alt="npm version">
|
|
7
|
+
</a>
|
|
5
8
|
<a href="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/test.yml">
|
|
6
|
-
<img src="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/test.yml/badge.svg" alt="Tests">
|
|
9
|
+
<img src="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/test.yml/badge.svg?branch=main" alt="Tests">
|
|
7
10
|
</a>
|
|
8
11
|
</p>
|
|
9
12
|
</div>
|
|
@@ -67,13 +70,22 @@ module.exports = ({ env }) => ({
|
|
|
67
70
|
|
|
68
71
|
Make sure to replace the placeholder values (e.g., `[Client ID from OpenID Provider]`) with the actual connection details from your chosen OIDC identity provider.
|
|
69
72
|
|
|
73
|
+
## How to Login
|
|
74
|
+
|
|
75
|
+
Once configured, you can initiate the OIDC login flow by navigating to:
|
|
76
|
+
`http://<your-strapi-domain>/strapi-plugin-oidc/oidc`
|
|
77
|
+
|
|
78
|
+
(e.g., `http://localhost:1337/strapi-plugin-oidc/oidc` for local development).
|
|
79
|
+
|
|
80
|
+
When the **Enforce OIDC Login** option is enabled in the Admin Settings, the standard Strapi admin login page will be automatically redirected to this URL.
|
|
81
|
+
|
|
70
82
|
## Admin Settings
|
|
71
83
|
|
|
72
84
|
Once the plugin is installed and configured, you can manage the OIDC settings from the Strapi Admin Panel under **Settings** > **OIDC Plugin**.
|
|
73
85
|
|
|
74
86
|
- **Whitelist Management**: Restrict login to specific users by adding their email addresses to the whitelist. You can also whitelist entire email domains (e.g., `*@company.com`). If the whitelist is empty, any user who successfully authenticates via your OIDC provider will be able to log in and an account will be automatically created for them.
|
|
75
87
|
- **Default Role Assignment**: Select the default Strapi admin role that will be assigned to newly created users when they log in for the first time via OIDC.
|
|
76
|
-
- **Enforce OIDC Login**: When enabled, the default Strapi email and password login form will be disabled, forcing all administrators to log in using your OIDC provider. _(Note: This option is automatically disabled and grayed out if your whitelist is empty to prevent accidentally locking everyone out of the admin panel)._
|
|
88
|
+
- **Enforce OIDC Login**: When enabled, the default Strapi email and password login form will be disabled and the standard login will be redirected to the OIDC login URL, forcing all administrators to log in using your OIDC provider. _(Note: This option is automatically disabled and grayed out if your whitelist is empty to prevent accidentally locking everyone out of the admin panel)._
|
|
77
89
|
|
|
78
90
|
## Credits & Changes
|
|
79
91
|
|
|
@@ -85,7 +97,9 @@ This plugin is a hard fork of the original [`strapi-plugin-sso`](https://github.
|
|
|
85
97
|
- Redesigned the Whitelist and Role management UI (switched to native Strapi cards, added pagination, etc.).
|
|
86
98
|
- Added an OIDC logout redirect URL.
|
|
87
99
|
- Added an option to "Enforce OIDC login" with an admin toggle (automatically disabled if the whitelist is empty).
|
|
100
|
+
- Added configurable "Remember Me" duration for sessions (`REMEMBER_ME_DAYS`).
|
|
88
101
|
- Migrated the testing framework to Vitest and added comprehensive test coverage for controllers and services.
|
|
89
102
|
- Cleaned up dead code and unused dependencies to improve maintainability.
|
|
90
103
|
- Upgraded to use newer versions of Node.js.
|
|
104
|
+
- Added styled success and error pages.
|
|
91
105
|
- Added misc. quality of life improvements and bug fixes.
|
|
@@ -55,7 +55,7 @@ const index = {
|
|
|
55
55
|
defaultMessage: "Configuration"
|
|
56
56
|
},
|
|
57
57
|
Component: async () => {
|
|
58
|
-
return await Promise.resolve().then(() => require("./index-
|
|
58
|
+
return await Promise.resolve().then(() => require("./index-CLDWnBI9.js"));
|
|
59
59
|
},
|
|
60
60
|
permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }]
|
|
61
61
|
}
|
|
@@ -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-
|
|
10
|
+
const index = require("./index-BITZIRCD.js");
|
|
11
11
|
const en = require("./en-jFPbEFeK.js");
|
|
12
12
|
const styled = require("styled-components");
|
|
13
13
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
@@ -395,10 +395,18 @@ function useOidcSettings() {
|
|
|
395
395
|
setUsers([...users, newUser]);
|
|
396
396
|
};
|
|
397
397
|
const onDeleteWhitelist = async (email) => {
|
|
398
|
-
|
|
398
|
+
const updatedUsers = users.filter((u) => u.email !== email);
|
|
399
|
+
setUsers(updatedUsers);
|
|
400
|
+
if (useWhitelist && updatedUsers.length === 0) {
|
|
401
|
+
setEnforceOIDC(false);
|
|
402
|
+
}
|
|
399
403
|
};
|
|
400
404
|
const onToggleWhitelist = (e) => {
|
|
401
|
-
|
|
405
|
+
const checked = e.target.checked;
|
|
406
|
+
setUseWhitelist(checked);
|
|
407
|
+
if (checked && users.length === 0) {
|
|
408
|
+
setEnforceOIDC(false);
|
|
409
|
+
}
|
|
402
410
|
};
|
|
403
411
|
const onToggleEnforce = (e) => {
|
|
404
412
|
setEnforceOIDC(e.target.checked);
|
|
@@ -413,13 +421,13 @@ function useOidcSettings() {
|
|
|
413
421
|
role: role.role
|
|
414
422
|
}))
|
|
415
423
|
});
|
|
424
|
+
const syncResponse = await put("/strapi-plugin-oidc/whitelist/sync", {
|
|
425
|
+
users: users.map((u) => ({ email: u.email, roles: u.roles }))
|
|
426
|
+
});
|
|
416
427
|
await put("/strapi-plugin-oidc/whitelist/settings", {
|
|
417
428
|
useWhitelist,
|
|
418
429
|
enforceOIDC
|
|
419
430
|
});
|
|
420
|
-
const syncResponse = await put("/strapi-plugin-oidc/whitelist/sync", {
|
|
421
|
-
users: users.map((u) => ({ email: u.email, roles: u.roles }))
|
|
422
|
-
});
|
|
423
431
|
setInitialOIDCRoles(JSON.parse(JSON.stringify(oidcRoles)));
|
|
424
432
|
setInitialUseWhitelist(useWhitelist);
|
|
425
433
|
setInitialEnforceOIDC(enforceOIDC);
|
|
@@ -54,7 +54,7 @@ const index = {
|
|
|
54
54
|
defaultMessage: "Configuration"
|
|
55
55
|
},
|
|
56
56
|
Component: async () => {
|
|
57
|
-
return await import("./index-
|
|
57
|
+
return await import("./index-p9ncVp1G.mjs");
|
|
58
58
|
},
|
|
59
59
|
permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }]
|
|
60
60
|
}
|
|
@@ -5,7 +5,7 @@ import { useState, useCallback, useEffect, memo } from "react";
|
|
|
5
5
|
import { Typography, Flex, Box, MultiSelect, MultiSelectOption, Field, Button, Divider, Thead, Tr, Th, Tbody, Td, Dialog, IconButton, Pagination, PreviousLink, PageLink, NextLink, Table, Alert } from "@strapi/design-system";
|
|
6
6
|
import { Plus, Trash, WarningCircle } from "@strapi/icons";
|
|
7
7
|
import { useIntl } from "react-intl";
|
|
8
|
-
import { p as pluginId } from "./index-
|
|
8
|
+
import { p as pluginId } from "./index-YOG9buUz.mjs";
|
|
9
9
|
import en from "./en-f0TxVfx7.mjs";
|
|
10
10
|
import styled from "styled-components";
|
|
11
11
|
function getTrad(id) {
|
|
@@ -391,10 +391,18 @@ function useOidcSettings() {
|
|
|
391
391
|
setUsers([...users, newUser]);
|
|
392
392
|
};
|
|
393
393
|
const onDeleteWhitelist = async (email) => {
|
|
394
|
-
|
|
394
|
+
const updatedUsers = users.filter((u) => u.email !== email);
|
|
395
|
+
setUsers(updatedUsers);
|
|
396
|
+
if (useWhitelist && updatedUsers.length === 0) {
|
|
397
|
+
setEnforceOIDC(false);
|
|
398
|
+
}
|
|
395
399
|
};
|
|
396
400
|
const onToggleWhitelist = (e) => {
|
|
397
|
-
|
|
401
|
+
const checked = e.target.checked;
|
|
402
|
+
setUseWhitelist(checked);
|
|
403
|
+
if (checked && users.length === 0) {
|
|
404
|
+
setEnforceOIDC(false);
|
|
405
|
+
}
|
|
398
406
|
};
|
|
399
407
|
const onToggleEnforce = (e) => {
|
|
400
408
|
setEnforceOIDC(e.target.checked);
|
|
@@ -409,13 +417,13 @@ function useOidcSettings() {
|
|
|
409
417
|
role: role.role
|
|
410
418
|
}))
|
|
411
419
|
});
|
|
420
|
+
const syncResponse = await put("/strapi-plugin-oidc/whitelist/sync", {
|
|
421
|
+
users: users.map((u) => ({ email: u.email, roles: u.roles }))
|
|
422
|
+
});
|
|
412
423
|
await put("/strapi-plugin-oidc/whitelist/settings", {
|
|
413
424
|
useWhitelist,
|
|
414
425
|
enforceOIDC
|
|
415
426
|
});
|
|
416
|
-
const syncResponse = await put("/strapi-plugin-oidc/whitelist/sync", {
|
|
417
|
-
users: users.map((u) => ({ email: u.email, roles: u.roles }))
|
|
418
|
-
});
|
|
419
427
|
setInitialOIDCRoles(JSON.parse(JSON.stringify(oidcRoles)));
|
|
420
428
|
setInitialUseWhitelist(useWhitelist);
|
|
421
429
|
setInitialEnforceOIDC(enforceOIDC);
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -201,7 +201,7 @@ async function registerNewUser(userService, oauthService2, roleService2, email,
|
|
|
201
201
|
return activateUser;
|
|
202
202
|
}
|
|
203
203
|
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
204
|
-
const email = userResponseData.email;
|
|
204
|
+
const email = String(userResponseData.email).toLowerCase();
|
|
205
205
|
const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
|
|
206
206
|
const dbUser = await userService.findOneByEmail(email);
|
|
207
207
|
let activateUser;
|
|
@@ -316,8 +316,14 @@ async function info(ctx) {
|
|
|
316
316
|
};
|
|
317
317
|
}
|
|
318
318
|
async function updateSettings(ctx) {
|
|
319
|
-
|
|
319
|
+
let { useWhitelist, enforceOIDC } = ctx.request.body;
|
|
320
320
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
321
|
+
if (useWhitelist && enforceOIDC) {
|
|
322
|
+
const users = await whitelistService2.getUsers();
|
|
323
|
+
if (users.length === 0) {
|
|
324
|
+
enforceOIDC = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
321
327
|
await whitelistService2.setSettings({ useWhitelist, enforceOIDC });
|
|
322
328
|
ctx.body = { useWhitelist, enforceOIDC };
|
|
323
329
|
}
|
|
@@ -334,7 +340,8 @@ async function register(ctx) {
|
|
|
334
340
|
ctx.body = { message: "Please enter a valid email address" };
|
|
335
341
|
return;
|
|
336
342
|
}
|
|
337
|
-
const
|
|
343
|
+
const rawEmails = Array.isArray(email) ? email : email.split(",");
|
|
344
|
+
const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
338
345
|
const existingUsers = await strapi.query("admin::user").findMany({
|
|
339
346
|
where: { email: { $in: emailList } },
|
|
340
347
|
populate: ["roles"]
|
|
@@ -364,7 +371,8 @@ async function removeEmail(ctx) {
|
|
|
364
371
|
ctx.body = {};
|
|
365
372
|
}
|
|
366
373
|
async function syncUsers(ctx) {
|
|
367
|
-
|
|
374
|
+
let { users } = ctx.request.body;
|
|
375
|
+
users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
|
|
368
376
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
369
377
|
const currentUsers = await whitelistService2.getUsers();
|
|
370
378
|
let matchedExistingUsersCount = 0;
|
|
@@ -410,6 +418,23 @@ const controllers = {
|
|
|
410
418
|
role,
|
|
411
419
|
whitelist
|
|
412
420
|
};
|
|
421
|
+
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
422
|
+
const RATE_LIMIT_WINDOW = 6e4;
|
|
423
|
+
const MAX_REQUESTS = 20;
|
|
424
|
+
const rateLimitMiddleware = async (ctx, next) => {
|
|
425
|
+
const ip = ctx.request.ip;
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
const windowStart = now - RATE_LIMIT_WINDOW;
|
|
428
|
+
const requestStamps = (rateLimitMap.get(ip) || []).filter((timestamp) => timestamp > windowStart);
|
|
429
|
+
if (requestStamps.length >= MAX_REQUESTS) {
|
|
430
|
+
ctx.status = 429;
|
|
431
|
+
ctx.body = "Too Many Requests";
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
requestStamps.push(now);
|
|
435
|
+
rateLimitMap.set(ip, requestStamps);
|
|
436
|
+
await next();
|
|
437
|
+
};
|
|
413
438
|
const routes = [
|
|
414
439
|
{
|
|
415
440
|
method: "GET",
|
|
@@ -429,7 +454,10 @@ const routes = [
|
|
|
429
454
|
config: {
|
|
430
455
|
policies: [
|
|
431
456
|
"admin::isAuthenticatedAdmin",
|
|
432
|
-
{
|
|
457
|
+
{
|
|
458
|
+
name: "admin::hasPermissions",
|
|
459
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
460
|
+
}
|
|
433
461
|
]
|
|
434
462
|
}
|
|
435
463
|
},
|
|
@@ -438,7 +466,8 @@ const routes = [
|
|
|
438
466
|
path: "/oidc",
|
|
439
467
|
handler: "oidc.oidcSignIn",
|
|
440
468
|
config: {
|
|
441
|
-
auth: false
|
|
469
|
+
auth: false,
|
|
470
|
+
middlewares: [rateLimitMiddleware]
|
|
442
471
|
}
|
|
443
472
|
},
|
|
444
473
|
{
|
|
@@ -446,7 +475,8 @@ const routes = [
|
|
|
446
475
|
path: "/oidc/callback",
|
|
447
476
|
handler: "oidc.oidcSignInCallback",
|
|
448
477
|
config: {
|
|
449
|
-
auth: false
|
|
478
|
+
auth: false,
|
|
479
|
+
middlewares: [rateLimitMiddleware]
|
|
450
480
|
}
|
|
451
481
|
},
|
|
452
482
|
{
|
|
@@ -475,7 +505,10 @@ const routes = [
|
|
|
475
505
|
config: {
|
|
476
506
|
policies: [
|
|
477
507
|
"admin::isAuthenticatedAdmin",
|
|
478
|
-
{
|
|
508
|
+
{
|
|
509
|
+
name: "admin::hasPermissions",
|
|
510
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
511
|
+
}
|
|
479
512
|
]
|
|
480
513
|
}
|
|
481
514
|
},
|
|
@@ -494,7 +527,10 @@ const routes = [
|
|
|
494
527
|
config: {
|
|
495
528
|
policies: [
|
|
496
529
|
"admin::isAuthenticatedAdmin",
|
|
497
|
-
{
|
|
530
|
+
{
|
|
531
|
+
name: "admin::hasPermissions",
|
|
532
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
533
|
+
}
|
|
498
534
|
]
|
|
499
535
|
}
|
|
500
536
|
},
|
|
@@ -505,7 +541,10 @@ const routes = [
|
|
|
505
541
|
config: {
|
|
506
542
|
policies: [
|
|
507
543
|
"admin::isAuthenticatedAdmin",
|
|
508
|
-
{
|
|
544
|
+
{
|
|
545
|
+
name: "admin::hasPermissions",
|
|
546
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
547
|
+
}
|
|
509
548
|
]
|
|
510
549
|
}
|
|
511
550
|
},
|
|
@@ -516,12 +555,132 @@ const routes = [
|
|
|
516
555
|
config: {
|
|
517
556
|
policies: [
|
|
518
557
|
"admin::isAuthenticatedAdmin",
|
|
519
|
-
{
|
|
558
|
+
{
|
|
559
|
+
name: "admin::hasPermissions",
|
|
560
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
561
|
+
}
|
|
520
562
|
]
|
|
521
563
|
}
|
|
522
564
|
}
|
|
523
565
|
];
|
|
524
566
|
const policies = {};
|
|
567
|
+
function renderHtmlTemplate(title, content) {
|
|
568
|
+
return `
|
|
569
|
+
<!doctype html>
|
|
570
|
+
<html lang="en">
|
|
571
|
+
<head>
|
|
572
|
+
<meta charset="utf-8">
|
|
573
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
574
|
+
<title>${title}</title>
|
|
575
|
+
<style>
|
|
576
|
+
:root {
|
|
577
|
+
--bg-color: #f6f6f9;
|
|
578
|
+
--card-bg: #ffffff;
|
|
579
|
+
--text-color: #32324d;
|
|
580
|
+
--text-muted: #666687;
|
|
581
|
+
--btn-bg: #4945ff;
|
|
582
|
+
--btn-hover: #271fe0;
|
|
583
|
+
--btn-text: #ffffff;
|
|
584
|
+
--icon-bg: #fcecea;
|
|
585
|
+
--icon-color: #d02b20;
|
|
586
|
+
--success-bg: #eafbe7;
|
|
587
|
+
--success-color: #328048;
|
|
588
|
+
--shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
|
|
589
|
+
}
|
|
590
|
+
@media (prefers-color-scheme: dark) {
|
|
591
|
+
:root {
|
|
592
|
+
--bg-color: #181826;
|
|
593
|
+
--card-bg: #212134;
|
|
594
|
+
--text-color: #ffffff;
|
|
595
|
+
--text-muted: #a5a5ba;
|
|
596
|
+
--btn-bg: #4945ff;
|
|
597
|
+
--btn-hover: #7b79ff;
|
|
598
|
+
--btn-text: #ffffff;
|
|
599
|
+
--icon-bg: #4a2123;
|
|
600
|
+
--icon-color: #f23628;
|
|
601
|
+
--success-bg: #1c3523;
|
|
602
|
+
--success-color: #55ca76;
|
|
603
|
+
--shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
body {
|
|
607
|
+
margin: 0;
|
|
608
|
+
padding: 0;
|
|
609
|
+
display: flex;
|
|
610
|
+
justify-content: center;
|
|
611
|
+
align-items: center;
|
|
612
|
+
height: 100vh;
|
|
613
|
+
background-color: var(--bg-color);
|
|
614
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
615
|
+
color: var(--text-color);
|
|
616
|
+
}
|
|
617
|
+
.card {
|
|
618
|
+
background: var(--card-bg);
|
|
619
|
+
padding: 32px 40px;
|
|
620
|
+
border-radius: 8px;
|
|
621
|
+
box-shadow: var(--shadow);
|
|
622
|
+
max-width: 400px;
|
|
623
|
+
width: 100%;
|
|
624
|
+
text-align: center;
|
|
625
|
+
box-sizing: border-box;
|
|
626
|
+
}
|
|
627
|
+
.icon {
|
|
628
|
+
width: 48px;
|
|
629
|
+
height: 48px;
|
|
630
|
+
background-color: var(--icon-bg);
|
|
631
|
+
color: var(--icon-color);
|
|
632
|
+
border-radius: 50%;
|
|
633
|
+
display: inline-flex;
|
|
634
|
+
justify-content: center;
|
|
635
|
+
align-items: center;
|
|
636
|
+
margin-bottom: 24px;
|
|
637
|
+
}
|
|
638
|
+
.icon.success {
|
|
639
|
+
background-color: var(--success-bg);
|
|
640
|
+
color: var(--success-color);
|
|
641
|
+
}
|
|
642
|
+
.icon svg {
|
|
643
|
+
width: 24px;
|
|
644
|
+
height: 24px;
|
|
645
|
+
stroke: currentColor;
|
|
646
|
+
stroke-width: 2;
|
|
647
|
+
stroke-linecap: round;
|
|
648
|
+
stroke-linejoin: round;
|
|
649
|
+
fill: none;
|
|
650
|
+
}
|
|
651
|
+
h1 {
|
|
652
|
+
margin: 0 0 12px 0;
|
|
653
|
+
font-size: 20px;
|
|
654
|
+
font-weight: 600;
|
|
655
|
+
color: var(--text-color);
|
|
656
|
+
}
|
|
657
|
+
p {
|
|
658
|
+
margin: 0 0 32px 0;
|
|
659
|
+
font-size: 14px;
|
|
660
|
+
line-height: 1.5;
|
|
661
|
+
color: var(--text-muted);
|
|
662
|
+
}
|
|
663
|
+
.btn {
|
|
664
|
+
display: inline-block;
|
|
665
|
+
background-color: var(--btn-bg);
|
|
666
|
+
color: var(--btn-text);
|
|
667
|
+
padding: 10px 16px;
|
|
668
|
+
border-radius: 4px;
|
|
669
|
+
text-decoration: none;
|
|
670
|
+
font-size: 14px;
|
|
671
|
+
font-weight: 500;
|
|
672
|
+
transition: background-color 0.2s;
|
|
673
|
+
}
|
|
674
|
+
.btn:hover {
|
|
675
|
+
background-color: var(--btn-hover);
|
|
676
|
+
}
|
|
677
|
+
</style>
|
|
678
|
+
</head>
|
|
679
|
+
<body>
|
|
680
|
+
${content}
|
|
681
|
+
</body>
|
|
682
|
+
</html>`;
|
|
683
|
+
}
|
|
525
684
|
function oauthService({ strapi: strapi2 }) {
|
|
526
685
|
return {
|
|
527
686
|
async createUser(email, lastname, firstname, locale, roles2 = []) {
|
|
@@ -602,40 +761,48 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
602
761
|
const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
|
|
603
762
|
const REMEMBER_ME = config2["REMEMBER_ME"];
|
|
604
763
|
const isRememberMe = !!REMEMBER_ME;
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
<
|
|
608
|
-
<
|
|
609
|
-
<
|
|
610
|
-
<
|
|
611
|
-
</
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
764
|
+
const content = `
|
|
765
|
+
<noscript>
|
|
766
|
+
<div class="card">
|
|
767
|
+
<div class="icon success">
|
|
768
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check">
|
|
769
|
+
<path d="M20 6 9 17l-5-5"/>
|
|
770
|
+
</svg>
|
|
771
|
+
</div>
|
|
772
|
+
<h1>JavaScript Required</h1>
|
|
773
|
+
<p>JavaScript must be enabled for authentication to complete.</p>
|
|
774
|
+
</div>
|
|
775
|
+
</noscript>
|
|
776
|
+
<script nonce="${nonce}">
|
|
777
|
+
window.addEventListener('load', function() {
|
|
778
|
+
if(${isRememberMe}){
|
|
779
|
+
localStorage.setItem('jwtToken', '"${jwtToken}"');
|
|
780
|
+
}else{
|
|
781
|
+
document.cookie = 'jwtToken=${encodeURIComponent(jwtToken)}; Path=/';
|
|
782
|
+
}
|
|
783
|
+
localStorage.setItem('isLoggedIn', 'true');
|
|
784
|
+
location.href = '${strapi2.config.admin.url}'
|
|
785
|
+
})
|
|
786
|
+
<\/script>`;
|
|
787
|
+
return renderHtmlTemplate("Authenticating...", content);
|
|
627
788
|
},
|
|
628
789
|
// Sign In Error
|
|
629
790
|
renderSignUpError(message) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
<
|
|
633
|
-
<
|
|
634
|
-
<
|
|
635
|
-
<
|
|
636
|
-
<
|
|
637
|
-
|
|
638
|
-
</
|
|
791
|
+
const safeMessage = String(message).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
792
|
+
const content = `
|
|
793
|
+
<div class="card">
|
|
794
|
+
<div class="icon">
|
|
795
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert">
|
|
796
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/>
|
|
797
|
+
<path d="M12 9v4"/>
|
|
798
|
+
<path d="M12 17h.01"/>
|
|
799
|
+
</svg>
|
|
800
|
+
</div>
|
|
801
|
+
<h1>Authentication Failed</h1>
|
|
802
|
+
<p>${safeMessage}</p>
|
|
803
|
+
<a href="${strapi2.config.admin.url}" class="btn">Return to Login</a>
|
|
804
|
+
</div>`;
|
|
805
|
+
return renderHtmlTemplate("Authentication Failed", content);
|
|
639
806
|
},
|
|
640
807
|
async generateToken(user, ctx) {
|
|
641
808
|
const sessionManager = strapi2.sessionManager;
|
|
@@ -729,39 +896,35 @@ function roleService({ strapi: strapi2 }) {
|
|
|
729
896
|
};
|
|
730
897
|
}
|
|
731
898
|
function whitelistService({ strapi: strapi2 }) {
|
|
899
|
+
const getPluginStore = () => strapi2.store({
|
|
900
|
+
environment: "",
|
|
901
|
+
type: "plugin",
|
|
902
|
+
name: "strapi-plugin-oidc"
|
|
903
|
+
});
|
|
904
|
+
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
732
905
|
return {
|
|
733
906
|
async getSettings() {
|
|
734
|
-
|
|
735
|
-
let settings = await pluginStore.get({ key: "settings" });
|
|
907
|
+
let settings = await getPluginStore().get({ key: "settings" });
|
|
736
908
|
if (!settings) {
|
|
737
909
|
settings = { useWhitelist: true, enforceOIDC: false };
|
|
738
|
-
await
|
|
910
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
739
911
|
}
|
|
740
912
|
return settings;
|
|
741
913
|
},
|
|
742
914
|
async setSettings(settings) {
|
|
743
|
-
|
|
744
|
-
await pluginStore.set({ key: "settings", value: settings });
|
|
915
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
745
916
|
},
|
|
746
917
|
async getUsers() {
|
|
747
|
-
|
|
748
|
-
return query.findMany();
|
|
918
|
+
return getWhitelistQuery().findMany();
|
|
749
919
|
},
|
|
750
920
|
async registerUser(email, roles2) {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
data: {
|
|
754
|
-
email,
|
|
755
|
-
roles: roles2
|
|
756
|
-
}
|
|
921
|
+
await getWhitelistQuery().create({
|
|
922
|
+
data: { email, roles: roles2 }
|
|
757
923
|
});
|
|
758
924
|
},
|
|
759
925
|
async removeUser(id) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
where: {
|
|
763
|
-
id
|
|
764
|
-
}
|
|
926
|
+
await getWhitelistQuery().delete({
|
|
927
|
+
where: { id }
|
|
765
928
|
});
|
|
766
929
|
},
|
|
767
930
|
async checkWhitelistForEmail(email) {
|
|
@@ -770,11 +933,8 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
770
933
|
if (!settings.useWhitelist) {
|
|
771
934
|
return null;
|
|
772
935
|
}
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
where: {
|
|
776
|
-
email
|
|
777
|
-
}
|
|
936
|
+
const result = await getWhitelistQuery().findOne({
|
|
937
|
+
where: { email }
|
|
778
938
|
});
|
|
779
939
|
console.log("checkWhitelistForEmail result:", result);
|
|
780
940
|
if (!result) {
|
package/dist/server/index.mjs
CHANGED
|
@@ -194,7 +194,7 @@ async function registerNewUser(userService, oauthService2, roleService2, email,
|
|
|
194
194
|
return activateUser;
|
|
195
195
|
}
|
|
196
196
|
async function handleUserAuthentication(userService, oauthService2, roleService2, whitelistService2, userResponseData, config2, ctx) {
|
|
197
|
-
const email = userResponseData.email;
|
|
197
|
+
const email = String(userResponseData.email).toLowerCase();
|
|
198
198
|
const whitelistUser = await whitelistService2.checkWhitelistForEmail(email);
|
|
199
199
|
const dbUser = await userService.findOneByEmail(email);
|
|
200
200
|
let activateUser;
|
|
@@ -309,8 +309,14 @@ async function info(ctx) {
|
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
311
|
async function updateSettings(ctx) {
|
|
312
|
-
|
|
312
|
+
let { useWhitelist, enforceOIDC } = ctx.request.body;
|
|
313
313
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
314
|
+
if (useWhitelist && enforceOIDC) {
|
|
315
|
+
const users = await whitelistService2.getUsers();
|
|
316
|
+
if (users.length === 0) {
|
|
317
|
+
enforceOIDC = false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
314
320
|
await whitelistService2.setSettings({ useWhitelist, enforceOIDC });
|
|
315
321
|
ctx.body = { useWhitelist, enforceOIDC };
|
|
316
322
|
}
|
|
@@ -327,7 +333,8 @@ async function register(ctx) {
|
|
|
327
333
|
ctx.body = { message: "Please enter a valid email address" };
|
|
328
334
|
return;
|
|
329
335
|
}
|
|
330
|
-
const
|
|
336
|
+
const rawEmails = Array.isArray(email) ? email : email.split(",");
|
|
337
|
+
const emailList = rawEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
331
338
|
const existingUsers = await strapi.query("admin::user").findMany({
|
|
332
339
|
where: { email: { $in: emailList } },
|
|
333
340
|
populate: ["roles"]
|
|
@@ -357,7 +364,8 @@ async function removeEmail(ctx) {
|
|
|
357
364
|
ctx.body = {};
|
|
358
365
|
}
|
|
359
366
|
async function syncUsers(ctx) {
|
|
360
|
-
|
|
367
|
+
let { users } = ctx.request.body;
|
|
368
|
+
users = users.map((u) => ({ ...u, email: String(u.email).toLowerCase() }));
|
|
361
369
|
const whitelistService2 = strapi.plugin("strapi-plugin-oidc").service("whitelist");
|
|
362
370
|
const currentUsers = await whitelistService2.getUsers();
|
|
363
371
|
let matchedExistingUsersCount = 0;
|
|
@@ -403,6 +411,23 @@ const controllers = {
|
|
|
403
411
|
role,
|
|
404
412
|
whitelist
|
|
405
413
|
};
|
|
414
|
+
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
415
|
+
const RATE_LIMIT_WINDOW = 6e4;
|
|
416
|
+
const MAX_REQUESTS = 20;
|
|
417
|
+
const rateLimitMiddleware = async (ctx, next) => {
|
|
418
|
+
const ip = ctx.request.ip;
|
|
419
|
+
const now = Date.now();
|
|
420
|
+
const windowStart = now - RATE_LIMIT_WINDOW;
|
|
421
|
+
const requestStamps = (rateLimitMap.get(ip) || []).filter((timestamp) => timestamp > windowStart);
|
|
422
|
+
if (requestStamps.length >= MAX_REQUESTS) {
|
|
423
|
+
ctx.status = 429;
|
|
424
|
+
ctx.body = "Too Many Requests";
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
requestStamps.push(now);
|
|
428
|
+
rateLimitMap.set(ip, requestStamps);
|
|
429
|
+
await next();
|
|
430
|
+
};
|
|
406
431
|
const routes = [
|
|
407
432
|
{
|
|
408
433
|
method: "GET",
|
|
@@ -422,7 +447,10 @@ const routes = [
|
|
|
422
447
|
config: {
|
|
423
448
|
policies: [
|
|
424
449
|
"admin::isAuthenticatedAdmin",
|
|
425
|
-
{
|
|
450
|
+
{
|
|
451
|
+
name: "admin::hasPermissions",
|
|
452
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
453
|
+
}
|
|
426
454
|
]
|
|
427
455
|
}
|
|
428
456
|
},
|
|
@@ -431,7 +459,8 @@ const routes = [
|
|
|
431
459
|
path: "/oidc",
|
|
432
460
|
handler: "oidc.oidcSignIn",
|
|
433
461
|
config: {
|
|
434
|
-
auth: false
|
|
462
|
+
auth: false,
|
|
463
|
+
middlewares: [rateLimitMiddleware]
|
|
435
464
|
}
|
|
436
465
|
},
|
|
437
466
|
{
|
|
@@ -439,7 +468,8 @@ const routes = [
|
|
|
439
468
|
path: "/oidc/callback",
|
|
440
469
|
handler: "oidc.oidcSignInCallback",
|
|
441
470
|
config: {
|
|
442
|
-
auth: false
|
|
471
|
+
auth: false,
|
|
472
|
+
middlewares: [rateLimitMiddleware]
|
|
443
473
|
}
|
|
444
474
|
},
|
|
445
475
|
{
|
|
@@ -468,7 +498,10 @@ const routes = [
|
|
|
468
498
|
config: {
|
|
469
499
|
policies: [
|
|
470
500
|
"admin::isAuthenticatedAdmin",
|
|
471
|
-
{
|
|
501
|
+
{
|
|
502
|
+
name: "admin::hasPermissions",
|
|
503
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
504
|
+
}
|
|
472
505
|
]
|
|
473
506
|
}
|
|
474
507
|
},
|
|
@@ -487,7 +520,10 @@ const routes = [
|
|
|
487
520
|
config: {
|
|
488
521
|
policies: [
|
|
489
522
|
"admin::isAuthenticatedAdmin",
|
|
490
|
-
{
|
|
523
|
+
{
|
|
524
|
+
name: "admin::hasPermissions",
|
|
525
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
526
|
+
}
|
|
491
527
|
]
|
|
492
528
|
}
|
|
493
529
|
},
|
|
@@ -498,7 +534,10 @@ const routes = [
|
|
|
498
534
|
config: {
|
|
499
535
|
policies: [
|
|
500
536
|
"admin::isAuthenticatedAdmin",
|
|
501
|
-
{
|
|
537
|
+
{
|
|
538
|
+
name: "admin::hasPermissions",
|
|
539
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
540
|
+
}
|
|
502
541
|
]
|
|
503
542
|
}
|
|
504
543
|
},
|
|
@@ -509,12 +548,132 @@ const routes = [
|
|
|
509
548
|
config: {
|
|
510
549
|
policies: [
|
|
511
550
|
"admin::isAuthenticatedAdmin",
|
|
512
|
-
{
|
|
551
|
+
{
|
|
552
|
+
name: "admin::hasPermissions",
|
|
553
|
+
config: { actions: ["plugin::strapi-plugin-oidc.update"] }
|
|
554
|
+
}
|
|
513
555
|
]
|
|
514
556
|
}
|
|
515
557
|
}
|
|
516
558
|
];
|
|
517
559
|
const policies = {};
|
|
560
|
+
function renderHtmlTemplate(title, content) {
|
|
561
|
+
return `
|
|
562
|
+
<!doctype html>
|
|
563
|
+
<html lang="en">
|
|
564
|
+
<head>
|
|
565
|
+
<meta charset="utf-8">
|
|
566
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
567
|
+
<title>${title}</title>
|
|
568
|
+
<style>
|
|
569
|
+
:root {
|
|
570
|
+
--bg-color: #f6f6f9;
|
|
571
|
+
--card-bg: #ffffff;
|
|
572
|
+
--text-color: #32324d;
|
|
573
|
+
--text-muted: #666687;
|
|
574
|
+
--btn-bg: #4945ff;
|
|
575
|
+
--btn-hover: #271fe0;
|
|
576
|
+
--btn-text: #ffffff;
|
|
577
|
+
--icon-bg: #fcecea;
|
|
578
|
+
--icon-color: #d02b20;
|
|
579
|
+
--success-bg: #eafbe7;
|
|
580
|
+
--success-color: #328048;
|
|
581
|
+
--shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
|
|
582
|
+
}
|
|
583
|
+
@media (prefers-color-scheme: dark) {
|
|
584
|
+
:root {
|
|
585
|
+
--bg-color: #181826;
|
|
586
|
+
--card-bg: #212134;
|
|
587
|
+
--text-color: #ffffff;
|
|
588
|
+
--text-muted: #a5a5ba;
|
|
589
|
+
--btn-bg: #4945ff;
|
|
590
|
+
--btn-hover: #7b79ff;
|
|
591
|
+
--btn-text: #ffffff;
|
|
592
|
+
--icon-bg: #4a2123;
|
|
593
|
+
--icon-color: #f23628;
|
|
594
|
+
--success-bg: #1c3523;
|
|
595
|
+
--success-color: #55ca76;
|
|
596
|
+
--shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
body {
|
|
600
|
+
margin: 0;
|
|
601
|
+
padding: 0;
|
|
602
|
+
display: flex;
|
|
603
|
+
justify-content: center;
|
|
604
|
+
align-items: center;
|
|
605
|
+
height: 100vh;
|
|
606
|
+
background-color: var(--bg-color);
|
|
607
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
608
|
+
color: var(--text-color);
|
|
609
|
+
}
|
|
610
|
+
.card {
|
|
611
|
+
background: var(--card-bg);
|
|
612
|
+
padding: 32px 40px;
|
|
613
|
+
border-radius: 8px;
|
|
614
|
+
box-shadow: var(--shadow);
|
|
615
|
+
max-width: 400px;
|
|
616
|
+
width: 100%;
|
|
617
|
+
text-align: center;
|
|
618
|
+
box-sizing: border-box;
|
|
619
|
+
}
|
|
620
|
+
.icon {
|
|
621
|
+
width: 48px;
|
|
622
|
+
height: 48px;
|
|
623
|
+
background-color: var(--icon-bg);
|
|
624
|
+
color: var(--icon-color);
|
|
625
|
+
border-radius: 50%;
|
|
626
|
+
display: inline-flex;
|
|
627
|
+
justify-content: center;
|
|
628
|
+
align-items: center;
|
|
629
|
+
margin-bottom: 24px;
|
|
630
|
+
}
|
|
631
|
+
.icon.success {
|
|
632
|
+
background-color: var(--success-bg);
|
|
633
|
+
color: var(--success-color);
|
|
634
|
+
}
|
|
635
|
+
.icon svg {
|
|
636
|
+
width: 24px;
|
|
637
|
+
height: 24px;
|
|
638
|
+
stroke: currentColor;
|
|
639
|
+
stroke-width: 2;
|
|
640
|
+
stroke-linecap: round;
|
|
641
|
+
stroke-linejoin: round;
|
|
642
|
+
fill: none;
|
|
643
|
+
}
|
|
644
|
+
h1 {
|
|
645
|
+
margin: 0 0 12px 0;
|
|
646
|
+
font-size: 20px;
|
|
647
|
+
font-weight: 600;
|
|
648
|
+
color: var(--text-color);
|
|
649
|
+
}
|
|
650
|
+
p {
|
|
651
|
+
margin: 0 0 32px 0;
|
|
652
|
+
font-size: 14px;
|
|
653
|
+
line-height: 1.5;
|
|
654
|
+
color: var(--text-muted);
|
|
655
|
+
}
|
|
656
|
+
.btn {
|
|
657
|
+
display: inline-block;
|
|
658
|
+
background-color: var(--btn-bg);
|
|
659
|
+
color: var(--btn-text);
|
|
660
|
+
padding: 10px 16px;
|
|
661
|
+
border-radius: 4px;
|
|
662
|
+
text-decoration: none;
|
|
663
|
+
font-size: 14px;
|
|
664
|
+
font-weight: 500;
|
|
665
|
+
transition: background-color 0.2s;
|
|
666
|
+
}
|
|
667
|
+
.btn:hover {
|
|
668
|
+
background-color: var(--btn-hover);
|
|
669
|
+
}
|
|
670
|
+
</style>
|
|
671
|
+
</head>
|
|
672
|
+
<body>
|
|
673
|
+
${content}
|
|
674
|
+
</body>
|
|
675
|
+
</html>`;
|
|
676
|
+
}
|
|
518
677
|
function oauthService({ strapi: strapi2 }) {
|
|
519
678
|
return {
|
|
520
679
|
async createUser(email, lastname, firstname, locale, roles2 = []) {
|
|
@@ -595,40 +754,48 @@ function oauthService({ strapi: strapi2 }) {
|
|
|
595
754
|
const config2 = strapi2.config.get("plugin::strapi-plugin-oidc");
|
|
596
755
|
const REMEMBER_ME = config2["REMEMBER_ME"];
|
|
597
756
|
const isRememberMe = !!REMEMBER_ME;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
<
|
|
601
|
-
<
|
|
602
|
-
<
|
|
603
|
-
<
|
|
604
|
-
</
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
757
|
+
const content = `
|
|
758
|
+
<noscript>
|
|
759
|
+
<div class="card">
|
|
760
|
+
<div class="icon success">
|
|
761
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check">
|
|
762
|
+
<path d="M20 6 9 17l-5-5"/>
|
|
763
|
+
</svg>
|
|
764
|
+
</div>
|
|
765
|
+
<h1>JavaScript Required</h1>
|
|
766
|
+
<p>JavaScript must be enabled for authentication to complete.</p>
|
|
767
|
+
</div>
|
|
768
|
+
</noscript>
|
|
769
|
+
<script nonce="${nonce}">
|
|
770
|
+
window.addEventListener('load', function() {
|
|
771
|
+
if(${isRememberMe}){
|
|
772
|
+
localStorage.setItem('jwtToken', '"${jwtToken}"');
|
|
773
|
+
}else{
|
|
774
|
+
document.cookie = 'jwtToken=${encodeURIComponent(jwtToken)}; Path=/';
|
|
775
|
+
}
|
|
776
|
+
localStorage.setItem('isLoggedIn', 'true');
|
|
777
|
+
location.href = '${strapi2.config.admin.url}'
|
|
778
|
+
})
|
|
779
|
+
<\/script>`;
|
|
780
|
+
return renderHtmlTemplate("Authenticating...", content);
|
|
620
781
|
},
|
|
621
782
|
// Sign In Error
|
|
622
783
|
renderSignUpError(message) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
<
|
|
626
|
-
<
|
|
627
|
-
<
|
|
628
|
-
<
|
|
629
|
-
<
|
|
630
|
-
|
|
631
|
-
</
|
|
784
|
+
const safeMessage = String(message).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
785
|
+
const content = `
|
|
786
|
+
<div class="card">
|
|
787
|
+
<div class="icon">
|
|
788
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert">
|
|
789
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/>
|
|
790
|
+
<path d="M12 9v4"/>
|
|
791
|
+
<path d="M12 17h.01"/>
|
|
792
|
+
</svg>
|
|
793
|
+
</div>
|
|
794
|
+
<h1>Authentication Failed</h1>
|
|
795
|
+
<p>${safeMessage}</p>
|
|
796
|
+
<a href="${strapi2.config.admin.url}" class="btn">Return to Login</a>
|
|
797
|
+
</div>`;
|
|
798
|
+
return renderHtmlTemplate("Authentication Failed", content);
|
|
632
799
|
},
|
|
633
800
|
async generateToken(user, ctx) {
|
|
634
801
|
const sessionManager = strapi2.sessionManager;
|
|
@@ -722,39 +889,35 @@ function roleService({ strapi: strapi2 }) {
|
|
|
722
889
|
};
|
|
723
890
|
}
|
|
724
891
|
function whitelistService({ strapi: strapi2 }) {
|
|
892
|
+
const getPluginStore = () => strapi2.store({
|
|
893
|
+
environment: "",
|
|
894
|
+
type: "plugin",
|
|
895
|
+
name: "strapi-plugin-oidc"
|
|
896
|
+
});
|
|
897
|
+
const getWhitelistQuery = () => strapi2.query("plugin::strapi-plugin-oidc.whitelists");
|
|
725
898
|
return {
|
|
726
899
|
async getSettings() {
|
|
727
|
-
|
|
728
|
-
let settings = await pluginStore.get({ key: "settings" });
|
|
900
|
+
let settings = await getPluginStore().get({ key: "settings" });
|
|
729
901
|
if (!settings) {
|
|
730
902
|
settings = { useWhitelist: true, enforceOIDC: false };
|
|
731
|
-
await
|
|
903
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
732
904
|
}
|
|
733
905
|
return settings;
|
|
734
906
|
},
|
|
735
907
|
async setSettings(settings) {
|
|
736
|
-
|
|
737
|
-
await pluginStore.set({ key: "settings", value: settings });
|
|
908
|
+
await getPluginStore().set({ key: "settings", value: settings });
|
|
738
909
|
},
|
|
739
910
|
async getUsers() {
|
|
740
|
-
|
|
741
|
-
return query.findMany();
|
|
911
|
+
return getWhitelistQuery().findMany();
|
|
742
912
|
},
|
|
743
913
|
async registerUser(email, roles2) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
data: {
|
|
747
|
-
email,
|
|
748
|
-
roles: roles2
|
|
749
|
-
}
|
|
914
|
+
await getWhitelistQuery().create({
|
|
915
|
+
data: { email, roles: roles2 }
|
|
750
916
|
});
|
|
751
917
|
},
|
|
752
918
|
async removeUser(id) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
where: {
|
|
756
|
-
id
|
|
757
|
-
}
|
|
919
|
+
await getWhitelistQuery().delete({
|
|
920
|
+
where: { id }
|
|
758
921
|
});
|
|
759
922
|
},
|
|
760
923
|
async checkWhitelistForEmail(email) {
|
|
@@ -763,11 +926,8 @@ function whitelistService({ strapi: strapi2 }) {
|
|
|
763
926
|
if (!settings.useWhitelist) {
|
|
764
927
|
return null;
|
|
765
928
|
}
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
where: {
|
|
769
|
-
email
|
|
770
|
-
}
|
|
929
|
+
const result = await getWhitelistQuery().findOne({
|
|
930
|
+
where: { email }
|
|
771
931
|
});
|
|
772
932
|
console.log("checkWhitelistForEmail result:", result);
|
|
773
933
|
if (!result) {
|
package/package.json
CHANGED