strapi-security-suite 0.2.3 → 0.3.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.
@@ -0,0 +1,162 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useFetchClient, Page } from "@strapi/strapi/admin";
3
+ import { Routes, Route } from "react-router-dom";
4
+ import { useState, useEffect } from "react";
5
+ import { Box, Typography, Alert, Flex, Divider, NumberInput, Switch, Button } from "@strapi/design-system";
6
+ import { A as API_BASE_PATH, S as SUCCESS_ALERT_DURATION } from "./index-CAEB836L.mjs";
7
+ const HomePage = () => {
8
+ const client = useFetchClient();
9
+ const [config, setConfig] = useState({
10
+ autoLogoutTime: 30,
11
+ multipleSessionsControl: false,
12
+ passwordExpiryDays: 365,
13
+ nonReusablePassword: false,
14
+ enablePasswordManagement: false
15
+ });
16
+ const [success, setSuccess] = useState(false);
17
+ const [error, setError] = useState(null);
18
+ useEffect(() => {
19
+ const fetchConfig = async () => {
20
+ try {
21
+ const { data } = await client.get(`${API_BASE_PATH}/admin/settings`);
22
+ if (data?.data) {
23
+ setConfig(data.data);
24
+ }
25
+ } catch {
26
+ setError("Failed to load settings. Please refresh the page.");
27
+ }
28
+ };
29
+ fetchConfig();
30
+ }, [client]);
31
+ const handleChange = (key, value) => {
32
+ setConfig((prev) => ({ ...prev, [key]: value }));
33
+ };
34
+ const saveConfig = async () => {
35
+ setError(null);
36
+ try {
37
+ await client.post(`${API_BASE_PATH}/admin/settings`, config);
38
+ setSuccess(true);
39
+ setTimeout(() => setSuccess(false), SUCCESS_ALERT_DURATION);
40
+ } catch (err) {
41
+ setError(err.response?.data?.error?.message ?? "Failed to save settings.");
42
+ }
43
+ };
44
+ return /* @__PURE__ */ jsxs(Box, { padding: 10, background: "neutral0", shadow: "filterShadow", borderRadius: "12px", children: [
45
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", as: "h1", fontWeight: "bold", children: "Security & Session Settings" }),
46
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", paddingTop: 2, paddingBottom: 4, children: "Configure session duration, password policies, and security settings." }),
47
+ success && /* @__PURE__ */ jsx(Alert, { title: "Success", variant: "success", marginBottom: 4, children: "Configuration saved successfully!" }),
48
+ error && /* @__PURE__ */ jsx(Alert, { title: "Error", variant: "danger", marginBottom: 4, onClose: () => setError(null), children: error }),
49
+ /* @__PURE__ */ jsxs(
50
+ Flex,
51
+ {
52
+ alignItems: "flex-start",
53
+ justifyContent: "space-between",
54
+ gap: 6,
55
+ paddingTop: 6,
56
+ wrap: "wrap",
57
+ children: [
58
+ /* @__PURE__ */ jsxs(
59
+ Box,
60
+ {
61
+ flex: "1",
62
+ minWidth: "400px",
63
+ padding: 6,
64
+ background: "neutral100",
65
+ borderRadius: "8px",
66
+ shadow: "tableShadow",
67
+ children: [
68
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "semiBold", children: "Session Management" }),
69
+ /* @__PURE__ */ jsx(Divider, { marginTop: 2, marginBottom: 4 }),
70
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 4, children: [
71
+ /* @__PURE__ */ jsxs(Flex, { direction: "row", gap: 4, children: [
72
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Auto Logout Time (minutes)" }),
73
+ /* @__PURE__ */ jsx(
74
+ NumberInput,
75
+ {
76
+ minValue: 1,
77
+ value: config.autoLogoutTime,
78
+ onValueChange: (value) => handleChange("autoLogoutTime", value)
79
+ }
80
+ )
81
+ ] }),
82
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, children: [
83
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Activate multiple sessions control?" }),
84
+ /* @__PURE__ */ jsx(
85
+ Switch,
86
+ {
87
+ checked: config.multipleSessionsControl,
88
+ onCheckedChange: () => handleChange("multipleSessionsControl", !config.multipleSessionsControl)
89
+ }
90
+ )
91
+ ] })
92
+ ] })
93
+ ]
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsxs(
97
+ Box,
98
+ {
99
+ flex: "1",
100
+ minWidth: "400px",
101
+ minHeight: "173px",
102
+ padding: 6,
103
+ background: "neutral100",
104
+ borderRadius: "8px",
105
+ shadow: "tableShadow",
106
+ children: [
107
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "semiBold", children: "Password Management" }),
108
+ /* @__PURE__ */ jsx(Divider, { marginTop: 2, marginBottom: 4 }),
109
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 4, children: [
110
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, children: [
111
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Activate Password Control?" }),
112
+ /* @__PURE__ */ jsx(
113
+ Switch,
114
+ {
115
+ checked: config.enablePasswordManagement,
116
+ onCheckedChange: () => handleChange("enablePasswordManagement", !config.enablePasswordManagement)
117
+ }
118
+ )
119
+ ] }),
120
+ config.enablePasswordManagement && /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 5, children: [
121
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 3, children: [
122
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Password Expiry (days)?" }),
123
+ /* @__PURE__ */ jsx(
124
+ NumberInput,
125
+ {
126
+ minValue: 1,
127
+ value: config.passwordExpiryDays,
128
+ onValueChange: (value) => handleChange("passwordExpiryDays", value)
129
+ }
130
+ )
131
+ ] }),
132
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 3, children: [
133
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Activate Non-Reusable Password?" }),
134
+ /* @__PURE__ */ jsx(
135
+ Switch,
136
+ {
137
+ checked: config.nonReusablePassword,
138
+ onCheckedChange: () => handleChange("nonReusablePassword", !config.nonReusablePassword)
139
+ }
140
+ )
141
+ ] })
142
+ ] })
143
+ ] })
144
+ ]
145
+ }
146
+ )
147
+ ]
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", paddingTop: 6, paddingBottom: 2, children: /* @__PURE__ */ jsx(Button, { onClick: saveConfig, variant: "secondary", size: "L", shadow: "buttonShadow", children: "Save Settings" }) })
151
+ ] });
152
+ };
153
+ const App = () => {
154
+ return /* @__PURE__ */ jsxs(Routes, { children: [
155
+ /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
156
+ /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
157
+ ] });
158
+ };
159
+ export {
160
+ App,
161
+ App as default
162
+ };
@@ -0,0 +1,6 @@
1
+ const en = {
2
+ "strapi-security-suite.settings.title": "Security Suite"
3
+ };
4
+ export {
5
+ en as default
6
+ };
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const en = {};
3
+ const en = {
4
+ "strapi-security-suite.settings.title": "Security Suite"
5
+ };
4
6
  exports.default = en;
@@ -0,0 +1,108 @@
1
+ import { useRef, useEffect } from "react";
2
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
3
+ const v = glob[path];
4
+ if (v) {
5
+ return typeof v === "function" ? v() : Promise.resolve(v);
6
+ }
7
+ return new Promise((_, reject) => {
8
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
9
+ reject.bind(
10
+ null,
11
+ new Error(
12
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
13
+ )
14
+ )
15
+ );
16
+ });
17
+ };
18
+ const PLUGIN_ID = "strapi-security-suite";
19
+ const Initializer = ({ setPlugin }) => {
20
+ const ref = useRef(setPlugin);
21
+ useEffect(() => {
22
+ ref.current(PLUGIN_ID);
23
+ }, []);
24
+ return null;
25
+ };
26
+ const SERVER_PLUGIN_NAME = "strapi-security-suite";
27
+ const API_BASE_PATH = `/${SERVER_PLUGIN_NAME}`;
28
+ const ADMIN_TOKEN_HEADER = "app.admin.tk";
29
+ const SUCCESS_ALERT_DURATION = 3e3;
30
+ const getTrad = (id) => `${PLUGIN_ID}.${id}`;
31
+ const index = {
32
+ /**
33
+ * Registers the plugin with the Strapi admin app and patches
34
+ * `window.fetch` to intercept forced-logout signal headers.
35
+ *
36
+ * @param {Object} app - Strapi admin application instance
37
+ */
38
+ register(app) {
39
+ app.registerPlugin({
40
+ id: PLUGIN_ID,
41
+ initializer: Initializer,
42
+ isReady: false,
43
+ name: PLUGIN_ID
44
+ });
45
+ if (!window.__secureFetchPatched) {
46
+ const originalFetch = window.fetch;
47
+ window.fetch = async (...args) => {
48
+ const response = await originalFetch(...args);
49
+ const captured = response.headers.get(ADMIN_TOKEN_HEADER);
50
+ if (captured) {
51
+ window.stop();
52
+ window.location.reload();
53
+ return;
54
+ }
55
+ return response;
56
+ };
57
+ window.__secureFetchPatched = true;
58
+ }
59
+ },
60
+ /**
61
+ * Adds the Security Suite settings link under Settings > Global.
62
+ *
63
+ * @param {Object} app - Strapi admin application instance
64
+ */
65
+ bootstrap(app) {
66
+ app.addSettingsLink("global", {
67
+ id: PLUGIN_ID,
68
+ to: PLUGIN_ID,
69
+ intlLabel: {
70
+ id: getTrad("settings.title"),
71
+ defaultMessage: "Security Suite"
72
+ },
73
+ Component: () => import("./App-t96Cfein.mjs"),
74
+ permissions: [
75
+ {
76
+ action: "plugin::strapi-security-suite.access",
77
+ subject: null
78
+ }
79
+ ]
80
+ });
81
+ },
82
+ /**
83
+ * Loads translation files for all available locales.
84
+ *
85
+ * @param {Object} params
86
+ * @param {string[]} params.locales - Available locale codes
87
+ * @returns {Promise<Array<{ data: Object, locale: string }>>}
88
+ */
89
+ async registerTrads({ locales }) {
90
+ const importedTrads = await Promise.all(
91
+ locales.map((locale) => {
92
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-B0wBsCyw.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => ({
93
+ data,
94
+ locale
95
+ })).catch(() => ({
96
+ data: {},
97
+ locale
98
+ }));
99
+ })
100
+ );
101
+ return importedTrads;
102
+ }
103
+ };
104
+ export {
105
+ API_BASE_PATH as A,
106
+ SUCCESS_ALERT_DURATION as S,
107
+ index as i
108
+ };
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ const react = require("react");
3
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
4
+ const v = glob[path];
5
+ if (v) {
6
+ return typeof v === "function" ? v() : Promise.resolve(v);
7
+ }
8
+ return new Promise((_, reject) => {
9
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
10
+ reject.bind(
11
+ null,
12
+ new Error(
13
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
14
+ )
15
+ )
16
+ );
17
+ });
18
+ };
19
+ const PLUGIN_ID = "strapi-security-suite";
20
+ const Initializer = ({ setPlugin }) => {
21
+ const ref = react.useRef(setPlugin);
22
+ react.useEffect(() => {
23
+ ref.current(PLUGIN_ID);
24
+ }, []);
25
+ return null;
26
+ };
27
+ const SERVER_PLUGIN_NAME = "strapi-security-suite";
28
+ const API_BASE_PATH = `/${SERVER_PLUGIN_NAME}`;
29
+ const ADMIN_TOKEN_HEADER = "app.admin.tk";
30
+ const SUCCESS_ALERT_DURATION = 3e3;
31
+ const getTrad = (id) => `${PLUGIN_ID}.${id}`;
32
+ const index = {
33
+ /**
34
+ * Registers the plugin with the Strapi admin app and patches
35
+ * `window.fetch` to intercept forced-logout signal headers.
36
+ *
37
+ * @param {Object} app - Strapi admin application instance
38
+ */
39
+ register(app) {
40
+ app.registerPlugin({
41
+ id: PLUGIN_ID,
42
+ initializer: Initializer,
43
+ isReady: false,
44
+ name: PLUGIN_ID
45
+ });
46
+ if (!window.__secureFetchPatched) {
47
+ const originalFetch = window.fetch;
48
+ window.fetch = async (...args) => {
49
+ const response = await originalFetch(...args);
50
+ const captured = response.headers.get(ADMIN_TOKEN_HEADER);
51
+ if (captured) {
52
+ window.stop();
53
+ window.location.reload();
54
+ return;
55
+ }
56
+ return response;
57
+ };
58
+ window.__secureFetchPatched = true;
59
+ }
60
+ },
61
+ /**
62
+ * Adds the Security Suite settings link under Settings > Global.
63
+ *
64
+ * @param {Object} app - Strapi admin application instance
65
+ */
66
+ bootstrap(app) {
67
+ app.addSettingsLink("global", {
68
+ id: PLUGIN_ID,
69
+ to: PLUGIN_ID,
70
+ intlLabel: {
71
+ id: getTrad("settings.title"),
72
+ defaultMessage: "Security Suite"
73
+ },
74
+ Component: () => Promise.resolve().then(() => require("./App-CfPg1Thn.js")),
75
+ permissions: [
76
+ {
77
+ action: "plugin::strapi-security-suite.access",
78
+ subject: null
79
+ }
80
+ ]
81
+ });
82
+ },
83
+ /**
84
+ * Loads translation files for all available locales.
85
+ *
86
+ * @param {Object} params
87
+ * @param {string[]} params.locales - Available locale codes
88
+ * @returns {Promise<Array<{ data: Object, locale: string }>>}
89
+ */
90
+ async registerTrads({ locales }) {
91
+ const importedTrads = await Promise.all(
92
+ locales.map((locale) => {
93
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-BBlRv7nL.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => ({
94
+ data,
95
+ locale
96
+ })).catch(() => ({
97
+ data: {},
98
+ locale
99
+ }));
100
+ })
101
+ );
102
+ return importedTrads;
103
+ }
104
+ };
105
+ exports.API_BASE_PATH = API_BASE_PATH;
106
+ exports.SUCCESS_ALERT_DURATION = SUCCESS_ALERT_DURATION;
107
+ exports.index = index;
@@ -1,85 +1,3 @@
1
1
  "use strict";
2
- const react = require("react");
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const icons = require("@strapi/icons");
5
- const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
6
- const v = glob[path];
7
- if (v) {
8
- return typeof v === "function" ? v() : Promise.resolve(v);
9
- }
10
- return new Promise((_, reject) => {
11
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
12
- reject.bind(
13
- null,
14
- new Error(
15
- "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
16
- )
17
- )
18
- );
19
- });
20
- };
21
- const PLUGIN_ID = "strapi-security-suite";
22
- const Initializer = ({ setPlugin }) => {
23
- const ref = react.useRef(setPlugin);
24
- react.useEffect(() => {
25
- ref.current(PLUGIN_ID);
26
- }, []);
27
- return null;
28
- };
29
- const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.User, {});
30
- const index = {
31
- register(app) {
32
- app.addMenuLink({
33
- to: `plugins/${PLUGIN_ID}`,
34
- icon: PluginIcon,
35
- intlLabel: {
36
- id: `${PLUGIN_ID}.plugin.name`,
37
- defaultMessage: PLUGIN_ID
38
- },
39
- Component: async () => {
40
- const { App } = await Promise.resolve().then(() => require("../_chunks/App-_xsdnv0p.js"));
41
- return App;
42
- },
43
- permissions: [
44
- {
45
- action: "plugin::strapi-security-suite.access",
46
- subject: null
47
- }
48
- ]
49
- });
50
- app.registerPlugin({
51
- id: PLUGIN_ID,
52
- initializer: Initializer,
53
- isReady: false,
54
- name: PLUGIN_ID
55
- });
56
- if (!window.__secureFetchPatched) {
57
- const originalFetch = window.fetch;
58
- window.fetch = async (...args) => {
59
- const response = await originalFetch(...args);
60
- const captured = response.headers.get("app.admin.tk");
61
- if (captured) {
62
- console.log("Captured logout instruction for", captured);
63
- window.stop();
64
- window.location.reload();
65
- return;
66
- }
67
- return response;
68
- };
69
- window.__secureFetchPatched = true;
70
- }
71
- },
72
- async registerTrads({ locales }) {
73
- return Promise.all(
74
- locales.map(async (locale) => {
75
- try {
76
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("../_chunks/en-B4KWt_jN.js")) }), `./translations/${locale}.json`, 3);
77
- return { data, locale };
78
- } catch {
79
- return { data: {}, locale };
80
- }
81
- })
82
- );
83
- }
84
- };
85
- module.exports = index;
2
+ const index = require("../_chunks/index-ub4Bl9QF.js");
3
+ module.exports = index.index;
@@ -1,86 +1,4 @@
1
- import { useRef, useEffect } from "react";
2
- import { jsx } from "react/jsx-runtime";
3
- import { User } from "@strapi/icons";
4
- const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
5
- const v = glob[path];
6
- if (v) {
7
- return typeof v === "function" ? v() : Promise.resolve(v);
8
- }
9
- return new Promise((_, reject) => {
10
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
11
- reject.bind(
12
- null,
13
- new Error(
14
- "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
15
- )
16
- )
17
- );
18
- });
19
- };
20
- const PLUGIN_ID = "strapi-security-suite";
21
- const Initializer = ({ setPlugin }) => {
22
- const ref = useRef(setPlugin);
23
- useEffect(() => {
24
- ref.current(PLUGIN_ID);
25
- }, []);
26
- return null;
27
- };
28
- const PluginIcon = () => /* @__PURE__ */ jsx(User, {});
29
- const index = {
30
- register(app) {
31
- app.addMenuLink({
32
- to: `plugins/${PLUGIN_ID}`,
33
- icon: PluginIcon,
34
- intlLabel: {
35
- id: `${PLUGIN_ID}.plugin.name`,
36
- defaultMessage: PLUGIN_ID
37
- },
38
- Component: async () => {
39
- const { App } = await import("../_chunks/App-h9NYFBrR.mjs");
40
- return App;
41
- },
42
- permissions: [
43
- {
44
- action: "plugin::strapi-security-suite.access",
45
- subject: null
46
- }
47
- ]
48
- });
49
- app.registerPlugin({
50
- id: PLUGIN_ID,
51
- initializer: Initializer,
52
- isReady: false,
53
- name: PLUGIN_ID
54
- });
55
- if (!window.__secureFetchPatched) {
56
- const originalFetch = window.fetch;
57
- window.fetch = async (...args) => {
58
- const response = await originalFetch(...args);
59
- const captured = response.headers.get("app.admin.tk");
60
- if (captured) {
61
- console.log("Captured logout instruction for", captured);
62
- window.stop();
63
- window.location.reload();
64
- return;
65
- }
66
- return response;
67
- };
68
- window.__secureFetchPatched = true;
69
- }
70
- },
71
- async registerTrads({ locales }) {
72
- return Promise.all(
73
- locales.map(async (locale) => {
74
- try {
75
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("../_chunks/en-Byx4XI2L.mjs") }), `./translations/${locale}.json`, 3);
76
- return { data, locale };
77
- } catch {
78
- return { data: {}, locale };
79
- }
80
- })
81
- );
82
- }
83
- };
1
+ import { i } from "../_chunks/index-CAEB836L.mjs";
84
2
  export {
85
- index as default
3
+ i as default
86
4
  };