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