Jak se přihlásit se jako user?
- MW
- Člen | 626
Zdravím,
jak byste prosím řešili „přihlásit se jako user“ ?
Řekněme, že jsem přihlášený jako admin, v administraci bych si vybral uživatele a přihlásil se jako on. Jde mi především o to, že při přihlašovaní načítám do $user->data (SimpleIdentity) dva parametry z databáze, tedy by je asi stačilo přepsat. Ale pokud neznám heslo usera, tak volat Authenticate nemohu a přepsat $data také nelze. Nebo nějak lze?
Díky moc
- David Matějka
- Moderator | 6445
do $user->login()
vůbec nemusíš předávat uživatelské
údaje, ale můžeš tam rovnou předat vytvořenou identitu
- David Matějka
- Moderator | 6445
Tu identitu vytvoříš stejným způsobem, jako ji vytváříš v authenticatoru. Takže ideálně přesuneš logiku vytváření identity do nějaké factory. A tu pak použiješ jak v klasickém authenticatoru, tak v službě, která admina přihlásí jako jiného uživatele.
- MW
- Člen | 626
Authenticator vyžaduje heslo. Pokud nezahasované heslo neznám, musím vše skládat z username, heslo, role + další data.
Prosím, neměl by jsi příklad, jak ten authenticator nějak použít jen s username? Lze to? Nerad bych přepisoval již vymyšlené věci. Třeba jen něco nechápu :)
Díky moc!
- Kamil Valenta
- Člen | 822
Proč do toho stále vnášíš ten autentikátor? Jde Tě jen o vrácení
Identity. Tu může, ale také nemusí, vrátit Autentikátor. Zrovna tak ji
může vrátit libovolná jiná servica.
Pokud se tedy uživatel legitimně přihlásí, necháš si vrátit Identitu od
autentikátoru na základě loginu a hesla.
Pokud uživatel jen převezme něčí roli, necháš si vrátit Identitu od
vlastní service na základě vlastních kritérií, patrně jen id
uživatele…
--
EDIT: nesouvisí s dotazem, ale jen nezapomenout, že je-li v aplikaci
nějaké logování, tak taková funkcionalita je dost znevěrohodní…
Editoval Kamil Valenta (4. 5. 2021 22:27)
- galab
- Backer | 74
Něco takového by mohlo fungovat..
class CustomerAuthenticator
{
private $storage;
private const TABLE_NAME = 'customer', COLUMN_ID = 'id', COLUMN_ROLE = 'role', COLUMN_PASSWORD_HASH = 'pass';
public function __construct(\Nette\Security\IUserStorage $storage)
{
$this->storage = $storage;
}
public function loginAsCustomer($userId)
{
$row = $this->db->table(self::TABLE_NAME)
->where(self::COLUMN_ID, $userId)
->fetch();
$arr = $row->toArray();
unset($arr[self::COLUMN_PASSWORD_HASH]);
$identity = new \Nette\Security\Identity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
$this->storage->setIdentity($identity);
$this->storage->setAuthenticated(true);
}
}
- MW
- Člen | 626
Kamil Valenta napsal(a):
Proč do toho stále vnášíš ten autentikátor? Jde Tě jen o vrácení Identity. Tu může, ale také nemusí, vrátit Autentikátor. Zrovna tak ji může vrátit libovolná jiná servica.
Pokud se tedy uživatel legitimně přihlásí, necháš si vrátit Identitu od autentikátoru na základě loginu a hesla.
Pokud uživatel jen převezme něčí roli, necháš si vrátit Identitu od vlastní service na základě vlastních kritérií, patrně jen id uživatele…--
EDIT: nesouvisí s dotazem, ale jen nezapomenout, že je-li v aplikaci nějaké logování, tak taková funkcionalita je dost znevěrohodní…
Authenticator do toho vnáším na základě předcházející rady. To, co píšeš zní hezky, ale mohu poprosit o příklad, jak to tedy udělat? Takto to zní velmi lehce.
Díky
- MW
- Člen | 626
Tak prosím o kontrolu :)
public function handleLoginAs($username) {
$ident = $this->userManager->adminAuthenticate($username);
$this->getUser()->login($ident);
$this->redirect('Users:');
}
a
public function adminAuthenticate(string $username): Nette\Security\SimpleIdentity {
$row = $this->database->table(self::TABLE_NAME)
->where(self::COLUMN_NAME, $username)
->fetch();
$arr = $row->toArray();
return new Nette\Security\SimpleIdentity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
}
Je to tak v pořádku ?
Díky moc
- Petr Parolek
- Člen | 455
Petr Parolek napsal(a):
Ahoj,
tady je příklad implementace https://github.com/…nticator.php#…
Použil bych prověřený kod, co jsem posílal:
public function updateRoles(NS\User $user, ?Role $testedRole = null): void
{
$dbuser = $this->userRepository->findById($user->id);
$netteRoles = [];
if (! $testedRole) {
if ($dbuser->isApproved()) {
foreach ($dbuser->getRoles() as $role) {
$netteRoles[$role->getId()] = $role->getName();
}
} else {
$roleUnapproved = $this->roleRepository->findBySystemName(Role::UNAPPROVED);
$netteRoles[$roleUnapproved->getId()] = $roleUnapproved->getName();
}
} else {
$netteRoles[0] = Role::TEST;
$netteRoles[$testedRole->getId()] = $testedRole->getName();
}
$identity = $user->identity;
assert($identity instanceof Identity);
$identity->setRoles($netteRoles);
}
Editoval Petr Parolek (6. 5. 2021 11:00)
- David Matějka
- Moderator | 6445
@MW jj, takhle to vypadá dobře. jen předpokladám, že ta konstrukce identity
$arr = $row->toArray();
return new Nette\Security\SimpleIdentity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
bude stejná i v klasickém authetnicate, takže bude nejlepší to extrahovat do jedné metody, kterou budeš volat z obou míst, takže se ti nestane, že se ty implementace rozjedou.
@PetrParolek to řeší jen změnu rolí, nikoliv celé identity. Navíc tam jsou nějaký specifika pro konkrétní role atd.
- Polki
- Člen | 553
Petr Parolek napsal(a):
Zbytečné vymýšlet kolo, které již je vynalezeno.
To je pravda, ale myslím, že tvoje komentáře jsou úplně mimo téma.
Pokud jsem to dobře pochopil, tak @MW chce udělat něco, jako má například seznam, kdy máš více E-mailů, tak aby si se nemusel pokaždé odhlašovat a zase přihlašovat, tak si nastavíš náhled na ostatní E-maily, takže mezi jednotlivými přihlášeními přepínáš pomocí selectu v aplikaci a chová se to, jako kdyby jsi byl přihlášen do jiného E-mailu. A to nemá co dělat s rolemi, které doporučuješ měnit ty.
Já toto řeším tak, že si do session uložím id originálně přihlášeného uživatele. Například, pokud jsem se přihlásil za administrátora, který má ID rovno číslu 9, tak si do session uložím číslo 9.
$this->sessionSection->originalUserId = $id; // $id = 9
Pak v identitě mám uložená data aktuálně přihlášeného uživatele. Tedy při přepnutí uživatele na jiného například s ID 5 a rolí ‚user‘ pomocí selectu se přenačte identita:
$this->user->login(new Nette\Security\Identity($newUserId, $newUserRoles, $newUserData)); // $newUserId = 5, $newUserRoles = 'user', $newUserData = []
No a aplikace se nadále chová tak, jako kdybych byl přihlášen za nového
uživatele.
Jediné, co se tak nechová je select na přepnutí uživatelů, který se
chová podle starého uživatele (toho uloženého v session) tedy pokud se ID
přihlášeného a ID originálního liší, tak vím, že jsem přepnutý.
Pokud jsou stejné, tak vím, že nejsem přepnutý.
Samozřejmě je potřeba ještě zjišťovat, jestli má aktuálně přihlášený uživatel vidět select na přepnutí uživatelů a jestli ano, tak jaké uživatele zde má vidět. To můžeme ověřit takto:
Přidání originálního uživatele do session:
$this->sessionSection->originalUserId = $id; // $id = 9
$this->sessionSection->originalUserRole = $role; // $role = 'admin'
Ověření, jestli se má zobrazit select:
public function canSwitchUser(): bool
{
return $this->permission->isAllowed( $this->sessionSection->originalUserRole, 'switchForm', 'view'); // $this->permission je instance 'Nette\Security\Permission'
}
EDIT 1:
Samozřejmě mám vše vyextrahované do tříd tak, abych dodržoval nějakou
rozumnou strukturu kódu a principy typu DRY.
Editoval Polki (6. 5. 2021 11:51)
- MW
- Člen | 626
David Matějka napsal(a):
@MW jj, takhle to vypadá dobře. jen předpokladám, že ta konstrukce identity
$arr = $row->toArray(); return new Nette\Security\SimpleIdentity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
bude stejná i v klasickém authetnicate, takže bude nejlepší to extrahovat do jedné metody, kterou budeš volat z obou míst, takže se ti nestane, že se ty implementace rozjedou.
@PetrParolek to řeší jen změnu rolí, nikoliv celé identity. Navíc tam jsou nějaký specifika pro konkrétní role atd.
jj, je to tak.
Díky moc!