123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- <?php
- /**
- * Two authentication factor handling
- */
- declare(strict_types=1);
- namespace PhpMyAdmin;
- use PhpMyAdmin\Plugins\TwoFactor\Application;
- use PhpMyAdmin\Plugins\TwoFactor\Invalid;
- use PhpMyAdmin\Plugins\TwoFactor\Key;
- use PhpMyAdmin\Plugins\TwoFactorPlugin;
- use PragmaRX\Google2FAQRCode\Google2FA;
- use Samyoul\U2F\U2FServer\U2FServer;
- use function array_merge;
- use function class_exists;
- use function in_array;
- use function ucfirst;
- /**
- * Two factor authentication wrapper class
- */
- class TwoFactor
- {
- /** @var string */
- public $user;
- /** @var array */
- public $config;
- /** @var bool */
- protected $writable;
- /** @var TwoFactorPlugin */
- protected $backend;
- /** @var array */
- protected $available;
- /** @var UserPreferences */
- private $userPreferences;
- /**
- * Creates new TwoFactor object
- *
- * @param string $user User name
- */
- public function __construct($user)
- {
- global $dbi;
- $dbi->initRelationParamsCache();
- $this->userPreferences = new UserPreferences();
- $this->user = $user;
- $this->available = $this->getAvailableBackends();
- $this->config = $this->readConfig();
- $this->writable = ($this->config['type'] === 'db');
- $this->backend = $this->getBackendForCurrentUser();
- }
- /**
- * Reads the configuration
- *
- * @return array
- */
- public function readConfig()
- {
- $result = [];
- $config = $this->userPreferences->load();
- if (isset($config['config_data']['2fa'])) {
- $result = $config['config_data']['2fa'];
- }
- $result['type'] = $config['type'];
- if (! isset($result['backend'])) {
- $result['backend'] = '';
- }
- if (! isset($result['settings'])) {
- $result['settings'] = [];
- }
- return $result;
- }
- public function isWritable(): bool
- {
- return $this->writable;
- }
- public function getBackend(): TwoFactorPlugin
- {
- return $this->backend;
- }
- /**
- * @return array
- */
- public function getAvailable(): array
- {
- return $this->available;
- }
- public function showSubmit(): bool
- {
- $backend = $this->backend;
- return $backend::$showSubmit;
- }
- /**
- * Returns list of available backends
- *
- * @return array
- */
- public function getAvailableBackends()
- {
- $result = [];
- if ($GLOBALS['cfg']['DBG']['simple2fa']) {
- $result[] = 'simple';
- }
- if (class_exists(Google2FA::class)) {
- $result[] = 'application';
- }
- if (class_exists(U2FServer::class)) {
- $result[] = 'key';
- }
- return $result;
- }
- /**
- * Returns list of missing dependencies
- *
- * @return array
- */
- public function getMissingDeps()
- {
- $result = [];
- if (! class_exists(Google2FA::class)) {
- $result[] = [
- 'class' => Application::getName(),
- 'dep' => 'pragmarx/google2fa-qrcode',
- ];
- }
- if (! class_exists('BaconQrCode\Renderer\Image\Png')) {
- $result[] = [
- 'class' => Application::getName(),
- 'dep' => 'bacon/bacon-qr-code',
- ];
- }
- if (! class_exists(U2FServer::class)) {
- $result[] = [
- 'class' => Key::getName(),
- 'dep' => 'samyoul/u2f-php-server',
- ];
- }
- return $result;
- }
- /**
- * Returns class name for given name
- *
- * @param string $name Backend name
- *
- * @return string
- */
- public function getBackendClass($name)
- {
- $result = TwoFactorPlugin::class;
- if (in_array($name, $this->available)) {
- $result = 'PhpMyAdmin\\Plugins\\TwoFactor\\' . ucfirst($name);
- } elseif (! empty($name)) {
- $result = Invalid::class;
- }
- return $result;
- }
- /**
- * Returns backend for current user
- *
- * @return TwoFactorPlugin
- */
- public function getBackendForCurrentUser()
- {
- $name = $this->getBackendClass($this->config['backend']);
- return new $name($this);
- }
- /**
- * Checks authentication, returns true on success
- *
- * @param bool $skip_session Skip session cache
- *
- * @return bool
- */
- public function check($skip_session = false)
- {
- if ($skip_session) {
- return $this->backend->check();
- }
- if (empty($_SESSION['two_factor_check'])) {
- $_SESSION['two_factor_check'] = $this->backend->check();
- }
- return $_SESSION['two_factor_check'];
- }
- /**
- * Renders user interface to enter two-factor authentication
- *
- * @return string HTML code
- */
- public function render()
- {
- return $this->backend->getError() . $this->backend->render();
- }
- /**
- * Renders user interface to configure two-factor authentication
- *
- * @return string HTML code
- */
- public function setup()
- {
- return $this->backend->getError() . $this->backend->setup();
- }
- /**
- * Saves current configuration.
- *
- * @return true|Message
- */
- public function save()
- {
- return $this->userPreferences->persistOption('2fa', $this->config, null);
- }
- /**
- * Changes two-factor authentication settings
- *
- * The object might stay in partially changed setup
- * if configuration fails.
- *
- * @param string $name Backend name
- *
- * @return bool
- */
- public function configure($name)
- {
- $this->config = ['backend' => $name];
- if ($name === '') {
- $cls = $this->getBackendClass($name);
- $this->config['settings'] = [];
- $this->backend = new $cls($this);
- } else {
- if (! in_array($name, $this->available)) {
- return false;
- }
- $cls = $this->getBackendClass($name);
- $this->config['settings'] = [];
- $this->backend = new $cls($this);
- if (! $this->backend->configure()) {
- return false;
- }
- }
- $result = $this->save();
- if ($result !== true) {
- echo $result->getDisplay();
- }
- return true;
- }
- /**
- * Returns array with all available backends
- *
- * @return array
- */
- public function getAllBackends()
- {
- $all = array_merge([''], $this->available);
- $backends = [];
- foreach ($all as $name) {
- $cls = $this->getBackendClass($name);
- $backends[] = [
- 'id' => $cls::$id,
- 'name' => $cls::getName(),
- 'description' => $cls::getDescription(),
- ];
- }
- return $backends;
- }
- }
|