current hostname as cookie domain, group edit member bugfix
This commit is contained in:
parent
6c551b08d8
commit
72d2850e83
@ -84,15 +84,8 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function loadDefaults(): Settings {
|
public static function loadDefaults(): Settings {
|
||||||
$hostname = $_SERVER["SERVER_NAME"] ?? null;
|
|
||||||
if (empty($hostname)) {
|
|
||||||
$hostname = $_SERVER["HTTP_HOST"] ?? null;
|
|
||||||
if (empty($hostname)) {
|
|
||||||
$hostname = "localhost";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$protocol = getProtocol();
|
$protocol = getProtocol();
|
||||||
|
$hostname = getHostName();
|
||||||
$settings = new Settings();
|
$settings = new Settings();
|
||||||
|
|
||||||
// General
|
// General
|
||||||
|
@ -92,7 +92,8 @@ class Context {
|
|||||||
|
|
||||||
public function sendCookies(): void {
|
public function sendCookies(): void {
|
||||||
// TODO: what will we do, when there is a domain mismatch? forbid access or just send cookies for the current domain? or should we send a redirect?
|
// TODO: what will we do, when there is a domain mismatch? forbid access or just send cookies for the current domain? or should we send a redirect?
|
||||||
$domain = $this->getSettings()->getDomain();
|
// $domain = $this->getSettings()->getDomain();
|
||||||
|
$domain = getCurrentHostName();
|
||||||
$this->language->sendCookie($domain);
|
$this->language->sendCookie($domain);
|
||||||
$this->session?->sendCookie($domain);
|
$this->session?->sendCookie($domain);
|
||||||
$this->session?->update();
|
$this->session?->update();
|
||||||
@ -202,7 +203,7 @@ class Context {
|
|||||||
return $this->language;
|
return $this->language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function invalidateSessions(bool $keepCurrent = true): bool {
|
public function invalidateSessions(bool $keepCurrent = false): bool {
|
||||||
$query = $this->sql->update("Session")
|
$query = $this->sql->update("Session")
|
||||||
->set("active", false)
|
->set("active", false)
|
||||||
->whereEq("user_id", $this->user->getId());
|
->whereEq("user_id", $this->user->getId());
|
||||||
|
@ -36,6 +36,18 @@ function getProtocol(): string {
|
|||||||
return $isSecure ? 'https' : 'http';
|
return $isSecure ? 'https' : 'http';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentHostName(): string {
|
||||||
|
$hostname = $_SERVER["SERVER_NAME"] ?? null;
|
||||||
|
if (empty($hostname)) {
|
||||||
|
$hostname = $_SERVER["HTTP_HOST"] ?? null;
|
||||||
|
if (empty($hostname)) {
|
||||||
|
$hostname = gethostname();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hostname;
|
||||||
|
}
|
||||||
|
|
||||||
function uuidv4(): string {
|
function uuidv4(): string {
|
||||||
$data = random_bytes(16);
|
$data = random_bytes(16);
|
||||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
|
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, {lazy, Suspense, useCallback, useState} from "react";
|
import React, {lazy, Suspense, useCallback, useState} from "react";
|
||||||
import {BrowserRouter, Route, Routes} from "react-router-dom";
|
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
|
||||||
import Dialog from "shared/elements/dialog";
|
import Dialog from "shared/elements/dialog";
|
||||||
import Sidebar from "./elements/sidebar";
|
import Sidebar from "./elements/sidebar";
|
||||||
import Footer from "./elements/footer";
|
import Footer from "./elements/footer";
|
||||||
@ -73,7 +73,7 @@ export default function AdminDashboard(props) {
|
|||||||
<section className={"content"}>
|
<section className={"content"}>
|
||||||
<Suspense fallback={<div>{L("general.loading")}... </div>}>
|
<Suspense fallback={<div>{L("general.loading")}... </div>}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path={"/admin"} element={<Overview {...controlObj} />}/>
|
<Route path={"/admin"} element={<Navigate to={"/admin/dashboard"} />}/>
|
||||||
<Route path={"/admin/dashboard"} element={<Overview {...controlObj} />}/>
|
<Route path={"/admin/dashboard"} element={<Overview {...controlObj} />}/>
|
||||||
<Route path={"/admin/users"} element={<UserListView {...controlObj} />}/>
|
<Route path={"/admin/users"} element={<UserListView {...controlObj} />}/>
|
||||||
<Route path={"/admin/user/:userId"} element={<UserEditView {...controlObj} />}/>
|
<Route path={"/admin/user/:userId"} element={<UserEditView {...controlObj} />}/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {useCallback, useContext, useEffect, useState} from "react";
|
import {useCallback, useContext, useEffect, useRef, useState} from "react";
|
||||||
import {Link, useNavigate, useParams} from "react-router-dom";
|
import {Link, useNavigate, useParams} from "react-router-dom";
|
||||||
import {LocaleContext} from "shared/locale";
|
import {LocaleContext} from "shared/locale";
|
||||||
import SearchField from "shared/elements/search-field";
|
import SearchField from "shared/elements/search-field";
|
||||||
@ -34,7 +34,7 @@ export default function EditGroupView(props) {
|
|||||||
const [fetchGroup, setFetchGroup] = useState(!isNewGroup);
|
const [fetchGroup, setFetchGroup] = useState(!isNewGroup);
|
||||||
const [group, setGroup] = useState(isNewGroup ? defaultGroupData : null);
|
const [group, setGroup] = useState(isNewGroup ? defaultGroupData : null);
|
||||||
const [members, setMembers] = useState([]);
|
const [members, setMembers] = useState([]);
|
||||||
const [selectedUser, setSelectedUser] = useState(null);
|
const selectedUserRef = useRef(null);
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
const [dialogData, setDialogData] = useState({open: false});
|
const [dialogData, setDialogData] = useState({open: false});
|
||||||
@ -86,19 +86,19 @@ export default function EditGroupView(props) {
|
|||||||
}, [api, showDialog, groupId, members]);
|
}, [api, showDialog, groupId, members]);
|
||||||
|
|
||||||
const onAddMember = useCallback(() => {
|
const onAddMember = useCallback(() => {
|
||||||
if (selectedUser) {
|
if (selectedUserRef.current) {
|
||||||
api.addGroupMember(groupId, selectedUser.id).then(data => {
|
api.addGroupMember(groupId, selectedUserRef.current.id).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
showDialog(data.msg, L("account.add_group_member_error"));
|
showDialog(data.msg, L("account.add_group_member_error"));
|
||||||
} else {
|
} else {
|
||||||
let newMembers = [...members];
|
let newMembers = [...members];
|
||||||
newMembers.push(selectedUser);
|
newMembers.push(selectedUserRef.current);
|
||||||
setMembers(newMembers);
|
setMembers(newMembers);
|
||||||
}
|
}
|
||||||
setSelectedUser(null);
|
selectedUserRef.current = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [api, showDialog, groupId, selectedUser, members])
|
}, [api, showDialog, groupId, selectedUserRef, members])
|
||||||
|
|
||||||
const onSave = useCallback(() => {
|
const onSave = useCallback(() => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
@ -152,15 +152,19 @@ export default function EditGroupView(props) {
|
|||||||
size: "small", key: "search",
|
size: "small", key: "search",
|
||||||
element: SearchField,
|
element: SearchField,
|
||||||
onSearch: v => onSearchUser(v),
|
onSearch: v => onSearchUser(v),
|
||||||
onSelect: u => setSelectedUser(u),
|
onSelect: u => { selectedUserRef.current = u },
|
||||||
getOptionLabel: u => u.fullName || u.name
|
getOptionLabel: u => u.fullName || u.name
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
onOption: (option) => option === 0 ?
|
onOption: (option) => {
|
||||||
onAddMember() :
|
if(option === 0) {
|
||||||
setSelectedUser(null)
|
onAddMember()
|
||||||
|
} else {
|
||||||
|
selectedUserRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, [onAddMember, onSearchUser, setSelectedUser, setDialogData]);
|
}, [onAddMember, onSearchUser, selectedUserRef, setDialogData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onFetchGroup();
|
onFetchGroup();
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
CircularProgress,
|
CircularProgress,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormLabel, Paper, styled,
|
FormLabel, styled,
|
||||||
TextField
|
TextField
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import {
|
||||||
@ -26,6 +26,7 @@ import ButtonBar from "../../elements/button-bar";
|
|||||||
import MfaTotp from "./mfa-totp";
|
import MfaTotp from "./mfa-totp";
|
||||||
import MfaFido from "./mfa-fido";
|
import MfaFido from "./mfa-fido";
|
||||||
import Dialog from "shared/elements/dialog";
|
import Dialog from "shared/elements/dialog";
|
||||||
|
import PasswordStrength from "shared/elements/password-strength";
|
||||||
|
|
||||||
const GpgKeyField = styled(TextField)((props) => ({
|
const GpgKeyField = styled(TextField)((props) => ({
|
||||||
"& > div": {
|
"& > div": {
|
||||||
@ -211,8 +212,6 @@ export default function ProfileView(props) {
|
|||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}, [showDialog]);
|
}, [showDialog]);
|
||||||
|
|
||||||
console.log("SELECTED USER:", profile.twoFactorToken);
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className={"content-header"}>
|
<div className={"content-header"}>
|
||||||
<div className={"container-fluid"}>
|
<div className={"container-fluid"}>
|
||||||
@ -285,6 +284,9 @@ export default function ProfileView(props) {
|
|||||||
onChange={e => setChangePassword({...changePassword, confirm: e.target.value })} />
|
onChange={e => setChangePassword({...changePassword, confirm: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</ProfileFormGroup>
|
||||||
|
<Box className={"w-50"}>
|
||||||
|
<PasswordStrength password={changePassword.new} minLength={6} />
|
||||||
|
</Box>
|
||||||
</CollapseBox>
|
</CollapseBox>
|
||||||
|
|
||||||
<CollapseBox title={L("account.gpg_key")} open={openedTab === "gpg"}
|
<CollapseBox title={L("account.gpg_key")} open={openedTab === "gpg"}
|
||||||
|
58
react/shared/elements/password-strength.js
Normal file
58
react/shared/elements/password-strength.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import {Box, styled} from "@mui/material";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "../locale";
|
||||||
|
import {sprintf} from "sprintf-js";
|
||||||
|
|
||||||
|
const PasswordStrengthBox = styled(Box)((props) => ({
|
||||||
|
textAlign: "center",
|
||||||
|
borderRadius: 5,
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: props.theme.palette.action,
|
||||||
|
padding: props.theme.spacing(0.5),
|
||||||
|
position: "relative",
|
||||||
|
"& > div": {
|
||||||
|
zIndex: 0,
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
"& > span": {
|
||||||
|
zIndex: 1,
|
||||||
|
position: "relative",
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default function PasswordStrength(props) {
|
||||||
|
|
||||||
|
const {password, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
const ref = 14;
|
||||||
|
let strength = password.length >= ref ? 100 : Math.round(password.length / ref * 100.0);
|
||||||
|
let label = "account.password_very_weak";
|
||||||
|
let bgColor = "red";
|
||||||
|
|
||||||
|
if (strength >= 85) {
|
||||||
|
label = "account.password_very_strong";
|
||||||
|
bgColor = "darkgreen";
|
||||||
|
} else if (strength >= 65) {
|
||||||
|
label = "account.password_strong";
|
||||||
|
bgColor = "green";
|
||||||
|
} else if (strength >= 50) {
|
||||||
|
label = "account.password_ok";
|
||||||
|
bgColor = "yellow";
|
||||||
|
} else if (strength >= 25) {
|
||||||
|
label = "account.password_weak";
|
||||||
|
bgColor = "orange";
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PasswordStrengthBox {...other}>
|
||||||
|
<Box position={"absolute"} sx={{
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
width: sprintf("%d%%", strength),
|
||||||
|
}} />
|
||||||
|
<span>{L(label)}</span>
|
||||||
|
</PasswordStrengthBox>
|
||||||
|
}
|
@ -129,6 +129,7 @@ export default function LoginForm(props) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("navigator.credentials.get")
|
||||||
set2FAToken({ ...tfaToken, step: 1, error: "" });
|
set2FAToken({ ...tfaToken, step: 1, error: "" });
|
||||||
navigator.credentials.get({
|
navigator.credentials.get({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
|
Loading…
Reference in New Issue
Block a user