2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * Two authentication factor handling
8 declare(strict_types
=1);
12 use PhpMyAdmin\Message
;
13 use PhpMyAdmin\Plugins\TwoFactor\Application
;
14 use PhpMyAdmin\Plugins\TwoFactor\Invalid
;
15 use PhpMyAdmin\Plugins\TwoFactor\Key
;
16 use PhpMyAdmin\Plugins\TwoFactorPlugin
;
17 use PhpMyAdmin\UserPreferences
;
18 use PragmaRX\Google2FAQRCode\Google2FA
;
19 use Samyoul\U2F\U2FServer\U2FServer
;
22 * Two factor authentication wrapper class
44 * @var TwoFactorPlugin
51 protected $_available;
54 * @var UserPreferences
56 private $userPreferences;
59 * Creates new TwoFactor object
61 * @param string $user User name
63 public function __construct($user)
65 $this->userPreferences
= new UserPreferences();
67 $this->_available
= $this->getAvailable();
68 $this->config
= $this->readConfig();
69 $this->_writable
= ($this->config
['type'] == 'db');
70 $this->_backend
= $this->getBackend();
74 * Reads the configuration
78 public function readConfig()
81 $config = $this->userPreferences
->load();
82 if (isset($config['config_data']['2fa'])) {
83 $result = $config['config_data']['2fa'];
85 $result['type'] = $config['type'];
86 if (! isset($result['backend'])) {
87 $result['backend'] = '';
89 if (! isset($result['settings'])) {
90 $result['settings'] = [];
96 * Get any property of this class
98 * @param string $property name of the property
100 * @return mixed|void if property exist, value of the relevant property
102 public function __get($property)
106 return $this->_backend
;
108 return $this->_available
;
110 return $this->_writable
;
112 $backend = $this->_backend
;
113 return $backend::$showSubmit;
118 * Returns list of available backends
122 public function getAvailable()
125 if ($GLOBALS['cfg']['DBG']['simple2fa']) {
126 $result[] = 'simple';
128 if (class_exists(Google2FA
::class)) {
129 $result[] = 'application';
131 if (class_exists(U2FServer
::class)) {
138 * Returns list of missing dependencies
142 public function getMissingDeps()
145 if (! class_exists(Google2FA
::class)) {
147 'class' => Application
::getName(),
148 'dep' => 'pragmarx/google2fa-qrcode',
151 if (! class_exists('BaconQrCode\Renderer\Image\Png')) {
153 'class' => Application
::getName(),
154 'dep' => 'bacon/bacon-qr-code',
157 if (! class_exists(U2FServer
::class)) {
159 'class' => Key
::getName(),
160 'dep' => 'samyoul/u2f-php-server',
167 * Returns class name for given name
169 * @param string $name Backend name
173 public function getBackendClass($name)
175 $result = TwoFactorPlugin
::class;
176 if (in_array($name, $this->_available
)) {
177 $result = 'PhpMyAdmin\\Plugins\\TwoFactor\\' . ucfirst($name);
178 } elseif (! empty($name)) {
179 $result = Invalid
::class;
185 * Returns backend for current user
187 * @return TwoFactorPlugin
189 public function getBackend()
191 $name = $this->getBackendClass($this->config
['backend']);
192 return new $name($this);
196 * Checks authentication, returns true on success
198 * @param boolean $skip_session Skip session cache
202 public function check($skip_session = false)
205 return $this->_backend
->check();
207 if (empty($_SESSION['two_factor_check'])) {
208 $_SESSION['two_factor_check'] = $this->_backend
->check();
210 return $_SESSION['two_factor_check'];
214 * Renders user interface to enter two-factor authentication
216 * @return string HTML code
218 public function render()
220 return $this->_backend
->getError() . $this->_backend
->render();
224 * Renders user interface to configure two-factor authentication
226 * @return string HTML code
228 public function setup()
230 return $this->_backend
->getError() . $this->_backend
->setup();
234 * Saves current configuration.
236 * @return true|Message
238 public function save()
240 return $this->userPreferences
->persistOption('2fa', $this->config
, null);
244 * Changes two-factor authentication settings
246 * The object might stay in partialy changed setup
247 * if configuration fails.
249 * @param string $name Backend name
253 public function configure($name)
259 $cls = $this->getBackendClass($name);
260 $this->config
['settings'] = [];
261 $this->_backend
= new $cls($this);
263 if (! in_array($name, $this->_available
)) {
266 $cls = $this->getBackendClass($name);
267 $this->config
['settings'] = [];
268 $this->_backend
= new $cls($this);
269 if (! $this->_backend
->configure()) {
273 $result = $this->save();
274 if ($result !== true) {
281 * Returns array with all available backends
285 public function getAllBackends()
287 $all = array_merge([''], $this->available
);
289 foreach ($all as $name) {
290 $cls = $this->getBackendClass($name);
293 'name' => $cls::getName(),
294 'description' => $cls::getDescription(),