TwoFactor.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Two authentication factor handling
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\UserPreferences;
  10. /**
  11. * Two factor authentication wrapper class
  12. */
  13. class TwoFactor
  14. {
  15. /**
  16. * @var string
  17. */
  18. public $user;
  19. /**
  20. * @var array
  21. */
  22. public $config;
  23. /**
  24. * @var boolean
  25. */
  26. protected $_writable;
  27. /**
  28. * @var PhpMyAdmin\Plugins\TwoFactorPlugin
  29. */
  30. protected $_backend;
  31. /**
  32. * @var array
  33. */
  34. protected $_available;
  35. /**
  36. * @var UserPreferences
  37. */
  38. private $userPreferences;
  39. /**
  40. * Creates new TwoFactor object
  41. *
  42. * @param string $user User name
  43. */
  44. public function __construct($user)
  45. {
  46. $this->userPreferences = new UserPreferences();
  47. $this->user = $user;
  48. $this->_available = $this->getAvailable();
  49. $this->config = $this->readConfig();
  50. $this->_writable = ($this->config['type'] == 'db');
  51. $this->_backend = $this->getBackend();
  52. }
  53. /**
  54. * Reads the configuration
  55. *
  56. * @return array
  57. */
  58. public function readConfig()
  59. {
  60. $result = [];
  61. $config = $this->userPreferences->load();
  62. if (isset($config['config_data']['2fa'])) {
  63. $result = $config['config_data']['2fa'];
  64. }
  65. $result['type'] = $config['type'];
  66. if (! isset($result['backend'])) {
  67. $result['backend'] = '';
  68. }
  69. if (! isset($result['settings'])) {
  70. $result['settings'] = [];
  71. }
  72. return $result;
  73. }
  74. /**
  75. * Get any property of this class
  76. *
  77. * @param string $property name of the property
  78. *
  79. * @return mixed|void if property exist, value of the relevant property
  80. */
  81. public function __get($property)
  82. {
  83. switch ($property) {
  84. case 'backend':
  85. return $this->_backend;
  86. case 'available':
  87. return $this->_available;
  88. case 'writable':
  89. return $this->_writable;
  90. case 'showSubmit':
  91. $backend = $this->_backend;
  92. return $backend::$showSubmit;
  93. }
  94. }
  95. /**
  96. * Returns list of available backends
  97. *
  98. * @return array
  99. */
  100. public function getAvailable()
  101. {
  102. $result = [];
  103. if ($GLOBALS['cfg']['DBG']['simple2fa']) {
  104. $result[] = 'simple';
  105. }
  106. if (class_exists('PragmaRX\Google2FA\Google2FA') && class_exists('BaconQrCode\Renderer\Image\Png')) {
  107. $result[] = 'application';
  108. }
  109. if (class_exists('Samyoul\U2F\U2FServer\U2FServer')) {
  110. $result[] = 'key';
  111. }
  112. return $result;
  113. }
  114. /**
  115. * Returns list of missing dependencies
  116. *
  117. * @return array
  118. */
  119. public function getMissingDeps()
  120. {
  121. $result = [];
  122. if (!class_exists('PragmaRX\Google2FA\Google2FA')) {
  123. $result[] = [
  124. 'class' => \PhpMyAdmin\Plugins\TwoFactor\Application::getName(),
  125. 'dep' => 'pragmarx/google2fa',
  126. ];
  127. }
  128. if (!class_exists('BaconQrCode\Renderer\Image\Png')) {
  129. $result[] = [
  130. 'class' => \PhpMyAdmin\Plugins\TwoFactor\Application::getName(),
  131. 'dep' => 'bacon/bacon-qr-code',
  132. ];
  133. }
  134. if (!class_exists('Samyoul\U2F\U2FServer\U2FServer')) {
  135. $result[] = [
  136. 'class' => \PhpMyAdmin\Plugins\TwoFactor\Key::getName(),
  137. 'dep' => 'samyoul/u2f-php-server',
  138. ];
  139. }
  140. return $result;
  141. }
  142. /**
  143. * Returns class name for given name
  144. *
  145. * @param string $name Backend name
  146. *
  147. * @return string
  148. */
  149. public function getBackendClass($name)
  150. {
  151. $result = 'PhpMyAdmin\\Plugins\\TwoFactorPlugin';
  152. if (in_array($name, $this->_available)) {
  153. $result = 'PhpMyAdmin\\Plugins\\TwoFactor\\' . ucfirst($name);
  154. } elseif (! empty($name)) {
  155. $result = 'PhpMyAdmin\\Plugins\\TwoFactor\\Invalid';
  156. }
  157. return $result;
  158. }
  159. /**
  160. * Returns backend for current user
  161. *
  162. * @return PhpMyAdmin\Plugins\TwoFactorPlugin
  163. */
  164. public function getBackend()
  165. {
  166. $name = $this->getBackendClass($this->config['backend']);
  167. return new $name($this);
  168. }
  169. /**
  170. * Checks authentication, returns true on success
  171. *
  172. * @param boolean $skip_session Skip session cache
  173. *
  174. * @return boolean
  175. */
  176. public function check($skip_session = false)
  177. {
  178. if ($skip_session) {
  179. return $this->_backend->check();
  180. }
  181. if (empty($_SESSION['two_factor_check'])) {
  182. $_SESSION['two_factor_check'] = $this->_backend->check();
  183. }
  184. return $_SESSION['two_factor_check'];
  185. }
  186. /**
  187. * Renders user interface to enter two-factor authentication
  188. *
  189. * @return string HTML code
  190. */
  191. public function render()
  192. {
  193. return $this->_backend->getError() . $this->_backend->render();
  194. }
  195. /**
  196. * Renders user interface to configure two-factor authentication
  197. *
  198. * @return string HTML code
  199. */
  200. public function setup()
  201. {
  202. return $this->_backend->getError() . $this->_backend->setup();
  203. }
  204. /**
  205. * Saves current configuration.
  206. *
  207. * @return true|PhpMyAdmin\Message
  208. */
  209. public function save()
  210. {
  211. return $this->userPreferences->persistOption('2fa', $this->config, null);
  212. }
  213. /**
  214. * Changes two-factor authentication settings
  215. *
  216. * The object might stay in partialy changed setup
  217. * if configuration fails.
  218. *
  219. * @param string $name Backend name
  220. *
  221. * @return boolean
  222. */
  223. public function configure($name)
  224. {
  225. $this->config = [
  226. 'backend' => $name
  227. ];
  228. if ($name === '') {
  229. $cls = $this->getBackendClass($name);
  230. $this->config['settings'] = [];
  231. $this->_backend = new $cls($this);
  232. } else {
  233. if (! in_array($name, $this->_available)) {
  234. return false;
  235. }
  236. $cls = $this->getBackendClass($name);
  237. $this->config['settings'] = [];
  238. $this->_backend = new $cls($this);
  239. if (! $this->_backend->configure()) {
  240. return false;
  241. }
  242. }
  243. $result = $this->save();
  244. if ($result !== true) {
  245. $result->display();
  246. }
  247. return true;
  248. }
  249. /**
  250. * Returns array with all available backends
  251. *
  252. * @return array
  253. */
  254. public function getAllBackends()
  255. {
  256. $all = array_merge([''], $this->available);
  257. $backends = [];
  258. foreach ($all as $name) {
  259. $cls = $this->getBackendClass($name);
  260. $backends[] = [
  261. 'id' => $cls::$id,
  262. 'name' => $cls::getName(),
  263. 'description' => $cls::getDescription(),
  264. ];
  265. }
  266. return $backends;
  267. }
  268. }