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