feat: Benutzerverwaltung als neuen Reiter in Einstellungen-Seite integriert
This commit is contained in:
79
README.md
79
README.md
@@ -237,3 +237,82 @@ Angezeigt mit [Mermaid.js](https://mermaid-js.github.io/).
|
|||||||
## 👋 Kontakt für Fragen
|
## 👋 Kontakt für Fragen
|
||||||
|
|
||||||
Bei Fragen oder Problemen bitte an das CPLv4.0 Entwicklungsteam wenden.
|
Bei Fragen oder Problemen bitte an das CPLv4.0 Entwicklungsteam wenden.
|
||||||
|
|
||||||
|
# 🔐 Benutzerverwaltung und Sicherheit
|
||||||
|
|
||||||
|
## Backend TLS-Authentifizierung
|
||||||
|
|
||||||
|
- **TLS-Authentifizierung** erfolgt über das Backend (Embedded-System, CPL).
|
||||||
|
- Wird genutzt für HTTPS-Verbindungen und verschlüsselte OPC UA-Kommunikation.
|
||||||
|
- TLS-Benutzername und Passwort werden **nicht** vom Frontend verwaltet.
|
||||||
|
- Diese Login-Daten dienen **nur der sicheren Netzwerkverbindung** (z.B. HTTPS, Zertifikate).
|
||||||
|
|
||||||
|
## Frontend-Admin-Login (eigene Benutzerverwaltung)
|
||||||
|
|
||||||
|
- Das **Frontend** besitzt eine eigene **Benutzerverwaltung** für erweiterte Funktionen.
|
||||||
|
- Beispiele für geschützte Funktionen:
|
||||||
|
- Firmware-Update der Kabelüberwachungsmodule
|
||||||
|
- Erweiterte Systemeinstellungen
|
||||||
|
|
||||||
|
### Benutzerstruktur
|
||||||
|
|
||||||
|
Benutzer werden definiert unter:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/components/main/settingsPageComponents/config/users.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Beispiel für die Benutzerstruktur:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const USERS = {
|
||||||
|
AdminUser: {
|
||||||
|
username: "admin",
|
||||||
|
password: "$2a$10$xpq/.tcOJN/LXfzdCcCVrenlBh2nRlM1R1ISY7dd1q2qGWC9Fyd2G", // bcrypt Hash von "admin"
|
||||||
|
role: "Admin",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default USERS;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sicherheit
|
||||||
|
|
||||||
|
- **Passwörter sind verschlüsselt** mit `bcryptjs`.
|
||||||
|
- Klartext-Passwörter werden **niemals** im Quellcode gespeichert.
|
||||||
|
|
||||||
|
### Rollenbeschreibung
|
||||||
|
|
||||||
|
| Benutzerrolle | Beschreibung |
|
||||||
|
| :------------------- | :-------------------------------------------------------------- |
|
||||||
|
| TLS-User (Backend) | Anmeldung für sichere HTTPS-Verbindung, keine Frontend-Funktion |
|
||||||
|
| AdminUser (Frontend) | Darf Firmware-Updates durchführen, Konfiguration ändern |
|
||||||
|
|
||||||
|
### Anleitung für Entwickler: Passwort-Hash erzeugen
|
||||||
|
|
||||||
|
Um ein neues Passwort zu erstellen, benutze folgendes Script:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import bcrypt from "bcryptjs";
|
||||||
|
|
||||||
|
const hash = bcrypt.hashSync("meinNeuesPasswort", 10);
|
||||||
|
console.log(hash); // Kopieren und in users.ts einfügen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Stelle sicher, dass das neue Passwort korrekt gehasht ist, bevor es ins Repository hochgeladen wird.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zusammenfassung
|
||||||
|
|
||||||
|
| Thema | Hinweis |
|
||||||
|
| :--------------------- | :-------------------------------------------------------- |
|
||||||
|
| TLS-Login (Backend) | Nur für Netzwerkverschlüsselung |
|
||||||
|
| Admin-Login (Frontend) | Schutz für kritische Funktionen |
|
||||||
|
| Passwort-Handling | Immer bcrypt verwenden |
|
||||||
|
| Benutzerdatei | `/components/main/settingsPageComponents/config/users.ts` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Stand:** 28. April 2025
|
||||||
|
**Projekt:** CPLv4.0 Weboberfläche
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
"use client";
|
||||||
|
// /components/main/settingsPageComponents/UserManagementSettings.tsx
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { AppDispatch } from "../../../redux/store";
|
||||||
|
import { useAdminAuth } from "./hooks/useAdminAuth";
|
||||||
|
import handleAdminLogin from "./handlers/handleAdminLogin";
|
||||||
|
|
||||||
|
const UserManagementSettings: React.FC = () => {
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
const { isAdminLoggedIn, logoutAdmin } = useAdminAuth(true);
|
||||||
|
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [loginSuccess, setLoginSuccess] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
handleAdminLogin(
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
() => {
|
||||||
|
setLoginSuccess(true);
|
||||||
|
setError("");
|
||||||
|
},
|
||||||
|
(errorMsg) => {
|
||||||
|
setLoginSuccess(false);
|
||||||
|
setError(errorMsg);
|
||||||
|
},
|
||||||
|
dispatch
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 md:p-3 bg-gray-100 max-w-5xl mr-auto">
|
||||||
|
<h2 className="text-sm md:text-md font-bold mb-4">
|
||||||
|
Benutzerverwaltung (nur Login)
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Admin Login/Logout */}
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{isAdminLoggedIn ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
|
onClick={logoutAdmin}
|
||||||
|
>
|
||||||
|
Admin abmelden
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-row gap-3">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Benutzername"
|
||||||
|
className="border border-gray-300 rounded h-8 p-1 w-full text-xs"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Passwort"
|
||||||
|
className="border border-gray-300 rounded h-8 p-1 w-full text-xs"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
|
onClick={handleLogin}
|
||||||
|
>
|
||||||
|
Admin anmelden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loginSuccess && (
|
||||||
|
<p className="text-green-600 text-xs mt-2">Login erfolgreich!</p>
|
||||||
|
)}
|
||||||
|
{error && <p className="text-red-500 text-xs mt-2">{error}</p>}
|
||||||
|
|
||||||
|
{/*
|
||||||
|
// Benutzerverwaltungstabelle (kommt später)
|
||||||
|
<table>...</table>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserManagementSettings;
|
||||||
@@ -6,5 +6,5 @@
|
|||||||
2: Patch oder Hotfix (Bugfixes oder kleine Änderungen).
|
2: Patch oder Hotfix (Bugfixes oder kleine Änderungen).
|
||||||
|
|
||||||
*/
|
*/
|
||||||
const webVersion = "1.6.292";
|
const webVersion = "1.6.293";
|
||||||
export default webVersion;
|
export default webVersion;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import GeneralSettings from "../components/main/settingsPageComponents/GeneralSe
|
|||||||
import OPCUAInterfaceSettings from "../components/main/settingsPageComponents/OPCUAInterfaceSettings";
|
import OPCUAInterfaceSettings from "../components/main/settingsPageComponents/OPCUAInterfaceSettings";
|
||||||
import DatabaseSettings from "../components/main/settingsPageComponents/DatabaseSettings";
|
import DatabaseSettings from "../components/main/settingsPageComponents/DatabaseSettings";
|
||||||
import NTPSettings from "../components/main/settingsPageComponents/NTPSettings";
|
import NTPSettings from "../components/main/settingsPageComponents/NTPSettings";
|
||||||
|
import UserManagementSettings from "../components/main/settingsPageComponents/UserManagementSettings";
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const [activeTab, setActiveTab] = useState("tab1");
|
const [activeTab, setActiveTab] = useState("tab1");
|
||||||
@@ -59,6 +60,16 @@ export default function Settings() {
|
|||||||
>
|
>
|
||||||
NTP
|
NTP
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className={`px-4 py-2 ${
|
||||||
|
activeTab === "tab5"
|
||||||
|
? "border-b-2 border-blue-500 text-blue-500"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab("tab5")}
|
||||||
|
>
|
||||||
|
Benutzerverwaltung
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tab-Inhalt */}
|
{/* Tab-Inhalt */}
|
||||||
@@ -67,6 +78,7 @@ export default function Settings() {
|
|||||||
{activeTab === "tab2" && <OPCUAInterfaceSettings />}
|
{activeTab === "tab2" && <OPCUAInterfaceSettings />}
|
||||||
{activeTab === "tab3" && <DatabaseSettings />}
|
{activeTab === "tab3" && <DatabaseSettings />}
|
||||||
{activeTab === "tab4" && <NTPSettings />}
|
{activeTab === "tab4" && <NTPSettings />}
|
||||||
|
{activeTab === "tab5" && <UserManagementSettings />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user