1<?php 2 3namespace dokuwiki\plugin\twofactor; 4 5use dokuwiki\Extension\Plugin; 6 7/** 8 * Manages the child plugins etc. 9 */ 10class Manager extends Plugin 11{ 12 /** @var Manager */ 13 protected static $instance; 14 15 /** @var bool */ 16 protected $ready = false; 17 18 /** @var string[] */ 19 protected $classes = []; 20 21 /** @var Provider[] */ 22 protected $providers; 23 24 /** 25 * Constructor 26 */ 27 protected function __construct() 28 { 29 $this->classes = $this->getProviderClasses(); 30 31 $attribute = plugin_load('helper', 'attribute'); 32 if ($attribute === null) { 33 msg('The attribute plugin is not available, 2fa disabled', -1); 34 } 35 36 if (!count($this->classes)) { 37 msg('No suitable 2fa providers found, 2fa disabled', -1); 38 return; 39 } 40 41 $this->ready = true; 42 } 43 44 /** 45 * Get the instance of this singleton 46 * 47 * @return Manager 48 */ 49 public static function getInstance() 50 { 51 if (self::$instance === null) { 52 self::$instance = new Manager(); 53 } 54 return self::$instance; 55 } 56 57 /** 58 * Is the plugin ready to be used? 59 * 60 * @return bool 61 */ 62 public function isReady() 63 { 64 return $this->ready; 65 } 66 67 /** 68 * Is a 2fa login required? 69 * 70 * @return bool 71 */ 72 public function isRequired() 73 { 74 $set = $this->getConf('optinout'); 75 if ($set === 'mandatory') { 76 return true; 77 } 78 // FIXME handle other options: 79 // when optout, return true unless user opted out 80 81 return false; 82 } 83 84 /** 85 * Convenience method to get current user 86 * 87 * @return string 88 */ 89 public function getUser() 90 { 91 global $INPUT; 92 $user = $INPUT->server->str('REMOTE_USER'); 93 if (!$user) { 94 throw new \RuntimeException('2fa user specifics used before user available'); 95 } 96 return $user; 97 } 98 99 /** 100 * Get all available providers 101 * 102 * @return Provider[] 103 */ 104 public function getAllProviders() 105 { 106 $user = $this->getUser(); 107 108 if ($this->providers === null) { 109 $this->providers = []; 110 foreach ($this->classes as $plugin => $class) { 111 $this->providers[$plugin] = new $class($user); 112 } 113 } 114 115 return $this->providers; 116 } 117 118 /** 119 * Get all providers that have been already set up by the user 120 * 121 * @return Provider[] 122 */ 123 public function getUserProviders() 124 { 125 $list = $this->getAllProviders(); 126 $list = array_filter($list, function ($provider) { 127 return $provider->isConfigured(); 128 }); 129 130 return $list; 131 } 132 133 /** 134 * Get the instance of the given provider 135 * 136 * @param string $providerID 137 * @return Provider 138 * @throws \Exception 139 */ 140 public function getUserProvider($providerID) 141 { 142 $providers = $this->getUserProviders(); 143 if (isset($providers[$providerID])) return $providers[$providerID]; 144 throw new \Exception('Uncofigured provider requested'); 145 } 146 147 /** 148 * Get the user's default provider if any 149 * 150 * Autoupdates the apropriate setting 151 * 152 * @return Provider|null 153 */ 154 public function getUserDefaultProvider() 155 { 156 $setting = new Settings('twofactor', $this->getUser()); 157 $default = $setting->get('defaultmod'); 158 $providers = $this->getUserProviders(); 159 160 if (isset($providers[$default])) return $providers[$default]; 161 // still here? no valid setting. Use first available one 162 $first = array_shift($providers); 163 if ($first !== null) { 164 $this->setUserDefaultProvider($first); 165 } 166 return $first; 167 } 168 169 /** 170 * Set the default provider for the user 171 * 172 * @param Provider $provider 173 * @return void 174 */ 175 public function setUserDefaultProvider($provider) 176 { 177 $setting = new Settings('twofactor', $this->getUser()); 178 $setting->set('defaultmod', $provider->getProviderID()); 179 } 180 181 /** 182 * Find all available provider classes 183 * 184 * @return string[]; 185 */ 186 protected function getProviderClasses() 187 { 188 // FIXME this relies on naming alone, we might want to use an action for registering 189 $plugins = plugin_list('helper'); 190 $plugins = array_filter($plugins, function ($plugin) { 191 return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor'; 192 }); 193 194 $classes = []; 195 foreach ($plugins as $plugin) { 196 $class = 'helper_plugin_' . $plugin; 197 if (is_a($class, Provider::class, true)) { 198 $classes[$plugin] = $class; 199 } 200 } 201 202 return $classes; 203 } 204 205} 206