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