fix(admin): persist private sessions across container replacement
deploy-preland / deploy zoomstage.online (push) Successful in 58s
deploy-preland / deploy zoomstage.online (push) Successful in 58s
This commit is contained in:
1 parent
2cfa2e2d2e
commit
d04b40519e
4 files changed
+150
-2
No files matched your search
+24
-1
@@ -6,10 +6,33 @@ final class Auth
|
||||
public function __construct(private Store $store) {}
|
||||
public static function session(): void {
|
||||
if(session_status()===PHP_SESSION_ACTIVE)return;
|
||||
$directory=self::sessionDirectory();
|
||||
if(ini_get('session.save_handler')!=='files'||session_save_path($directory)===false)throw new Problem('Файловое хранилище сессий недоступно.',503);
|
||||
ini_set('session.use_strict_mode','1');ini_set('session.use_only_cookies','1');
|
||||
ini_set('session.gc_maxlifetime','3600');ini_set('session.gc_probability','1');ini_set('session.gc_divisor','100');
|
||||
session_name('preland_admin');
|
||||
session_set_cookie_params(['lifetime'=>0,'path'=>'/','secure'=>getenv('ADMIN_COOKIE_SECURE')!=='0','httponly'=>true,'samesite'=>'Strict']);
|
||||
session_start();
|
||||
if(!@session_start())throw new Problem('Не удалось открыть приватное хранилище сессий.',503);
|
||||
}
|
||||
/** Готовит приватный backing до Store: не создаёт данные в небезопасном state. */
|
||||
private static function sessionDirectory(): string {
|
||||
$state=rtrim(getenv('ADMIN_STATE_DIR')?:'/var/lib/preland-admin','/');
|
||||
if($state===''||!str_starts_with($state,'/')||str_contains($state,'\\')||preg_match('/[\x00-\x1f]/',$state))throw new Problem('Для сессий требуется абсолютный приватный путь состояния.',503);
|
||||
$parts=explode('/',substr($state,1));
|
||||
foreach($parts as $part)if($part===''||$part==='.'||$part==='..')throw new Problem('Некорректный путь приватного состояния сессий.',503);
|
||||
$publicRoots=[dirname(__DIR__).'/public',$_SERVER['DOCUMENT_ROOT']??''];
|
||||
foreach(explode(',',getenv('ADMIN_LOCAL_ROOTS')?:'') as $root)$publicRoots[]=trim($root);
|
||||
foreach($publicRoots as $root){$root=rtrim($root,'/');if($root!==''&&($state===$root||str_starts_with($state,$root.'/')))throw new Problem('Сессии нельзя хранить в публичном каталоге.',503);}
|
||||
$current='';
|
||||
foreach([...$parts,'sessions'] as $part){
|
||||
$current.='/'.$part;clearstatcache(true,$current);
|
||||
if(is_link($current))throw new Problem('Символические ссылки в пути сессий запрещены.',503);
|
||||
if(!is_dir($current)&&!@mkdir($current,0700)&&!is_dir($current))throw new Problem('Не удалось подготовить приватное хранилище сессий.',503);
|
||||
clearstatcache(true,$current);
|
||||
if(is_link($current)||realpath($current)!==$current)throw new Problem('Небезопасный путь хранилища сессий.',503);
|
||||
}
|
||||
if(!@chmod($current,0700)||!is_readable($current)||!is_writable($current))throw new Problem('Приватное хранилище сессий недоступно.',503);
|
||||
return $current;
|
||||
}
|
||||
public function login(string $username,string $password,string $ip): array {
|
||||
$key=hash('sha256',$ip);$this->store->locked('login',function() use($username,$password,$key){
|
||||
|
||||
Reference in new issue
Block a user