strapi-plugin-oidc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/_chunks/en-AsM8uCFB.mjs +23 -0
- package/dist/_chunks/en-BbQ9XzfO.js +23 -0
- package/dist/_chunks/fr-C8Qw4iPZ.js +4 -0
- package/dist/_chunks/fr-hkSxFuzl.mjs +4 -0
- package/dist/_chunks/index-Bq6bRISt.js +414 -0
- package/dist/_chunks/index-BuS0wmlA.mjs +412 -0
- package/dist/_chunks/index-CHl-03dY.mjs +204 -0
- package/dist/_chunks/index-DR3YYYZL.js +203 -0
- package/dist/_chunks/ja-B2WcMFA2.js +23 -0
- package/dist/_chunks/ja-COdupAQd.mjs +23 -0
- package/dist/admin/en-f0TxVfx7.mjs +41 -0
- package/dist/admin/en-jFPbEFeK.js +41 -0
- package/dist/admin/index-BuuCScSN.mjs +143 -0
- package/dist/admin/index-C0GkDnGG.js +142 -0
- package/dist/admin/index-D_0jCOLk.js +491 -0
- package/dist/admin/index-DwpTg1-J.mjs +489 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +746 -0
- package/dist/server/index.mjs +741 -0
- package/package.json +107 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 edmogeor
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/edmogeor/strapi-plugin-oidc/main/assets/icon.png" width="140" alt="OIDC Plugin for Strapi Logo"/>
|
|
3
|
+
<h1>OIDC Plugin for Strapi</h1>
|
|
4
|
+
<p>
|
|
5
|
+
<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">
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
A Strapi plugin that provides OpenID Connect (OIDC) authentication functionality for the Strapi Admin Panel.
|
|
12
|
+
|
|
13
|
+
This plugin allows your administrators to log in to the Strapi administration interface using external OIDC identity providers such as Zitadel, Keycloak, Auth0, AWS Cognito, and others.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
You can install the plugin via `npm` or `yarn`:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Using npm
|
|
21
|
+
npm install strapi-plugin-oidc
|
|
22
|
+
|
|
23
|
+
# Using yarn
|
|
24
|
+
yarn add strapi-plugin-oidc
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
To enable and configure the plugin, update your `config/plugins.js` (or `config/plugins.ts`) file with your OIDC provider's settings.
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
module.exports = ({ env }) => ({
|
|
33
|
+
// ...
|
|
34
|
+
'strapi-plugin-oidc': {
|
|
35
|
+
enabled: true,
|
|
36
|
+
config: {
|
|
37
|
+
// Set to true to store the token in local storage, false for session storage
|
|
38
|
+
REMEMBER_ME: false,
|
|
39
|
+
|
|
40
|
+
// OpenID Connect Settings
|
|
41
|
+
OIDC_REDIRECT_URI: 'http://localhost:1337/strapi-plugin-oidc/oidc/callback', // Callback URI after successful login
|
|
42
|
+
OIDC_CLIENT_ID: '[Client ID from OpenID Provider]',
|
|
43
|
+
OIDC_CLIENT_SECRET: '[Client Secret from OpenID Provider]',
|
|
44
|
+
|
|
45
|
+
OIDC_SCOPES: 'openid profile email', // Standard OIDC scopes
|
|
46
|
+
|
|
47
|
+
// API Endpoints required for OIDC provider
|
|
48
|
+
OIDC_AUTHORIZATION_ENDPOINT: '[Authorization Endpoint]',
|
|
49
|
+
OIDC_TOKEN_ENDPOINT: '[Token Endpoint]',
|
|
50
|
+
OIDC_USER_INFO_ENDPOINT: '[User Info Endpoint]',
|
|
51
|
+
OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER: false,
|
|
52
|
+
OIDC_GRANT_TYPE: 'authorization_code',
|
|
53
|
+
|
|
54
|
+
// Customizable user field mapping for user creation
|
|
55
|
+
OIDC_FAMILY_NAME_FIELD: 'family_name',
|
|
56
|
+
OIDC_GIVEN_NAME_FIELD: 'given_name',
|
|
57
|
+
|
|
58
|
+
// Redirect to OIDC provider's logout page when users log out of Strapi
|
|
59
|
+
OIDC_LOGOUT_URL: '[OIDC Provider Logout URL]'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ...
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
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.
|
|
67
|
+
|
|
68
|
+
## Admin Settings
|
|
69
|
+
|
|
70
|
+
Once the plugin is installed and configured, you can manage the OIDC settings from the Strapi Admin Panel under **Settings** > **OIDC Plugin**.
|
|
71
|
+
|
|
72
|
+
- **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.
|
|
73
|
+
- **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.
|
|
74
|
+
- **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).*
|
|
75
|
+
|
|
76
|
+
## Credits & Changes
|
|
77
|
+
|
|
78
|
+
This plugin is a hard fork of the original [`strapi-plugin-sso`](https://github.com/yasudacloud/strapi-plugin-sso) created by **yasudacloud**. Huge thanks to them for creating the foundation of this plugin!
|
|
79
|
+
|
|
80
|
+
### Changes made to the original codebase:
|
|
81
|
+
- Removed alternative SSO methods to simplify the plugin.
|
|
82
|
+
- Redesigned the Whitelist and Role management UI (switched to native Strapi cards, added pagination, etc.).
|
|
83
|
+
- Added an OIDC logout redirect URL.
|
|
84
|
+
- Added an option to "Enforce OIDC login" with an admin toggle (automatically disabled if the whitelist is empty).
|
|
85
|
+
- Migrated the testing framework to Vitest and added comprehensive test coverage for controllers and services.
|
|
86
|
+
- Cleaned up dead code and unused dependencies to improve maintainability.
|
|
87
|
+
- Upgraded to use newer versions of Node.js.
|
|
88
|
+
- Added misc. quality of life improvements and bug fixes.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const en = {
|
|
2
|
+
"page.title": "Default role setting at first login",
|
|
3
|
+
"page.notes": "This will not be reflected for already registered users.",
|
|
4
|
+
"page.save": "Save",
|
|
5
|
+
"page.save.success": "Updated settings",
|
|
6
|
+
"page.save.error": "Update failed.",
|
|
7
|
+
"page.cancel": "Cancel",
|
|
8
|
+
"page.ok": "OK",
|
|
9
|
+
"tab.roles": "Roles",
|
|
10
|
+
"tab.whitelist": "Whitelist",
|
|
11
|
+
"tab.whitelist.error.unique": "Already registered email address.",
|
|
12
|
+
"tab.whitelist.enabled": "Whitelist is currently enabled.",
|
|
13
|
+
"tab.whitelist.disabled": "Whitelist is currently disabled.",
|
|
14
|
+
"tab.whitelist.description": "Only the following email addresses are allowed to authenticate with SSO.",
|
|
15
|
+
"tab.whitelist.table.no": "No",
|
|
16
|
+
"tab.whitelist.table.email": "Email",
|
|
17
|
+
"tab.whitelist.table.created": "Created At",
|
|
18
|
+
"tab.whitelist.delete.title": "Confirmation",
|
|
19
|
+
"tab.whitelist.delete.description": "Are you sure you want to delete this?"
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
en as default
|
|
23
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const en = {
|
|
4
|
+
"page.title": "Default role setting at first login",
|
|
5
|
+
"page.notes": "This will not be reflected for already registered users.",
|
|
6
|
+
"page.save": "Save",
|
|
7
|
+
"page.save.success": "Updated settings",
|
|
8
|
+
"page.save.error": "Update failed.",
|
|
9
|
+
"page.cancel": "Cancel",
|
|
10
|
+
"page.ok": "OK",
|
|
11
|
+
"tab.roles": "Roles",
|
|
12
|
+
"tab.whitelist": "Whitelist",
|
|
13
|
+
"tab.whitelist.error.unique": "Already registered email address.",
|
|
14
|
+
"tab.whitelist.enabled": "Whitelist is currently enabled.",
|
|
15
|
+
"tab.whitelist.disabled": "Whitelist is currently disabled.",
|
|
16
|
+
"tab.whitelist.description": "Only the following email addresses are allowed to authenticate with SSO.",
|
|
17
|
+
"tab.whitelist.table.no": "No",
|
|
18
|
+
"tab.whitelist.table.email": "Email",
|
|
19
|
+
"tab.whitelist.table.created": "Created At",
|
|
20
|
+
"tab.whitelist.delete.title": "Confirmation",
|
|
21
|
+
"tab.whitelist.delete.description": "Are you sure you want to delete this?"
|
|
22
|
+
};
|
|
23
|
+
exports.default = en;
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const reactRouterDom = require("react-router-dom");
|
|
6
|
+
const admin = require("@strapi/strapi/admin");
|
|
7
|
+
const designSystem = require("@strapi/design-system");
|
|
8
|
+
const reactIntl = require("react-intl");
|
|
9
|
+
const index = require("./index-DR3YYYZL.js");
|
|
10
|
+
const styled = require("styled-components");
|
|
11
|
+
const icons = require("@strapi/icons");
|
|
12
|
+
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
13
|
+
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
14
|
+
const getTrad = (id) => `${index.pluginId}.${id}`;
|
|
15
|
+
const ButtonWrapper = styled__default.default.div`
|
|
16
|
+
margin: 10px 0 0 0;
|
|
17
|
+
|
|
18
|
+
& button {
|
|
19
|
+
margin: 0 0 0 auto;
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
const Description = styled__default.default.p`
|
|
23
|
+
font-size: 16px;
|
|
24
|
+
margin: 20px 0;
|
|
25
|
+
`;
|
|
26
|
+
function Role({ ssoRoles, roles, onSaveRole, onChangeRoleCheck }) {
|
|
27
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
28
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
29
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: roles.length + 1, rowCount: ssoRoles.length, children: [
|
|
30
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Checkbox, { style: { display: "none" } }) }),
|
|
32
|
+
roles.map((role) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: role["name"] }, role["id"]))
|
|
33
|
+
] }) }),
|
|
34
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: ssoRoles.map((ssoRole) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: ssoRole["name"] }),
|
|
36
|
+
roles.map((role) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
37
|
+
designSystem.Checkbox,
|
|
38
|
+
{
|
|
39
|
+
checked: ssoRole["role"] && ssoRole["role"].includes(role["id"]),
|
|
40
|
+
onCheckedChange: (value) => onChangeRoleCheck(value, ssoRole["oauth_type"], role["id"]),
|
|
41
|
+
children: ""
|
|
42
|
+
}
|
|
43
|
+
) }, role["id"]))
|
|
44
|
+
] }, ssoRole["oauth_type"])) })
|
|
45
|
+
] }),
|
|
46
|
+
/* @__PURE__ */ jsxRuntime.jsx(Description, { children: formatMessage({
|
|
47
|
+
id: getTrad("page.notes"),
|
|
48
|
+
defaultMessage: "This will not be reflected for already registered users."
|
|
49
|
+
}) }),
|
|
50
|
+
/* @__PURE__ */ jsxRuntime.jsx(ButtonWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "default", onClick: onSaveRole, children: formatMessage({
|
|
51
|
+
id: getTrad("page.save"),
|
|
52
|
+
defaultMessage: "Save"
|
|
53
|
+
}) }) })
|
|
54
|
+
] });
|
|
55
|
+
}
|
|
56
|
+
const LocalizedDate = ({ date }) => {
|
|
57
|
+
const userLocale = navigator.language || "en-US";
|
|
58
|
+
return new Intl.DateTimeFormat(userLocale, {
|
|
59
|
+
year: "numeric",
|
|
60
|
+
month: "long",
|
|
61
|
+
day: "numeric",
|
|
62
|
+
hour: "2-digit",
|
|
63
|
+
minute: "2-digit"
|
|
64
|
+
}).format(new Date(date));
|
|
65
|
+
};
|
|
66
|
+
function Whitelist({ users, roles, useWhitelist, loading, onSave, onDelete, onToggle }) {
|
|
67
|
+
const [email, setEmail] = react.useState("");
|
|
68
|
+
const [selectedRoles, setSelectedRoles] = react.useState([]);
|
|
69
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
70
|
+
const onSaveEmail = react.useCallback(async () => {
|
|
71
|
+
const emailText = email.trim();
|
|
72
|
+
if (users.some((user) => user.email === emailText)) {
|
|
73
|
+
alert(
|
|
74
|
+
formatMessage({
|
|
75
|
+
id: getTrad("tab.whitelist.error.unique"),
|
|
76
|
+
defaultMessage: "Already registered email address."
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
await onSave(emailText, selectedRoles);
|
|
81
|
+
setEmail("");
|
|
82
|
+
setSelectedRoles([]);
|
|
83
|
+
}
|
|
84
|
+
}, [email, selectedRoles, users, onSave, formatMessage]);
|
|
85
|
+
const isValidEmail = react.useCallback(() => {
|
|
86
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
87
|
+
return emailRegex.test(email);
|
|
88
|
+
}, [email]);
|
|
89
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, children: [
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, marginBottom: 4, children: [
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
92
|
+
designSystem.Toggle,
|
|
93
|
+
{
|
|
94
|
+
checked: useWhitelist,
|
|
95
|
+
onLabel: "Enabled",
|
|
96
|
+
offLabel: "Disabled",
|
|
97
|
+
onChange: onToggle
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", children: useWhitelist ? formatMessage({
|
|
101
|
+
id: getTrad("tab.whitelist.enabled"),
|
|
102
|
+
defaultMessage: "Whitelist is currently enabled."
|
|
103
|
+
}) : formatMessage({
|
|
104
|
+
id: getTrad("tab.whitelist.disabled"),
|
|
105
|
+
defaultMessage: "Whitelist is currently disabled."
|
|
106
|
+
}) })
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", marginBottom: 4, children: formatMessage({
|
|
109
|
+
id: getTrad("tab.whitelist.description"),
|
|
110
|
+
defaultMessage: "Only the following email addresses are allowed to authenticate with SSO."
|
|
111
|
+
}) }),
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { tag: "fieldset", gap: 4, padding: "0px", gridCols: 3, borderWidth: 0, marginTop: 5, marginBottom: 5, children: [
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 1, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
114
|
+
designSystem.Field.Input,
|
|
115
|
+
{
|
|
116
|
+
type: "email",
|
|
117
|
+
disabled: loading,
|
|
118
|
+
value: email,
|
|
119
|
+
hasError: email && !isValidEmail(),
|
|
120
|
+
onChange: (e) => setEmail(e.currentTarget.value),
|
|
121
|
+
placeholder: "Email address"
|
|
122
|
+
}
|
|
123
|
+
) }) }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 1, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
+
designSystem.MultiSelect,
|
|
126
|
+
{
|
|
127
|
+
placeholder: "Select specific roles",
|
|
128
|
+
withTags: true,
|
|
129
|
+
value: selectedRoles,
|
|
130
|
+
onChange: setSelectedRoles,
|
|
131
|
+
children: roles.map((role) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.MultiSelectOption, { value: role.id.toString(), children: role.name }, role.id))
|
|
132
|
+
}
|
|
133
|
+
) }) }),
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
135
|
+
designSystem.Button,
|
|
136
|
+
{
|
|
137
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
138
|
+
disabled: loading || email.trim() === "" || !isValidEmail(),
|
|
139
|
+
loading,
|
|
140
|
+
onClick: onSaveEmail,
|
|
141
|
+
children: formatMessage({
|
|
142
|
+
id: getTrad("page.save"),
|
|
143
|
+
defaultMessage: "Save"
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
) })
|
|
147
|
+
] }),
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
|
|
149
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: users.length, children: [
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage({
|
|
152
|
+
id: getTrad("tab.whitelist.table.no"),
|
|
153
|
+
defaultMessage: "No"
|
|
154
|
+
}) }),
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage({
|
|
156
|
+
id: getTrad("tab.whitelist.table.email"),
|
|
157
|
+
defaultMessage: "Email"
|
|
158
|
+
}) }),
|
|
159
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: "Roles" }),
|
|
160
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage({
|
|
161
|
+
id: getTrad("tab.whitelist.table.created"),
|
|
162
|
+
defaultMessage: "Created At"
|
|
163
|
+
}) }),
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: " " })
|
|
165
|
+
] }) }),
|
|
166
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: users.map((user) => {
|
|
167
|
+
const userRolesNames = (user.roles || []).map((roleId) => {
|
|
168
|
+
const r = roles.find((ro) => ro.id.toString() === roleId.toString());
|
|
169
|
+
return r ? r.name : roleId;
|
|
170
|
+
}).join(", ");
|
|
171
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.id }),
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.email }),
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: userRolesNames || "Default" }),
|
|
175
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: user.createdAt }) }),
|
|
176
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Root, { children: [
|
|
177
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Delete", withTooltip: false, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) }) }),
|
|
178
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
|
|
180
|
+
id: getTrad("tab.whitelist.delete.title"),
|
|
181
|
+
defaultMessage: "Confirmation"
|
|
182
|
+
}) }),
|
|
183
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.WarningCircle, { fill: "danger600" }), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", children: [
|
|
184
|
+
formatMessage({
|
|
185
|
+
id: getTrad("tab.whitelist.delete.description"),
|
|
186
|
+
defaultMessage: "Are you sure you want to delete this?"
|
|
187
|
+
}),
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
189
|
+
user.email
|
|
190
|
+
] }) }),
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
|
|
192
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: formatMessage({
|
|
193
|
+
id: getTrad("page.cancel"),
|
|
194
|
+
defaultMessage: "Cancel"
|
|
195
|
+
}) }) }),
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "danger-light", onClick: () => onDelete(user.id), children: formatMessage({
|
|
197
|
+
id: getTrad("page.ok"),
|
|
198
|
+
defaultMessage: "OK"
|
|
199
|
+
}) }) })
|
|
200
|
+
] })
|
|
201
|
+
] })
|
|
202
|
+
] }) })
|
|
203
|
+
] }, user.id);
|
|
204
|
+
}) })
|
|
205
|
+
] })
|
|
206
|
+
] }) });
|
|
207
|
+
}
|
|
208
|
+
const AlertMessage = styled__default.default.div`
|
|
209
|
+
margin-left: -250px;
|
|
210
|
+
position: fixed;
|
|
211
|
+
left: 50%;
|
|
212
|
+
top: 2.875rem;
|
|
213
|
+
z-index: 10;
|
|
214
|
+
width: 31.25rem;
|
|
215
|
+
`;
|
|
216
|
+
function SuccessAlertMessage({ onClose: onClose2 }) {
|
|
217
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
218
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AlertMessage, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
219
|
+
designSystem.Alert,
|
|
220
|
+
{
|
|
221
|
+
title: "Success",
|
|
222
|
+
variant: "success",
|
|
223
|
+
closeLabel: "",
|
|
224
|
+
onClose: onClose2,
|
|
225
|
+
children: formatMessage({
|
|
226
|
+
id: getTrad("page.save.success"),
|
|
227
|
+
defaultMessage: "Updated settings"
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
) });
|
|
231
|
+
}
|
|
232
|
+
function ErrorAlertMessage() {
|
|
233
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
234
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AlertMessage, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
235
|
+
designSystem.Alert,
|
|
236
|
+
{
|
|
237
|
+
title: "Error",
|
|
238
|
+
variant: "danger",
|
|
239
|
+
closeLabel: "",
|
|
240
|
+
onClose,
|
|
241
|
+
children: formatMessage({
|
|
242
|
+
id: getTrad("page.save.error"),
|
|
243
|
+
defaultMessage: "Update failed."
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
) });
|
|
247
|
+
}
|
|
248
|
+
const HomePage = () => {
|
|
249
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
250
|
+
const [loading, setLoading] = react.useState(false);
|
|
251
|
+
const [ssoRoles, setSSORoles] = react.useState([]);
|
|
252
|
+
const [roles, setRoles] = react.useState([]);
|
|
253
|
+
const [useWhitelist, setUseWhitelist] = react.useState(false);
|
|
254
|
+
const [users, setUsers] = react.useState([]);
|
|
255
|
+
const [showSuccess, setSuccess] = react.useState(false);
|
|
256
|
+
const [showError, setError] = react.useState(false);
|
|
257
|
+
const { get, put, post, del } = admin.useFetchClient();
|
|
258
|
+
react.useEffect(() => {
|
|
259
|
+
get(`/strapi-plugin-oidc/sso-roles`).then((response) => {
|
|
260
|
+
setSSORoles(response.data);
|
|
261
|
+
});
|
|
262
|
+
get(`/admin/roles`).then((response) => {
|
|
263
|
+
setRoles(response.data.data);
|
|
264
|
+
});
|
|
265
|
+
get("/strapi-plugin-oidc/whitelist").then((response) => {
|
|
266
|
+
setUsers(response.data.whitelistUsers);
|
|
267
|
+
setUseWhitelist(response.data.useWhitelist);
|
|
268
|
+
});
|
|
269
|
+
}, [setSSORoles, setRoles]);
|
|
270
|
+
const onChangeRoleCheck = (value, ssoId, role) => {
|
|
271
|
+
for (const ssoRole of ssoRoles) {
|
|
272
|
+
if (ssoRole["oauth_type"] === ssoId) {
|
|
273
|
+
if (ssoRole["role"]) {
|
|
274
|
+
if (value) {
|
|
275
|
+
ssoRole["role"].push(role);
|
|
276
|
+
} else {
|
|
277
|
+
ssoRole["role"] = ssoRole["role"].filter((selectRole) => selectRole !== role);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
ssoRole["role"] = [role];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
setSSORoles(ssoRoles.slice());
|
|
285
|
+
};
|
|
286
|
+
const onSaveRole = async () => {
|
|
287
|
+
try {
|
|
288
|
+
await put("/strapi-plugin-oidc/sso-roles", {
|
|
289
|
+
roles: ssoRoles.map((role) => ({
|
|
290
|
+
"oauth_type": role["oauth_type"],
|
|
291
|
+
role: role["role"]
|
|
292
|
+
}))
|
|
293
|
+
});
|
|
294
|
+
setSuccess(true);
|
|
295
|
+
setTimeout(() => {
|
|
296
|
+
setSuccess(false);
|
|
297
|
+
}, 3e3);
|
|
298
|
+
} catch (e) {
|
|
299
|
+
console.error(e);
|
|
300
|
+
setError(true);
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
setError(false);
|
|
303
|
+
}, 3e3);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const onRegisterWhitelist = async (email, selectedRoles) => {
|
|
307
|
+
setLoading(true);
|
|
308
|
+
post("/strapi-plugin-oidc/whitelist", {
|
|
309
|
+
email,
|
|
310
|
+
roles: selectedRoles
|
|
311
|
+
}).then((response) => {
|
|
312
|
+
get("/strapi-plugin-oidc/whitelist").then((response2) => {
|
|
313
|
+
setUsers(response2.data.whitelistUsers);
|
|
314
|
+
setUseWhitelist(response2.data.useWhitelist);
|
|
315
|
+
});
|
|
316
|
+
setLoading(false);
|
|
317
|
+
setSuccess(true);
|
|
318
|
+
setTimeout(() => {
|
|
319
|
+
setSuccess(false);
|
|
320
|
+
}, 3e3);
|
|
321
|
+
});
|
|
322
|
+
};
|
|
323
|
+
const onDeleteWhitelist = async (id) => {
|
|
324
|
+
setLoading(true);
|
|
325
|
+
del(`/strapi-plugin-oidc/whitelist/${id}`).then((response) => {
|
|
326
|
+
get("/strapi-plugin-oidc/whitelist").then((response2) => {
|
|
327
|
+
setUsers(response2.data.whitelistUsers);
|
|
328
|
+
setUseWhitelist(response2.data.useWhitelist);
|
|
329
|
+
});
|
|
330
|
+
setLoading(false);
|
|
331
|
+
setSuccess(true);
|
|
332
|
+
setTimeout(() => {
|
|
333
|
+
setSuccess(false);
|
|
334
|
+
}, 3e3);
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
const onToggleWhitelist = async (e) => {
|
|
338
|
+
const newValue = e.target.checked;
|
|
339
|
+
setLoading(true);
|
|
340
|
+
try {
|
|
341
|
+
await put("/strapi-plugin-oidc/whitelist/settings", {
|
|
342
|
+
useWhitelist: newValue
|
|
343
|
+
});
|
|
344
|
+
setUseWhitelist(newValue);
|
|
345
|
+
setSuccess(true);
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
setSuccess(false);
|
|
348
|
+
}, 3e3);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
console.error(err);
|
|
351
|
+
setError(true);
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
setError(false);
|
|
354
|
+
}, 3e3);
|
|
355
|
+
} finally {
|
|
356
|
+
setLoading(false);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Protect, { permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }], children: [
|
|
360
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
361
|
+
admin.Layouts.Header,
|
|
362
|
+
{
|
|
363
|
+
title: "OIDC",
|
|
364
|
+
subtitle: formatMessage({
|
|
365
|
+
id: getTrad("page.title"),
|
|
366
|
+
defaultMessage: "Default role setting at first login"
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
),
|
|
370
|
+
showSuccess && /* @__PURE__ */ jsxRuntime.jsx(SuccessAlertMessage, { onClose: () => setSuccess(false) }),
|
|
371
|
+
showError && /* @__PURE__ */ jsxRuntime.jsx(ErrorAlertMessage, { onClose: () => setError(false) }),
|
|
372
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 10, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Root, { defaultValue: "role", children: [
|
|
373
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { "aria-label": "Manage your attribute", style: { maxWidth: 300 }, children: [
|
|
374
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "role", children: formatMessage({
|
|
375
|
+
id: getTrad("tab.roles"),
|
|
376
|
+
defaultMessage: "Roles"
|
|
377
|
+
}) }),
|
|
378
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "whitelist", children: formatMessage({
|
|
379
|
+
id: getTrad("tab.whitelist"),
|
|
380
|
+
defaultMessage: "Whitelist"
|
|
381
|
+
}) })
|
|
382
|
+
] }),
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "role", style: { background: "initial" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
384
|
+
Role,
|
|
385
|
+
{
|
|
386
|
+
roles,
|
|
387
|
+
ssoRoles,
|
|
388
|
+
onSaveRole,
|
|
389
|
+
onChangeRoleCheck
|
|
390
|
+
}
|
|
391
|
+
) }),
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "whitelist", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
393
|
+
Whitelist,
|
|
394
|
+
{
|
|
395
|
+
loading,
|
|
396
|
+
users,
|
|
397
|
+
roles,
|
|
398
|
+
useWhitelist,
|
|
399
|
+
onSave: onRegisterWhitelist,
|
|
400
|
+
onDelete: onDeleteWhitelist,
|
|
401
|
+
onToggle: onToggleWhitelist
|
|
402
|
+
}
|
|
403
|
+
) })
|
|
404
|
+
] }) })
|
|
405
|
+
] });
|
|
406
|
+
};
|
|
407
|
+
const HomePage$1 = react.memo(HomePage);
|
|
408
|
+
const App = () => {
|
|
409
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
|
|
410
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage$1, {}) }),
|
|
411
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
|
|
412
|
+
] }) });
|
|
413
|
+
};
|
|
414
|
+
exports.default = App;
|