1<?php 2 3namespace dokuwiki\plugin\twofactor; 4 5use dokuwiki\Extension\Event; 6use dokuwiki\Extension\Plugin; 7 8/** 9 * Manages the child plugins etc. 10 */ 11class Manager extends Plugin 12{ 13 /** 14 * Generally all our actions should run before all other plugins 15 */ 16 const EVENT_PRIORITY = -5000; 17 18 /** @var Manager */ 19 protected static $instance; 20 21 /** @var bool */ 22 protected $ready = false; 23 24 /** @var Provider[] */ 25 protected $providers; 26 27 /** @var bool */ 28 protected $providersInitialized; 29 30 /** 31 * Constructor 32 */ 33 protected function __construct() 34 { 35 $attribute = plugin_load('helper', 'attribute'); 36 if ($attribute === null) { 37 msg('The attribute plugin is not available, 2fa disabled', -1); 38 return; 39 } 40 41 $this->loadProviders(); 42 if (!count($this->providers)) { 43 msg('No suitable 2fa providers found, 2fa disabled', -1); 44 return; 45 } 46 47 $this->ready = true; 48 } 49 50 /** 51 * This is not a conventional class, plugin name can't be determined automatically 52 * @inheritdoc 53 */ 54 public function getPluginName() 55 { 56 return 'twofactor'; 57 } 58 59 /** 60 * Get the instance of this singleton 61 * 62 * @return Manager 63 */ 64 public static function getInstance() 65 { 66 if (self::$instance === null) { 67 self::$instance = new Manager(); 68 } 69 return self::$instance; 70 } 71 72 /** 73 * Is the plugin ready to be used? 74 * 75 * @return bool 76 */ 77 public function isReady() 78 { 79 if (!$this->ready) return false; 80 try { 81 $this->getUser(); 82 } catch (\Exception $ignored) { 83 return false; 84 } 85 86 return true; 87 } 88 89 /** 90 * Is a 2fa login required? 91 * 92 * @return bool 93 */ 94 public function isRequired() 95 { 96 $set = $this->getConf('optinout'); 97 if ($set === 'mandatory') { 98 return true; 99 } 100 if ($set === 'optout') { 101 $setting = new Settings('twofactor', $this->getUser()); 102 if ($setting->get('state') !== 'optout') { 103 return true; 104 } 105 } 106 107 return false; 108 } 109 110 /** 111 * Convenience method to get current user 112 * 113 * @return string 114 */ 115 public function getUser() 116 { 117 global $INPUT; 118 $user = $INPUT->server->str('REMOTE_USER'); 119 if (!$user) { 120 throw new \RuntimeException('2fa user specifics used before user available'); 121 } 122 return $user; 123 } 124 125 /** 126 * Get or set the user opt-out state 127 * 128 * true: user opted out 129 * false: user did not opt out 130 * 131 * @param bool|null $set 132 * @return bool 133 */ 134 public function userOptOutState($set = null) 135 { 136 // is optout allowed? 137 if ($this->getConf('optinout') !== 'optout') return false; 138 139 $settings = new Settings('twofactor', $this->getUser()); 140 141 if ($set === null) { 142 $current = $settings->get('state'); 143 return $current === 'optout'; 144 } 145 146 if ($set) { 147 $settings->set('state', 'optout'); 148 } else { 149 $settings->delete('state'); 150 } 151 return $set; 152 } 153 154 /** 155 * Get all available providers 156 * 157 * @return Provider[] 158 */ 159 public function getAllProviders() 160 { 161 $user = $this->getUser(); 162 163 if (!$this->providersInitialized) { 164 // initialize providers with user and ensure the ID is correct 165 foreach ($this->providers as $providerID => $provider) { 166 if ($providerID !== $provider->getProviderID()) { 167 $this->providers[$provider->getProviderID()] = $provider; 168 unset($this->providers[$providerID]); 169 } 170 $provider->init($user); 171 } 172 $this->providersInitialized = true; 173 } 174 175 return $this->providers; 176 } 177 178 /** 179 * Get all providers that have been already set up by the user 180 * 181 * @param bool $configured when set to false, all providers NOT configured are returned 182 * @return Provider[] 183 */ 184 public function getUserProviders($configured = true) 185 { 186 $list = $this->getAllProviders(); 187 $list = array_filter($list, function ($provider) use ($configured) { 188 return $configured ? $provider->isConfigured() : !$provider->isConfigured(); 189 }); 190 191 return $list; 192 } 193 194 /** 195 * Get the instance of the given provider 196 * 197 * @param string $providerID 198 * @return Provider 199 * @throws \Exception 200 */ 201 public function getUserProvider($providerID) 202 { 203 $providers = $this->getUserProviders(); 204 if (isset($providers[$providerID])) return $providers[$providerID]; 205 throw new \Exception('Uncofigured provider requested'); 206 } 207 208 /** 209 * Get the user's default provider if any 210 * 211 * Autoupdates the apropriate setting 212 * 213 * @return Provider|null 214 */ 215 public function getUserDefaultProvider() 216 { 217 $setting = new Settings('twofactor', $this->getUser()); 218 $default = $setting->get('defaultmod'); 219 $providers = $this->getUserProviders(); 220 221 if (isset($providers[$default])) return $providers[$default]; 222 // still here? no valid setting. Use first available one 223 $first = array_shift($providers); 224 if ($first !== null) { 225 $this->setUserDefaultProvider($first); 226 } 227 return $first; 228 } 229 230 /** 231 * Set the default provider for the user 232 * 233 * @param Provider $provider 234 * @return void 235 */ 236 public function setUserDefaultProvider($provider) 237 { 238 $setting = new Settings('twofactor', $this->getUser()); 239 $setting->set('defaultmod', $provider->getProviderID()); 240 } 241 242 /** 243 * Load all available provider classes 244 * 245 * @return Provider[]; 246 */ 247 protected function loadProviders() 248 { 249 /** @var Provider[] providers */ 250 $this->providers = []; 251 $event = new Event('PLUGIN_TWOFACTOR_PROVIDER_REGISTER', $this->providers); 252 $event->advise_before(false); 253 $event->advise_after(); 254 return $this->providers; 255 } 256 257} 258