secretMapper->getSecret($user); return (int)$secret->getState() === ITotp::STATE_ENABLED; } catch (DoesNotExistException $ex) { return false; } } private function generateSecret(): string { return $this->random->generate(32, ISecureRandom::CHAR_UPPER . '234567'); } /** * @param IUser $user */ public function createSecret(IUser $user): string { try { // Delete existing one $oldSecret = $this->secretMapper->getSecret($user); $this->secretMapper->delete($oldSecret); } catch (DoesNotExistException $ex) { // Ignore } // Create new one $secret = $this->generateSecret(); $dbSecret = new TotpSecret(); $dbSecret->setUserId($user->getUID()); $dbSecret->setSecret($this->crypto->encrypt($secret)); $dbSecret->setState(ITotp::STATE_CREATED); $this->secretMapper->insert($dbSecret); return $secret; } public function getSecret(IUser $user): TotpSecret { try { return $this->secretMapper->getSecret($user); } catch (DoesNotExistException $e) { throw new NoTotpSecretFoundException( $e->getMessage(), $e->getCode(), $e, ); } } public function enable(IUser $user, $key): bool { $dbSecret = $this->secretMapper->getSecret($user); if (!$this->validateSecret($dbSecret, $key)) { return false; } $dbSecret->setState(ITotp::STATE_ENABLED); $this->secretMapper->update($dbSecret); $this->eventDispatcher->dispatch(StateChanged::class, new StateChanged($user, true)); return true; } public function deleteSecret(IUser $user, bool $byAdmin = false): void { try { // TODO: execute DELETE sql in mapper instead $dbSecret = $this->secretMapper->getSecret($user); $this->secretMapper->delete($dbSecret); } catch (DoesNotExistException $ex) { // Ignore } if ($byAdmin) { $this->eventDispatcher->dispatch(DisabledByAdmin::class, new DisabledByAdmin($user)); } else { $this->eventDispatcher->dispatch(StateChanged::class, new StateChanged($user, false)); } } public function validateSecret(TotpSecret $secret, string $key): bool { $decryptedSecret = $this->crypto->decrypt($secret->getSecret()); $otp = Factory::getTOTP(Base32::decode($decryptedSecret), 30, 6); $counter = null; $lastCounter = $secret->getLastCounter(); if ($lastCounter !== -1) { $counter = $lastCounter; } $result = $otp->verify($key, 3, $counter); if ($result instanceof TOTPValidResultInterface) { $secret->setLastCounter($result->getCounter()); $this->secretMapper->update($secret); return true; } return false; } }