register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'handleUserMenuAssembly'); $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handlePreProcess'); $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleUnknownAction'); } /** * Add 2fa Menu Item * * @param Doku_Event $event */ public function handleUserMenuAssembly(Doku_Event $event) { if (!(Manager::getInstance())->isReady()) return; global $INPUT; // If this is not the user menu, then get out. if ($event->data['view'] != 'user') return; if (!$INPUT->server->has('REMOTE_USER')) return; // Create the new menu item $menuitem = new MenuItem($this->getLang('btn_twofactor_profile')); // Find index of existing Profile menu item. for ($index = 0; $index > count($event->data['items']); $index++) { if ($event->data['items'][$index]->getType() === 'profile') { break; } } array_splice($event->data['items'], $index + 1, 0, [$menuitem]); } /** * Check permissions to call the 2fa profile * * @param Doku_Event $event */ public function handlePreProcess(Doku_Event $event) { if ($event->data != 'twofactor_profile') return; if (!(Manager::getInstance())->isReady()) return; // We will be handling this action's permissions here. $event->preventDefault(); $event->stopPropagation(); // If not logged into the main auth plugin then send there. global $INPUT; global $ID; if (!$INPUT->server->has('REMOTE_USER')) { $event->result = false; send_redirect(wl($ID, array('do' => 'login'), true, '&')); return; } if (strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') { if ($this->handleProfile()) { // we might have changed something important, make sure the whole workflow restarts send_redirect(wl($ID, ['do' => 'twofactor_profile'], true, '&')); } } } /** * Output the forms * * @param Doku_Event $event */ public function handleUnknownAction(Doku_Event $event) { if ($event->data != 'twofactor_profile') return; if (!(Manager::getInstance())->isReady()) return; $event->preventDefault(); $event->stopPropagation(); global $INPUT; echo '
'; echo $this->locale_xhtml('profile'); if ($INPUT->has('twofactor_setup')) { $this->printProviderSetup(); } else { if (!$this->printOptOutForm()) { $this->printConfiguredProviders(); $this->printProviderSetupSelect(); } } echo '
'; } /** * Handle POSTs for provider forms * * @return bool should a redirect be made? */ protected function handleProfile() { global $INPUT; if (!checkSecurityToken()) return true; $manager = Manager::getInstance(); if ($INPUT->has('twofactor_optout') && $this->getConf('optinout') === 'optout') { $manager->userOptOutState($INPUT->bool('optout')); return true; } if (!$INPUT->has('provider')) return true; $providers = $manager->getAllProviders(); if (!isset($providers[$INPUT->str('provider')])) return true; $provider = $providers[$INPUT->str('provider')]; if ($INPUT->has('twofactor_delete')) { $provider->reset(); $manager->getUserDefaultProvider(); // resets the default to the next available return true; } if ($INPUT->has('twofactor_default')) { $manager->setUserDefaultProvider($provider); return true; } if (!$provider->isConfigured()) { $provider->handleProfileForm(); return $provider->isConfigured(); // redirect only if configuration finished } return true; } /** * Print the opt-out form (if available) * * @return bool true if the user currently opted out */ protected function printOptOutForm() { $manager = Manager::getInstance(); $optedout = false; $setting = $this->getConf('optinout'); echo '
'; echo $this->locale_xhtml($setting); // optout form if ($setting == 'optout') { $form = new Form(['method' => 'post']); $cb = $form->addCheckbox('optout', $this->getLang('optout')); if ($manager->userOptOutState()) { $cb->attr('checked', 'checked'); } $form->addButton('twofactor_optout', $this->getLang('btn_confirm')); echo $form->toHTML(); // when user opted out, don't show the rest of the form if ($manager->userOptOutState()) { $optedout = true; } } echo '
'; return $optedout; } /** * Print the form where a user can select their default provider * * @return void */ protected function printConfiguredProviders() { $manager = Manager::getInstance(); $userproviders = $manager->getUserProviders(); $default = $manager->getUserDefaultProvider(); if (!$userproviders) return; $form = new Form(['method' => 'POST']); $form->addFieldsetOpen($this->getLang('providers')); foreach ($userproviders as $provider) { $el = $form->addRadioButton('provider', $provider->getLabel())->val($provider->getProviderID()); if ($provider->getProviderID() === $default->getProviderID()) { $el->attr('checked', 'checked'); $el->getLabel()->val($provider->getLabel() . ' ' . $this->getLang('default')); } } $form->addTagOpen('div')->addClass('buttons'); $form->addButton('twofactor_default', $this->getLang('btn_default'))->attr('submit'); $form->addButton('twofactor_delete', $this->getLang('btn_remove')) ->addClass('twofactor_delconfirm')->attr('submit'); $form->addTagClose('div'); $form->addFieldsetClose(); echo $form->toHTML(); } /** * List providers available for adding * * @return void */ protected function printProviderSetupSelect() { $manager = Manager::getInstance(); $available = $manager->getUserProviders(false); if (!$available) return; $options = []; foreach ($available as $provider) { $options[$provider->getProviderID()] = $provider->getLabel(); } $form = new Form(['method' => 'post']); $form->setHiddenField('do', 'twofactor_profile'); $form->setHiddenField('init', '1'); $form->addFieldsetOpen($this->getLang('newprovider')); $form->addDropdown('provider', $options, $this->getLang('provider')); $form->addTagOpen('div')->addClass('buttons'); $form->addButton('twofactor_setup', $this->getLang('btn_setup'))->attr('type', 'submit'); $form->addTagClose('div'); $form->addFieldsetClose(); echo $form->toHTML(); } /** * Display the setup form for a provider * * @return void */ protected function printProviderSetup() { global $lang; global $INPUT; $providerID = $INPUT->str('provider'); $providers = (Manager::getInstance())->getUserProviders(false); if (!isset($providers[$providerID])) return; $provider = $providers[$providerID]; $form = new Form(['method' => 'POST', 'class' => 'provider-' . $providerID]); $form->setHiddenField('do', 'twofactor_profile'); $form->setHiddenField('twofactor_setup', '1'); $form->setHiddenField('provider', $provider->getProviderID()); $form->addFieldsetOpen($provider->getLabel()); $provider->renderProfileForm($form); $form->addTagOpen('div')->addClass('buttons'); $form->addButton('twofactor_submit', $this->getLang('btn_confirm'))->attr('type', 'submit'); $form->addButton('twofactor_delete', $lang['btn_cancel'])->attr('type', 'submit'); $form->addTagClose('div'); $form->addFieldsetClose(); echo $form->toHTML(); } }