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