sqrl-2.0.0-rc1/src/Plugin/SqrlAction/Ident.php
src/Plugin/SqrlAction/Ident.php
<?php
namespace Drupal\sqrl\Plugin\SqrlAction;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\sqrl\Entity\Identity;
use Drupal\sqrl\Exception\ClientException;
use Drupal\sqrl\SqrlActionPluginBase;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
/**
* Plugin implementation of the sqrl action "ident".
*
* @SqrlAction(
* id = "ident",
* label = @Translation("Ident"),
* description = @Translation("TBD.")
* )
*/
class Ident extends SqrlActionPluginBase {
/**
* {@inheritdoc}
*
* @throws \JsonException
*/
public function run(): bool {
return match ($this->client->getNut()->getClientOperation()) {
'register' => $this->register(),
'login' => $this->login(),
'link' => $this->setkey(),
'unlink' => $this->unsetkey(),
'profile' => ($this->client->getIdentity() !== NULL),
default => FALSE,
};
}
/**
* Performs the login action.
*
* @return bool
* TRUE, if login was successful, FALSE otherwise.
*
* @throws \Drupal\sqrl\Exception\ClientException
* @throws \JsonException
*/
private function login(): bool {
if ($this->client->getIdentity() === NULL) {
return $this->register();
}
if (!$this->client->getIdentity()->isEnabled()) {
throw new ClientException('SQRL disabled for this account');
}
$found_active_user = FALSE;
foreach ($this->client->getIdentity()->getUsers() as $user) {
if ($user->isActive()) {
$found_active_user = TRUE;
break;
}
}
if (!$found_active_user) {
return FALSE;
}
if (($this->client->getTif() & $this->client::FLAG_PIDK_MATCH) === $this->client::FLAG_PIDK_MATCH) {
return $this->setkey();
}
return TRUE;
}
/**
* Performs the register action.
*
* @return bool
* TRUE, if registration was successful, FALSE otherwise.
*
* @throws \Drupal\sqrl\Exception\ClientException
* @throws \JsonException
*/
private function register(): bool {
if ($this->client->getIdentity() !== NULL) {
return $this->login();
}
$registerMethod = $this->configFactory->get('user.settings')->get('register');
if ($registerMethod === UserInterface::REGISTER_ADMINISTRATORS_ONLY) {
// We can't create new accounts on this server.
$this->log->debug('No user registration allowed!');
return FALSE;
}
$userinfo = [
'name' => '',
'pass' => $this->random->string(32),
'init' => '',
'status' => $registerMethod === UserInterface::REGISTER_VISITORS,
'access' => $this->time->getRequestTime(),
];
$pattern = $this->configFactory->get('sqrl.settings')->get('account_name_pattern');
if (!str_contains($pattern, '[rand]')) {
$pattern .= '[rand]';
}
$length = $this->configFactory->get('sqrl.settings')->get('account_name_random_length');
$user = NULL;
while ($user === NULL) {
$name = str_replace('[rand]', $this->random->name($length, TRUE), $pattern);
if (mb_strlen($name) > UserInterface::USERNAME_MAX_LENGTH) {
$name = mb_substr($name, 0, UserInterface::USERNAME_MAX_LENGTH);
}
if (!user_load_by_name($name)) {
$userinfo['name'] = $userinfo['init'] = $name;
$userinfo['mail'] = $this->identities->dummyMail();
try {
$user = User::create($userinfo);
$user->save();
}
catch (\Exception) {
// Do nothing, just retry.
$user = NULL;
}
}
}
$this->account = $user;
if ($this->setkey()) {
if ($registerMethod === UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
// New accounts require admin approval.
$this->state->addMessage($this->sqrl->getNut()->getPublicNut(), $this->t('Account created, please wait for an administrator for approval of your account.'));
}
return TRUE;
}
return FALSE;
}
/**
* Performs the set key action.
*
* @return bool
* TRUE, if set key action was successful, FALSE otherwise.
*
* @throws \Drupal\sqrl\Exception\ClientException
*/
private function setkey(): bool {
if ($this->account === NULL && $this->client->getIdentity() === NULL) {
return FALSE;
}
if ($this->account !== NULL && $this->client->getIdentity() !== NULL && $this->identities->getIdentityByIdk($this->account->id())) {
// Nothing changed and we have nothing to do.
return TRUE;
}
if ($identity = $this->identities->getIdentityByIdk()) {
// We know we do have the account as well, because from login (where we
// don't know the account yet) we only get to setkey() when the client
// request was matching a previous key.
$identity->addUser($this->account);
}
else {
$values = [
'idk' => $this->client->getClientVar('idk'),
'suk' => $this->client->getClientVar('suk'),
'vuk' => $this->client->getClientVar('vuk'),
'status' => TRUE,
];
if ($this->account !== NULL) {
$values['user'] = $this->account;
}
$identity = Identity::create($values);
}
try {
if ($identity->save()) {
if ($previous_identity = $this->identities->getIdentityByPidk()) {
$previous_identity
->setSuccessor($identity)
->save();
// Bring forward all linked user accounts to the new identity.
$changed = FALSE;
foreach ($previous_identity->getUsers() as $user) {
if ($this->account === NULL || $user->id() !== $this->account->id()) {
$identity->addUser($user);
$changed = TRUE;
}
}
if ($changed) {
$identity->save();
}
}
return TRUE;
}
}
catch (EntityStorageException) {
}
// Saving previous id failed, undo everything.
try {
$identity->delete();
}
catch (EntityStorageException) {
}
throw new ClientException('Saving current or previous ID failed');
}
/**
* Performs the unset key action.
*
* @return bool
* TRUE, if unset key action was successful, FALSE otherwise.
*/
private function unsetkey(): bool {
if ($this->client->getIdentity() === NULL) {
return FALSE;
}
$this->client->getIdentity()->removeUser($this->account);
return TRUE;
}
}
