Jak se přihlásit se jako user?

MW
Člen | 615
+
0
-

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

Petr Parolek
Člen | 455
+
-1
-

Ahoj,

tady je příklad implementace https://github.com/…nticator.php#…

David Matějka
Moderator | 6445
+
+4
-

do $user->login() vůbec nemusíš předávat uživatelské údaje, ale můžeš tam rovnou předat vytvořenou identitu

MW
Člen | 615
+
0
-

David Matějka napsal(a):

do $user->login() vůbec nemusíš předávat uživatelské údaje, ale můžeš tam rovnou předat vytvořenou identitu

Myslíš něco jako

$user->login(new Identity($user->id));
redirect....

Nebo jak zkonstruovat identitu daného uživatele s danou rolí a daty?

Díky moc

David Matějka
Moderator | 6445
+
+1
-

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 | 615
+
0
-

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 | 762
+
+2
-

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
+
-1
-

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 | 615
+
0
-

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

David Matějka
Moderator | 6445
+
0
-

@MW jak vytváříš identitu v současném authenticatoru?

MW
Člen | 615
+
+2
-

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
+
-3
-

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)

Petr Parolek
Člen | 455
+
-3
-

Zbytečné vymýšlet kolo, které již je vynalezeno.

David Matějka
Moderator | 6445
+
+1
-

@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
+
0
-

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 | 615
+
0
-

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!