1fca58076SAndreas Gohr<?php 2fca58076SAndreas Gohr 3b6119621SAndreas Gohruse dokuwiki\Form\Form; 48b7620a8SAndreas Gohruse dokuwiki\plugin\twofactor\Manager; 58b7620a8SAndreas Gohruse dokuwiki\plugin\twofactor\MenuItem; 6a635cb20SAndreas Gohr 7fca58076SAndreas Gohr/** 8fca58076SAndreas Gohr * DokuWiki Plugin twofactor (Action Component) 9fca58076SAndreas Gohr * 10fca58076SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 11fca58076SAndreas Gohr */ 12fca58076SAndreas Gohrclass action_plugin_twofactor_profile extends \dokuwiki\Extension\ActionPlugin 13fca58076SAndreas Gohr{ 14b6119621SAndreas Gohr /** @var Manager */ 15b6119621SAndreas Gohr protected $manager; 16b6119621SAndreas Gohr 17b6119621SAndreas Gohr /** 18b6119621SAndreas Gohr * Constructor 19b6119621SAndreas Gohr */ 20b6119621SAndreas Gohr public function __construct() 21b6119621SAndreas Gohr { 22b6119621SAndreas Gohr $this->manager = Manager::getInstance(); 23b6119621SAndreas Gohr } 24fca58076SAndreas Gohr 25fca58076SAndreas Gohr /** @inheritDoc */ 26fca58076SAndreas Gohr public function register(Doku_Event_Handler $controller) 27fca58076SAndreas Gohr { 28b6119621SAndreas Gohr if (!$this->manager->isReady()) return; 298b7620a8SAndreas Gohr 30fca58076SAndreas Gohr // Adds our twofactor profile to the user menu. 31fca58076SAndreas Gohr $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'handleUserMenuAssembly'); 32fca58076SAndreas Gohr 33fca58076SAndreas Gohr $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handlePreProcess'); 34fca58076SAndreas Gohr $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleUnknownAction'); 35fca58076SAndreas Gohr } 36fca58076SAndreas Gohr 37fca58076SAndreas Gohr /** 38fca58076SAndreas Gohr * Add 2fa Menu Item 39fca58076SAndreas Gohr * 40fca58076SAndreas Gohr * @param Doku_Event $event 41fca58076SAndreas Gohr */ 42fca58076SAndreas Gohr public function handleUserMenuAssembly(Doku_Event $event) 43fca58076SAndreas Gohr { 44fca58076SAndreas Gohr global $INPUT; 45fca58076SAndreas Gohr // If this is not the user menu, then get out. 46fca58076SAndreas Gohr if ($event->data['view'] != 'user') return; 47fca58076SAndreas Gohr if (!$INPUT->server->has('REMOTE_USER')) return; 48fca58076SAndreas Gohr 49fca58076SAndreas Gohr // Create the new menu item 508b7620a8SAndreas Gohr $menuitem = new MenuItem($this->getLang('btn_twofactor_profile')); 51fca58076SAndreas Gohr 52fca58076SAndreas Gohr // Find index of existing Profile menu item. 53fca58076SAndreas Gohr for ($index = 0; $index > count($event->data['items']); $index++) { 54fca58076SAndreas Gohr if ($event->data['items'][$index]->getType() === 'profile') { 55fca58076SAndreas Gohr break; 56fca58076SAndreas Gohr } 57fca58076SAndreas Gohr } 58fca58076SAndreas Gohr array_splice($event->data['items'], $index + 1, 0, [$menuitem]); 59fca58076SAndreas Gohr } 60fca58076SAndreas Gohr 61fca58076SAndreas Gohr /** 62fca58076SAndreas Gohr * Check permissions to call the 2fa profile 63fca58076SAndreas Gohr * 64fca58076SAndreas Gohr * @param Doku_Event $event 65fca58076SAndreas Gohr */ 66fca58076SAndreas Gohr public function handlePreProcess(Doku_Event $event) 67fca58076SAndreas Gohr { 68fca58076SAndreas Gohr if ($event->data != 'twofactor_profile') return; 69fca58076SAndreas Gohr 70fca58076SAndreas Gohr // We will be handling this action's permissions here. 71fca58076SAndreas Gohr $event->preventDefault(); 72fca58076SAndreas Gohr $event->stopPropagation(); 73fca58076SAndreas Gohr 74fca58076SAndreas Gohr // If not logged into the main auth plugin then send there. 75fca58076SAndreas Gohr global $INPUT; 76fca58076SAndreas Gohr global $ID; 77fca58076SAndreas Gohr 78fca58076SAndreas Gohr if (!$INPUT->server->has('REMOTE_USER')) { 79fca58076SAndreas Gohr $event->result = false; 80fca58076SAndreas Gohr send_redirect(wl($ID, array('do' => 'login'), true, '&')); 81fca58076SAndreas Gohr return; 82fca58076SAndreas Gohr } 83fca58076SAndreas Gohr 84*4b9cff8aSAndreas Gohr if (strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') { 85a635cb20SAndreas Gohr $this->handleProfile(); 86fca58076SAndreas Gohr } 87fca58076SAndreas Gohr 88*4b9cff8aSAndreas Gohr } 89*4b9cff8aSAndreas Gohr 90fca58076SAndreas Gohr /** 91fca58076SAndreas Gohr * @param Doku_Event $event 92fca58076SAndreas Gohr */ 93fca58076SAndreas Gohr public function handleUnknownAction(Doku_Event $event) 94fca58076SAndreas Gohr { 95fca58076SAndreas Gohr if ($event->data != 'twofactor_profile') return; 96fca58076SAndreas Gohr 97fca58076SAndreas Gohr $event->preventDefault(); 98fca58076SAndreas Gohr $event->stopPropagation(); 99fca58076SAndreas Gohr $this->printProfile(); 100fca58076SAndreas Gohr } 101fca58076SAndreas Gohr 102fca58076SAndreas Gohr /** 103a635cb20SAndreas Gohr * Handle POSTs for provider forms 104a635cb20SAndreas Gohr */ 105a635cb20SAndreas Gohr protected function handleProfile() 106a635cb20SAndreas Gohr { 107a635cb20SAndreas Gohr global $INPUT; 108a635cb20SAndreas Gohr if (!checkSecurityToken()) return; 109a635cb20SAndreas Gohr 110*4b9cff8aSAndreas Gohr if ($INPUT->has('2fa_optout') && $this->getConf('optinout') === 'optout') { 111*4b9cff8aSAndreas Gohr $this->manager->userOptOutState($INPUT->bool('optout')); 112*4b9cff8aSAndreas Gohr return; 113*4b9cff8aSAndreas Gohr } 114*4b9cff8aSAndreas Gohr 115*4b9cff8aSAndreas Gohr if (!$INPUT->has('provider')) return; 116*4b9cff8aSAndreas Gohr $providers = $this->manager->getAllProviders(); 117*4b9cff8aSAndreas Gohr if (!isset($providers[$INPUT->str('provider')])) return; 118*4b9cff8aSAndreas Gohr $provider = $providers[$INPUT->str('provider')]; 119*4b9cff8aSAndreas Gohr 120a635cb20SAndreas Gohr if (!$provider->isConfigured()) { 121a635cb20SAndreas Gohr $provider->handleProfileForm(); 122a635cb20SAndreas Gohr } elseif ($INPUT->has('2fa_delete')) { 123a635cb20SAndreas Gohr $provider->reset(); 124b6119621SAndreas Gohr $this->manager->getUserDefaultProvider(); // resets the default to the next available 125b6119621SAndreas Gohr } elseif ($INPUT->has('2fa_default')) { 126b6119621SAndreas Gohr $this->manager->setUserDefaultProvider($provider); 127a635cb20SAndreas Gohr } 128a635cb20SAndreas Gohr } 129a635cb20SAndreas Gohr 130a635cb20SAndreas Gohr /** 131fca58076SAndreas Gohr * Handles the profile form rendering. Displays user manageable settings. 132*4b9cff8aSAndreas Gohr * 133*4b9cff8aSAndreas Gohr * @todo split up in smaller methods 134fca58076SAndreas Gohr */ 135a635cb20SAndreas Gohr protected function printProfile() 136fca58076SAndreas Gohr { 137fca58076SAndreas Gohr global $lang; 138fca58076SAndreas Gohr 139a635cb20SAndreas Gohr echo $this->locale_xhtml('profile'); 140fca58076SAndreas Gohr 141*4b9cff8aSAndreas Gohr switch ($this->getConf('optinout')) { 142*4b9cff8aSAndreas Gohr case 'optout': 143*4b9cff8aSAndreas Gohr $form = new Form(['method' => 'post']); 144*4b9cff8aSAndreas Gohr $form->addFieldsetOpen('Opt Out'); 145*4b9cff8aSAndreas Gohr $form->addHTML('<p>This wiki highly recomends 2fa, if you don\'t want it opt out here...</p>'); 146*4b9cff8aSAndreas Gohr $cb = $form->addCheckbox('optout', 'I understand my account is less secure. I opt out'); 147*4b9cff8aSAndreas Gohr if ($this->manager->userOptOutState()) { 148*4b9cff8aSAndreas Gohr $cb->attr('checked', 'checked'); 149*4b9cff8aSAndreas Gohr } 150*4b9cff8aSAndreas Gohr $form->addButton('2fa_optout', 'Save'); 151*4b9cff8aSAndreas Gohr $form->addFieldsetClose(); 152*4b9cff8aSAndreas Gohr echo $form->toHTML(); 153*4b9cff8aSAndreas Gohr 154*4b9cff8aSAndreas Gohr // when user opted out, don't show the rest of the form 155*4b9cff8aSAndreas Gohr if ($this->manager->userOptOutState()) { 156*4b9cff8aSAndreas Gohr return; 157*4b9cff8aSAndreas Gohr } 158*4b9cff8aSAndreas Gohr break; 159*4b9cff8aSAndreas Gohr case 'optin': 160*4b9cff8aSAndreas Gohr echo '<p>Using 2fa is not mandatory but recommended. Please configure at least one of the providers...</p>'; 161*4b9cff8aSAndreas Gohr break; 162*4b9cff8aSAndreas Gohr case 'mandatory': 163*4b9cff8aSAndreas Gohr echo '<p>The use of 2fa is mandatory you must configure at least one provider before using the wiki...</p>'; 164*4b9cff8aSAndreas Gohr break; 165*4b9cff8aSAndreas Gohr } 166*4b9cff8aSAndreas Gohr 167b6119621SAndreas Gohr // default provider selection 168b6119621SAndreas Gohr $userproviders = $this->manager->getUserProviders(); 169b6119621SAndreas Gohr $default = $this->manager->getUserDefaultProvider(); 170b6119621SAndreas Gohr if (count($userproviders)) { 171b6119621SAndreas Gohr $form = new Form(['method' => 'POST']); 172b6119621SAndreas Gohr $form->addFieldsetOpen('Default Provider'); 173b6119621SAndreas Gohr foreach ($userproviders as $provider) { 174b6119621SAndreas Gohr $form->addRadioButton('provider', $provider->getLabel()) 175b6119621SAndreas Gohr ->val($provider->getProviderID()) 176b6119621SAndreas Gohr ->attr('checked', $provider->getProviderID() === $default->getProviderID()); 177b6119621SAndreas Gohr } 178b6119621SAndreas Gohr $form->addButton('2fa_default', $lang['btn_save'])->attr('submit'); 179b6119621SAndreas Gohr $form->addFieldsetClose(); 180b6119621SAndreas Gohr echo $form->toHTML(); 181b6119621SAndreas Gohr } 182b6119621SAndreas Gohr 183a635cb20SAndreas Gohr // iterate over all providers 184b6119621SAndreas Gohr $providers = $this->manager->getAllProviders(); 185a635cb20SAndreas Gohr foreach ($providers as $provider) { 186a635cb20SAndreas Gohr $form = new dokuwiki\Form\Form(['method' => 'POST']); 187a635cb20SAndreas Gohr $form->setHiddenField('do', 'twofactor_profile'); 188a635cb20SAndreas Gohr $form->setHiddenField('provider', $provider->getProviderID()); 189a635cb20SAndreas Gohr $form->addFieldsetOpen($provider->getLabel()); 190a635cb20SAndreas Gohr $provider->renderProfileForm($form); 191a635cb20SAndreas Gohr if (!$provider->isConfigured()) { 192a635cb20SAndreas Gohr $form->addButton('2fa_submit', $lang['btn_save'])->attr('submit'); 193a635cb20SAndreas Gohr } else { 194a635cb20SAndreas Gohr $form->addButton('2fa_delete', $lang['btn_delete'])->attr('submit'); 195a635cb20SAndreas Gohr } 196a635cb20SAndreas Gohr $form->addFieldsetClose(); 197fca58076SAndreas Gohr echo $form->toHTML(); 198a635cb20SAndreas Gohr } 199fca58076SAndreas Gohr } 200fca58076SAndreas Gohr} 201fca58076SAndreas Gohr 202