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 * @return Provider[] 182 */ 183 public function getUserProviders() 184 { 185 $list = $this->getAllProviders(); 186 $list = array_filter($list, function ($provider) { 187 return $provider->isConfigured(); 188 }); 189 190 return $list; 191 } 192 193 /** 194 * Get the instance of the given provider 195 * 196 * @param string $providerID 197 * @return Provider 198 * @throws \Exception 199 */ 200 public function getUserProvider($providerID) 201 { 202 $providers = $this->getUserProviders(); 203 if (isset($providers[$providerID])) return $providers[$providerID]; 204 throw new \Exception('Uncofigured provider requested'); 205 } 206 207 /** 208 * Get the user's default provider if any 209 * 210 * Autoupdates the apropriate setting 211 * 212 * @return Provider|null 213 */ 214 public function getUserDefaultProvider() 215 { 216 $setting = new Settings('twofactor', $this->getUser()); 217 $default = $setting->get('defaultmod'); 218 $providers = $this->getUserProviders(); 219 220 if (isset($providers[$default])) return $providers[$default]; 221 // still here? no valid setting. Use first available one 222 $first = array_shift($providers); 223 if ($first !== null) { 224 $this->setUserDefaultProvider($first); 225 } 226 return $first; 227 } 228 229 /** 230 * Set the default provider for the user 231 * 232 * @param Provider $provider 233 * @return void 234 */ 235 public function setUserDefaultProvider($provider) 236 { 237 $setting = new Settings('twofactor', $this->getUser()); 238 $setting->set('defaultmod', $provider->getProviderID()); 239 } 240 241 /** 242 * Load all available provider classes 243 * 244 * @return Provider[]; 245 */ 246 protected function loadProviders() 247 { 248 /** @var Provider[] providers */ 249 $this->providers = []; 250 $event = new Event('PLUGIN_TWOFACTOR_PROVIDER_REGISTER', $this->providers); 251 $event->advise_before(false); 252 $event->advise_after(); 253 return $this->providers; 254 } 255 256} 257