1<?php 2 3use dokuwiki\plugin\twofactor\Manager; 4use dokuwiki\plugin\twofactor\MenuItem; 5use dokuwiki\plugin\twofactor\Provider; 6 7/** 8 * DokuWiki Plugin twofactor (Action Component) 9 * 10 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 11 */ 12class action_plugin_twofactor_profile extends \dokuwiki\Extension\ActionPlugin 13{ 14 15 /** @inheritDoc */ 16 public function register(Doku_Event_Handler $controller) 17 { 18 if (!(Manager::getInstance())->isReady()) return; 19 20 // Adds our twofactor profile to the user menu. 21 $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'handleUserMenuAssembly'); 22 23 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handlePreProcess'); 24 $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleUnknownAction'); 25 } 26 27 /** 28 * Add 2fa Menu Item 29 * 30 * @param Doku_Event $event 31 */ 32 public function handleUserMenuAssembly(Doku_Event $event) 33 { 34 global $INPUT; 35 // If this is not the user menu, then get out. 36 if ($event->data['view'] != 'user') return; 37 if (!$INPUT->server->has('REMOTE_USER')) return; 38 39 // Create the new menu item 40 $menuitem = new MenuItem($this->getLang('btn_twofactor_profile')); 41 42 // Find index of existing Profile menu item. 43 for ($index = 0; $index > count($event->data['items']); $index++) { 44 if ($event->data['items'][$index]->getType() === 'profile') { 45 break; 46 } 47 } 48 array_splice($event->data['items'], $index + 1, 0, [$menuitem]); 49 } 50 51 /** 52 * Check permissions to call the 2fa profile 53 * 54 * @param Doku_Event $event 55 */ 56 public function handlePreProcess(Doku_Event $event) 57 { 58 if ($event->data != 'twofactor_profile') return; 59 60 // We will be handling this action's permissions here. 61 $event->preventDefault(); 62 $event->stopPropagation(); 63 64 // If not logged into the main auth plugin then send there. 65 global $INPUT; 66 global $ID; 67 68 if (!$INPUT->server->has('REMOTE_USER')) { 69 $event->result = false; 70 send_redirect(wl($ID, array('do' => 'login'), true, '&')); 71 return; 72 } 73 74 $this->handleProfile(); 75 76 /** FIXME 77 * 78 * // If not logged into twofactor then send there. 79 * if (!$this->get_clearance()) { 80 * $event->result = false; 81 * send_redirect(wl($ID, array('do' => 'twofactor_login'), true, '&')); 82 * return; 83 * } 84 * // Otherwise handle the action. 85 * $event->result = $this->_process_changes($event, $param); 86 */ 87 88 } 89 90 /** 91 * @param Doku_Event $event 92 */ 93 public function handleUnknownAction(Doku_Event $event) 94 { 95 if ($event->data != 'twofactor_profile') return; 96 97 $event->preventDefault(); 98 $event->stopPropagation(); 99 $this->printProfile(); 100 } 101 102 /** 103 * Handle POSTs for provider forms 104 */ 105 protected function handleProfile() 106 { 107 global $INPUT; 108 if (!$INPUT->has('provider')) return; 109 $user = $INPUT->server->str('REMOTE_USER'); 110 111 $class = 'helper_plugin_' . $INPUT->str('provider'); 112 /** @var Provider $provider */ 113 $provider = new $class($user); 114 115 if (!checkSecurityToken()) return; 116 117 if (!$provider->isConfigured()) { 118 $provider->handleProfileForm(); 119 } elseif ($INPUT->has('2fa_delete')) { 120 $provider->reset(); 121 } 122 } 123 124 /** 125 * Handles the profile form rendering. Displays user manageable settings. 126 * @todo move elsewhere 127 */ 128 protected function printProfile() 129 { 130 global $lang; 131 132 echo $this->locale_xhtml('profile'); 133 134 // iterate over all providers 135 $providers = (Manager::getInstance())->getAllProviders(); 136 foreach ($providers as $provider) { 137 $form = new dokuwiki\Form\Form(['method' => 'POST']); 138 $form->setHiddenField('do', 'twofactor_profile'); 139 $form->setHiddenField('provider', $provider->getProviderID()); 140 $form->addFieldsetOpen($provider->getLabel()); 141 $provider->renderProfileForm($form); 142 if (!$provider->isConfigured()) { 143 $form->addButton('2fa_submit', $lang['btn_save'])->attr('submit'); 144 } else { 145 $form->addButton('2fa_delete', $lang['btn_delete'])->attr('submit'); 146 } 147 $form->addFieldsetClose(); 148 echo $form->toHTML(); 149 } 150 151 /* FIXME 152 153 154 $optinout = $this->getConf("optinout"); 155 $optinvalue = $optinout == 'mandatory' ? 'in' : ($this->attribute ? $this->attribute->get("twofactor", 156 "state") : ''); 157 $available = count($this->tokenMods) + count($this->otpMods) > 0; 158 // If the user is being redirected here because of mandatory two factor, then display a message saying so. 159 if (!$available && $optinout == 'mandatory') { 160 msg($this->getLang('mandatory'), -1); 161 } elseif ($this->attribute->get("twofactor", "state") == '' && $optinout == 'optout') { 162 msg($this->getLang('optout_notice'), 2); 163 } elseif ($this->attribute->get("twofactor", 164 "state") == 'in' && count($this->tokenMods) == 0 && count($this->otpMods) == 0) { 165 msg($this->getLang('not_configured_notice'), 2); 166 } 167 global $USERINFO, $lang, $conf; 168 $form = new Doku_Form(array('id' => 'twofactor_setup')); 169 // Add the checkbox to opt in and out, only if optinout is not mandatory. 170 $items = array(); 171 if ($optinout != 'mandatory') { 172 if (!$this->attribute || !$optinvalue) { // If there is no personal setting for optin, the default is based on the wiki default. 173 $optinvalue = $this->getConf("optinout") == 'optout'; 174 } 175 $items[] = form_makeCheckboxField('optinout', '1', $this->getLang('twofactor_optin'), '', 'block', 176 $optinvalue == 'in' ? array('checked' => 'checked') : array()); 177 } 178 // Add the notification checkbox if appropriate. 179 if ($this->getConf('loginnotice') == 'user' && $optinvalue == 'in' && count($this->otpMods) > 0) { 180 $loginnotice = $this->attribute ? $this->attribute->get("twofactor", "loginnotice") : false; 181 $items[] = form_makeCheckboxField('loginnotice', '1', $this->getLang('twofactor_notify'), '', 'block', 182 $loginnotice === true ? array('checked' => 'checked') : array()); 183 } 184 // Select a notification provider. 185 if ($optinvalue == 'in') { 186 // If there is more than one choice, have the user select the default. 187 if (count($this->otpMods) > 1) { 188 $defaultMod = $this->attribute->exists("twofactor", "defaultmod") ? $this->attribute->get("twofactor", 189 "defaultmod") : null; 190 $modList = array_merge(array($this->getLang('useallotp')), array_keys($this->otpMods)); 191 $items[] = form_makeListboxField('default_module', $modList, $defaultMod, 192 $this->getLang('defaultmodule'), '', 'block'); 193 } 194 } 195 if (count($items) > 0) { 196 $form->startFieldset($this->getLang('settings')); 197 foreach ($items as $item) { 198 $form->addElement($item); 199 } 200 $form->endFieldset(); 201 } 202 // TODO: Make this AJAX so that the user does not have to keep clicking 203 // submit them Update Profile! 204 // Loop through all modules and render the profile components. 205 if ($optinvalue == 'in') { 206 $parts = array(); 207 foreach ($this->modules as $mod) { 208 if ($mod->getConf("enable") == 1) { 209 $this->log('twofactor_profile_form: processing ' . get_class($mod) . '::renderProfileForm()', 210 self::LOGGING_DEBUG); 211 $items = $mod->renderProfileForm(); 212 if (count($items) > 0) { 213 $form->startFieldset($mod->getLang('name')); 214 foreach ($items as $item) { 215 $form->addElement($item); 216 } 217 $form->endFieldset(); 218 } 219 } 220 } 221 } 222 if ($conf['profileconfirm']) { 223 $form->addElement('<br />'); 224 $form->startFieldset($this->getLang('verify_password')); 225 $form->addElement(form_makePasswordField('oldpass', $lang['oldpass'], '', 'block', 226 array('size' => '50', 'required' => 'required'))); 227 $form->endFieldset(); 228 } 229 $form->addElement('<br />'); 230 $form->addElement(form_makeButton('submit', '', $lang['btn_save'])); 231 $form->addElement('<a href="' . wl($ID, array('do' => 'show'), true, 232 '&') . '">' . $this->getLang('btn_return') . '</a>'); 233 $form->addHidden('do', 'twofactor_profile'); 234 $form->addHidden('save', '1'); 235 echo '<div class="centeralign">' . NL . $form->getForm() . '</div>' . NL; 236 237 */ 238 } 239} 240 241