diff --git a/.env.development b/.env.development index d8943b8..da89f5b 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1,4 @@ NEXT_PUBLIC_NODE_ENV=development -NEXT_PUBLIC_ENCRYPTION_KEY=your-secure-encryption-key +NEXT_PUBLIC_ENCRYPTION_KEY=1 +NEXT_PUBLIC_ENCRYPTION_IV=1 diff --git a/.env.production b/.env.production index 0c493c7..7503ab2 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1,3 @@ NEXT_PUBLIC_NODE_ENV=production -NEXT_PUBLIC_ENCRYPTION_KEY=your-secure-encryption-key +NEXT_PUBLIC_ENCRYPTION_KEY=1 +NEXT_PUBLIC_ENCRYPTION_IV=1 diff --git a/components/modales/settingsModal/SettingsModal.tsx b/components/modales/settingsModal/SettingsModal.tsx index 5fde5e8..1c8ca3f 100644 --- a/components/modales/settingsModal/SettingsModal.tsx +++ b/components/modales/settingsModal/SettingsModal.tsx @@ -1,18 +1,17 @@ -"use client"; //components/modales/settingsModal/SettingsModal.jsx +"use client"; // components/modales/settingsModal/SettingsModal.jsx import React, { useState, useEffect } from "react"; import ReactModal from "react-modal"; -import { ClipLoader } from "react-spinners"; import "bootstrap-icons/font/bootstrap-icons.css"; -import { useSelector, useDispatch } from "react-redux"; +import { useSelector } from "react-redux"; import handleClearDatabase from "./handlers/handleClearDatabase"; import handleReboot from "./handlers/handleReboot"; import handleSetDateTime from "./handlers/handleSetDateTime"; import handleSubmit from "./handlers/handleSubmit"; -import { useRouter } from "next/router"; - import bcrypt from "bcryptjs"; +import CryptoJS from "crypto-js"; ReactModal.setAppElement("#__next"); + const USERS = { Admin: { username: "admin", @@ -21,7 +20,22 @@ const USERS = { role: "Admin", }, }; -// Function to generate JWT token + +// Funktion zur Generierung eines AES-Schlüssels und IVs +function generateKeyAndIV() { + const encryptionKey = process.env.NEXT_PUBLIC_ENCRYPTION_KEY; + const encryptionIV = process.env.NEXT_PUBLIC_ENCRYPTION_IV; + + if (!encryptionKey || !encryptionIV) { + throw new Error("Encryption key or IV is not defined."); + } + + const key = CryptoJS.enc.Utf8.parse(encryptionKey); + const iv = CryptoJS.enc.Utf8.parse(encryptionIV); + return { key, iv }; +} + +// Funktion zur Generierung eines Tokens function generateToken(user) { const payload = { username: user.username, @@ -29,40 +43,34 @@ function generateToken(user) { exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten }; const token = JSON.stringify(payload); - const encryptedToken = CryptoJS.AES.encrypt( - token, - process.env.NEXT_PUBLIC_ENCRYPTION_KEY - ).toString(); + const { key, iv } = generateKeyAndIV(); + const encryptedToken = CryptoJS.AES.encrypt(token, key, { iv }).toString(); return encryptedToken; } + +// Funktion zur Entschlüsselung des Tokens function decryptToken(encryptedToken) { - const bytes = CryptoJS.AES.decrypt( - encryptedToken, - process.env.NEXT_PUBLIC_ENCRYPTION_KEY - ); + const { key, iv } = generateKeyAndIV(); + const bytes = CryptoJS.AES.decrypt(encryptedToken, key, { iv }); const decryptedToken = bytes.toString(CryptoJS.enc.Utf8); return JSON.parse(decryptedToken); } function SettingModal({ showModal, onClose }) { const [isAdminLoggedIn, setAdminLoggedIn] = useState(false); - //const isAdminLoggedIn = sessionStorage.getItem("token"); - const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); - const [showLoginForm, setShowLoginForm] = useState(false); // Zustand für Login-Formular - const router = useRouter(); + const [showLoginForm, setShowLoginForm] = useState(false); function handleAdminLogin(e) { e.preventDefault(); - const user = USERS.Admin; // Finde den Admin-Benutzer + const user = USERS.Admin; bcrypt.compare(password, user.password, (err, isMatch) => { if (isMatch) { const token = generateToken(user); - sessionStorage.setItem("token", token); // Speichere Token in SessionStorage + sessionStorage.setItem("token", token); localStorage.setItem("isAdminLoggedIn", "true"); - setShowLoginForm(false); onClose(); } else { @@ -72,6 +80,25 @@ function SettingModal({ showModal, onClose }) { } }); } + + useEffect(() => { + if (showModal) { + const token = sessionStorage.getItem("token"); + if (token) { + try { + const { exp } = decryptToken(token); + if (Date.now() < exp) { + setAdminLoggedIn(true); + } else { + sessionStorage.removeItem("token"); + localStorage.setItem("isAdminLoggedIn", "false"); + } + } catch (error) { + console.error("Token-Entschlüsselung fehlgeschlagen:", error); + } + } + } + }, [showModal]); const deviceName_Redux = useSelector((state) => state.variables.deviceName); const mac1_Redux = useSelector((state) => state.variables.mac1); const ip_Redux = useSelector((state) => state.variables.ip); @@ -154,16 +181,30 @@ function SettingModal({ showModal, onClose }) { ntpTimezone_Redux, active_Redux, ]); + useEffect(() => { - // Check if a valid token exists in localStorage + // Überprüfen, ob ein Token im SessionStorage vorhanden ist const token = sessionStorage.getItem("token"); if (token) { - setAdminLoggedIn(true); - const { exp } = JSON.parse(atob(token)); - if (Date.now() < exp) { - setAdminLoggedIn(true); - } else { - // localStorage.removeItem("token"); // Remove expired token + try { + // Token mit CryptoJS entschlüsseln + const bytes = CryptoJS.AES.decrypt( + token, + process.env.NEXT_PUBLIC_ENCRYPTION_KEY + ); + const decryptedToken = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)); + + // Ablaufzeit überprüfen + if (Date.now() < decryptedToken.exp) { + setAdminLoggedIn(true); + } else { + // Token ist abgelaufen + sessionStorage.removeItem("token"); + setAdminLoggedIn(false); + } + } catch (error) { + console.error("Fehler beim Entschlüsseln des Tokens:", error); + setAdminLoggedIn(false); } } }, []); diff --git a/package-lock.json b/package-lock.json index debea4c..5fe3912 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/bcryptjs": "^2.4.6", + "@types/crypto-js": "^4.2.2", "@types/cypress": "^1.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.10.10", @@ -1602,6 +1603,12 @@ "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", "dev": true }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, "node_modules/@types/cypress": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@types/cypress/-/cypress-1.1.6.tgz", diff --git a/package.json b/package.json index 04fa320..cfe437d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/bcryptjs": "^2.4.6", + "@types/crypto-js": "^4.2.2", "@types/cypress": "^1.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.10.10", diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 6dce135..04c77cc 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -272,34 +272,17 @@ function Dashboard() {

{gateway}

+
- OPC UA Status +
OPC-UA
+
-

OPCUA Status

+

Status

{opcUaZustand}

- OPC UA Clients -
-

Verbundene Clients

-

- {opcUaActiveClientCount} -

-
-
- -
- Nodeset Name

Nodeset Name

diff --git a/utils/loadWindowVariables.ts b/utils/loadWindowVariables.ts index f95e5a8..056aff6 100644 --- a/utils/loadWindowVariables.ts +++ b/utils/loadWindowVariables.ts @@ -54,6 +54,9 @@ export async function loadWindowVariables(): Promise { "win_analogeEingaenge6", "win_analogeEingaenge7", "win_analogeEingaenge8", + "win_opcUaZustand", + "win_opcUaActiveClientCount", + "win_opcUaNodesetName", ]; const loadScript = (src: string): Promise => {