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 */ 139 public function getUserProvider($providerID) 140 { 141 $providers = $this->getUserProviders(); 142 if (isset($providers[$providerID])) return $providers[$providerID]; 143 throw new \RuntimeException('Uncofigured provider requested'); 144 } 145 146 /** 147 * Get the user's default provider if any 148 * 149 * Autoupdates the apropriate setting 150 * 151 * @return Provider|null 152 */ 153 public function getUserDefaultProvider() 154 { 155 $setting = new Settings('twofactor', $this->getUser()); 156 $default = $setting->get('defaultmod'); 157 $providers = $this->getUserProviders(); 158 159 if (isset($providers[$default])) return $providers[$default]; 160 // still here? no valid setting. Use first available one 161 $first = array_shift($providers); 162 if ($first !== null) { 163 $this->setUserDefaultProvider($first); 164 } 165 return $first; 166 } 167 168 /** 169 * Set the default provider for the user 170 * 171 * @param Provider $provider 172 * @return void 173 */ 174 public function setUserDefaultProvider($provider) 175 { 176 $setting = new Settings('twofactor', $this->getUser()); 177 $setting->set('defaultmod', $provider->getProviderID()); 178 } 179 180 /** 181 * Find all available provider classes 182 * 183 * @return string[]; 184 */ 185 protected function getProviderClasses() 186 { 187 // FIXME this relies on naming alone, we might want to use an action for registering 188 $plugins = plugin_list('helper'); 189 $plugins = array_filter($plugins, function ($plugin) { 190 return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor'; 191 }); 192 193 $classes = []; 194 foreach ($plugins as $plugin) { 195 $class = 'helper_plugin_' . $plugin; 196 if (is_a($class, Provider::class, true)) { 197 $classes[$plugin] = $class; 198 } 199 } 200 201 return $classes; 202 } 203 204} 205