1fca58076SAndreas Gohr<?php 2fca58076SAndreas Gohr 3fca58076SAndreas Gohrnamespace dokuwiki\plugin\twofactor; 4fca58076SAndreas Gohr 5a386a536SAndreas Gohruse dokuwiki\Extension\Plugin; 6a386a536SAndreas Gohr 7fca58076SAndreas Gohr/** 8fca58076SAndreas Gohr * Manages the child plugins etc. 9fca58076SAndreas Gohr */ 10a386a536SAndreas Gohrclass Manager extends Plugin 118b7620a8SAndreas Gohr{ 128b7620a8SAndreas Gohr /** @var Manager */ 138b7620a8SAndreas Gohr protected static $instance; 148b7620a8SAndreas Gohr 158b7620a8SAndreas Gohr /** @var bool */ 168b7620a8SAndreas Gohr protected $ready = false; 178b7620a8SAndreas Gohr 188b7620a8SAndreas Gohr /** @var string[] */ 198b7620a8SAndreas Gohr protected $classes = []; 208b7620a8SAndreas Gohr 218b7620a8SAndreas Gohr /** @var Provider[] */ 228b7620a8SAndreas Gohr protected $providers; 23fca58076SAndreas Gohr 24fca58076SAndreas Gohr /** 258b7620a8SAndreas Gohr * Constructor 26fca58076SAndreas Gohr */ 278b7620a8SAndreas Gohr protected function __construct() 288b7620a8SAndreas Gohr { 298b7620a8SAndreas Gohr $this->classes = $this->getProviderClasses(); 308b7620a8SAndreas Gohr 318b7620a8SAndreas Gohr $attribute = plugin_load('helper', 'attribute'); 328b7620a8SAndreas Gohr if ($attribute === null) { 338b7620a8SAndreas Gohr msg('The attribute plugin is not available, 2fa disabled', -1); 348b7620a8SAndreas Gohr } 358b7620a8SAndreas Gohr 368b7620a8SAndreas Gohr if (!count($this->classes)) { 378b7620a8SAndreas Gohr msg('No suitable 2fa providers found, 2fa disabled', -1); 388b7620a8SAndreas Gohr return; 398b7620a8SAndreas Gohr } 408b7620a8SAndreas Gohr 418b7620a8SAndreas Gohr $this->ready = true; 428b7620a8SAndreas Gohr } 438b7620a8SAndreas Gohr 448b7620a8SAndreas Gohr /** 458b7620a8SAndreas Gohr * Get the instance of this singleton 468b7620a8SAndreas Gohr * 478b7620a8SAndreas Gohr * @return Manager 488b7620a8SAndreas Gohr */ 498b7620a8SAndreas Gohr public static function getInstance() 508b7620a8SAndreas Gohr { 518b7620a8SAndreas Gohr if (self::$instance === null) { 528b7620a8SAndreas Gohr self::$instance = new Manager(); 538b7620a8SAndreas Gohr } 548b7620a8SAndreas Gohr return self::$instance; 558b7620a8SAndreas Gohr } 568b7620a8SAndreas Gohr 578b7620a8SAndreas Gohr /** 588b7620a8SAndreas Gohr * Is the plugin ready to be used? 598b7620a8SAndreas Gohr * 608b7620a8SAndreas Gohr * @return bool 618b7620a8SAndreas Gohr */ 628b7620a8SAndreas Gohr public function isReady() 638b7620a8SAndreas Gohr { 648b7620a8SAndreas Gohr return $this->ready; 658b7620a8SAndreas Gohr } 668b7620a8SAndreas Gohr 678b7620a8SAndreas Gohr /** 68*b6119621SAndreas Gohr * Is a 2fa login required? 69*b6119621SAndreas Gohr * 70*b6119621SAndreas Gohr * @return bool 71a386a536SAndreas Gohr */ 72a386a536SAndreas Gohr public function isRequired() 73a386a536SAndreas Gohr { 74a386a536SAndreas Gohr $set = $this->getConf('optinout'); 75a386a536SAndreas Gohr if ($set === 'mandatory') { 76a386a536SAndreas Gohr return true; 77a386a536SAndreas Gohr } 78a386a536SAndreas Gohr // FIXME handle other options: 79a386a536SAndreas Gohr // when optout, return true unless user opted out 80a386a536SAndreas Gohr 81a386a536SAndreas Gohr return false; 82a386a536SAndreas Gohr } 83a386a536SAndreas Gohr 84a386a536SAndreas Gohr /** 85a386a536SAndreas Gohr * Convenience method to get current user 86a386a536SAndreas Gohr * 87a386a536SAndreas Gohr * @return string 88a386a536SAndreas Gohr */ 89a386a536SAndreas Gohr public function getUser() 90a386a536SAndreas Gohr { 91a386a536SAndreas Gohr global $INPUT; 92*b6119621SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 93*b6119621SAndreas Gohr if (!$user) { 94*b6119621SAndreas Gohr throw new \RuntimeException('2fa user specifics used before user available'); 95*b6119621SAndreas Gohr } 96*b6119621SAndreas Gohr return $user; 97a386a536SAndreas Gohr } 98a386a536SAndreas Gohr 99a386a536SAndreas Gohr /** 1008b7620a8SAndreas Gohr * Get all available providers 1018b7620a8SAndreas Gohr * 1028b7620a8SAndreas Gohr * @return Provider[] 1038b7620a8SAndreas Gohr */ 1048b7620a8SAndreas Gohr public function getAllProviders() 1058b7620a8SAndreas Gohr { 106a386a536SAndreas Gohr $user = $this->getUser(); 1078b7620a8SAndreas Gohr 1088b7620a8SAndreas Gohr if ($this->providers === null) { 1098b7620a8SAndreas Gohr $this->providers = []; 110a386a536SAndreas Gohr foreach ($this->classes as $plugin => $class) { 111a386a536SAndreas Gohr $this->providers[$plugin] = new $class($user); 1128b7620a8SAndreas Gohr } 1138b7620a8SAndreas Gohr } 1148b7620a8SAndreas Gohr 1158b7620a8SAndreas Gohr return $this->providers; 1168b7620a8SAndreas Gohr } 1178b7620a8SAndreas Gohr 1188b7620a8SAndreas Gohr /** 119a386a536SAndreas Gohr * Get all providers that have been already set up by the user 120a386a536SAndreas Gohr * 121a386a536SAndreas Gohr * @return Provider[] 122a386a536SAndreas Gohr */ 123a386a536SAndreas Gohr public function getUserProviders() 124a386a536SAndreas Gohr { 125a386a536SAndreas Gohr $list = $this->getAllProviders(); 126a386a536SAndreas Gohr $list = array_filter($list, function ($provider) { 127a386a536SAndreas Gohr return $provider->isConfigured(); 128a386a536SAndreas Gohr }); 129a386a536SAndreas Gohr 130a386a536SAndreas Gohr return $list; 131a386a536SAndreas Gohr } 132a386a536SAndreas Gohr 133a386a536SAndreas Gohr /** 134a386a536SAndreas Gohr * Get the instance of the given provider 135a386a536SAndreas Gohr * 136a386a536SAndreas Gohr * @param string $providerID 137a386a536SAndreas Gohr * @return Provider 138a386a536SAndreas Gohr */ 139a386a536SAndreas Gohr public function getUserProvider($providerID) 140a386a536SAndreas Gohr { 141a386a536SAndreas Gohr $providers = $this->getUserProviders(); 142a386a536SAndreas Gohr if (isset($providers[$providerID])) return $providers[$providerID]; 143a386a536SAndreas Gohr throw new \RuntimeException('Uncofigured provider requested'); 144a386a536SAndreas Gohr } 145a386a536SAndreas Gohr 146a386a536SAndreas Gohr /** 147*b6119621SAndreas Gohr * Get the user's default provider if any 148*b6119621SAndreas Gohr * 149*b6119621SAndreas Gohr * Autoupdates the apropriate setting 150*b6119621SAndreas Gohr * 151*b6119621SAndreas Gohr * @return Provider|null 152*b6119621SAndreas Gohr */ 153*b6119621SAndreas Gohr public function getUserDefaultProvider() 154*b6119621SAndreas Gohr { 155*b6119621SAndreas Gohr $setting = new Settings('twofactor', $this->getUser()); 156*b6119621SAndreas Gohr $default = $setting->get('defaultmod'); 157*b6119621SAndreas Gohr $providers = $this->getUserProviders(); 158*b6119621SAndreas Gohr 159*b6119621SAndreas Gohr if (isset($providers[$default])) return $providers[$default]; 160*b6119621SAndreas Gohr // still here? no valid setting. Use first available one 161*b6119621SAndreas Gohr $first = array_shift($providers); 162*b6119621SAndreas Gohr if ($first !== null) { 163*b6119621SAndreas Gohr $this->setUserDefaultProvider($first); 164*b6119621SAndreas Gohr } 165*b6119621SAndreas Gohr return $first; 166*b6119621SAndreas Gohr } 167*b6119621SAndreas Gohr 168*b6119621SAndreas Gohr /** 169*b6119621SAndreas Gohr * Set the default provider for the user 170*b6119621SAndreas Gohr * 171*b6119621SAndreas Gohr * @param Provider $provider 172*b6119621SAndreas Gohr * @return void 173*b6119621SAndreas Gohr */ 174*b6119621SAndreas Gohr public function setUserDefaultProvider($provider) 175*b6119621SAndreas Gohr { 176*b6119621SAndreas Gohr $setting = new Settings('twofactor', $this->getUser()); 177*b6119621SAndreas Gohr $setting->set('defaultmod', $provider->getProviderID()); 178*b6119621SAndreas Gohr } 179*b6119621SAndreas Gohr 180*b6119621SAndreas Gohr /** 1818b7620a8SAndreas Gohr * Find all available provider classes 1828b7620a8SAndreas Gohr * 1838b7620a8SAndreas Gohr * @return string[]; 1848b7620a8SAndreas Gohr */ 1858b7620a8SAndreas Gohr protected function getProviderClasses() 1868b7620a8SAndreas Gohr { 1878b7620a8SAndreas Gohr // FIXME this relies on naming alone, we might want to use an action for registering 1888b7620a8SAndreas Gohr $plugins = plugin_list('helper'); 1898b7620a8SAndreas Gohr $plugins = array_filter($plugins, function ($plugin) { 1908b7620a8SAndreas Gohr return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor'; 1918b7620a8SAndreas Gohr }); 1928b7620a8SAndreas Gohr 1938b7620a8SAndreas Gohr $classes = []; 1948b7620a8SAndreas Gohr foreach ($plugins as $plugin) { 1958b7620a8SAndreas Gohr $class = 'helper_plugin_' . $plugin; 1968b7620a8SAndreas Gohr if (is_a($class, Provider::class, true)) { 197a386a536SAndreas Gohr $classes[$plugin] = $class; 1988b7620a8SAndreas Gohr } 1998b7620a8SAndreas Gohr } 2008b7620a8SAndreas Gohr 2018b7620a8SAndreas Gohr return $classes; 202fca58076SAndreas Gohr } 203fca58076SAndreas Gohr 204fca58076SAndreas Gohr} 205