1<?php 2 3use dokuwiki\Extension\ActionPlugin; 4use dokuwiki\Extension\EventHandler; 5use dokuwiki\Extension\Event; 6use dokuwiki\Form\Form; 7use dokuwiki\Action\Exception\ActionException; 8use dokuwiki\Action\Resendpwd; 9 10/** 11 * DokuWiki Plugin passpolicy (Action Component) 12 * 13 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 14 * @author Andreas Gohr <andi@splitbrain.org> 15 */ 16class action_plugin_passpolicy extends ActionPlugin 17{ 18 /** 19 * Registers a callback function for a given event 20 * 21 * @param EventHandler $controller DokuWiki's event controller object 22 * @return void 23 */ 24 public function register(EventHandler $controller) 25 { 26 $controller->register_hook('FORM_REGISTER_OUTPUT', 'BEFORE', $this, 'handleForms'); 27 $controller->register_hook('FORM_UPDATEPROFILE_OUTPUT', 'BEFORE', $this, 'handleForms'); 28 $controller->register_hook('FORM_RESENDPWD_OUTPUT', 'BEFORE', $this, 'handleForms'); 29 30 $controller->register_hook('HTML_REGISTERFORM_OUTPUT', 'BEFORE', $this, 'handleForms'); 31 $controller->register_hook('HTML_UPDATEPROFILEFORM_OUTPUT', 'BEFORE', $this, 'handleForms'); 32 $controller->register_hook('HTML_RESENDPWDFORM_OUTPUT', 'BEFORE', $this, 'handleForms'); 33 34 $controller->register_hook('AUTH_USER_CHANGE', 'BEFORE', $this, 'handlePasschange'); 35 36 $controller->register_hook('AUTH_PASSWORD_GENERATE', 'BEFORE', $this, 'handlePassgen'); 37 38 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax'); 39 40 if ($this->getConf('supressuserhints')) { 41 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleResendPwd'); 42 $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleResendPwdUI'); 43 } 44 } 45 46 /** 47 * Handle Ajax for the Password strength check 48 * 49 * @param Event $event 50 * @param $param 51 */ 52 public function handleAjax(Event $event, $param) 53 { 54 if ($event->data !== 'plugin_passpolicy') { 55 return; 56 } 57 //no other ajax call handlers needed 58 $event->stopPropagation(); 59 $event->preventDefault(); 60 61 /* @var $INPUT \Input */ 62 global $INPUT; 63 $pass = $INPUT->post->str('pass'); 64 $user = $INPUT->post->str('user', $_SERVER['REMOTE_USER']); 65 66 /** @var helper_plugin_passpolicy $passpolicy */ 67 $passpolicy = $this->loadHelper('passpolicy'); 68 if (!$passpolicy->checkPolicy($pass, $user)) { 69 // passpolicy not matched, throw error 70 echo '0'; 71 } else { 72 echo '1'; 73 } 74 } 75 76 /** 77 * Print the password policy in forms that allow setting passwords 78 * 79 * @param Event $event event object 80 * @param mixed $param 81 */ 82 public function handleForms(Event $event, $param) 83 { 84 if (is_a($event->data, Form::class)) { 85 // applicable to development snapshot 2020-10-13 or later 86 $pos = $event->data->findPositionByAttribute('name', 'passchk'); 87 } else { 88 // applicable to 2020-07-29 "Hogfather" and older 89 $pos = $event->data->findElementByAttribute('name', 'passchk'); 90 } 91 if (!$pos) return; // no password repeat field found 92 93 /** @var $passpolicy helper_plugin_passpolicy */ 94 $passpolicy = plugin_load('helper', 'passpolicy'); 95 $html = '<p class="passpolicy_hint">' . $passpolicy->explainPolicy() . '</p>'; 96 if (is_a($event->data, Form::class)) { 97 $event->data->addHTML($html, ++$pos); 98 } else { 99 $event->data->insertElement(++$pos, $html); 100 } 101 } 102 103 /** 104 * Check if a new password matches the set password policy 105 * 106 * @param Event $event event object 107 * @param mixed $param 108 */ 109 public function handlePasschange(Event $event, $param) 110 { 111 if ($event->data['type'] == 'create') { 112 $user = $event->data['params'][0]; 113 $pass = $event->data['params'][1]; 114 } elseif ($event->data['type'] == 'modify') { 115 $user = $event->data['params'][0]; 116 if (!isset($event->data['params'][1]['pass'])) { 117 return; //password is not changed, nothing to do 118 } 119 $pass = $event->data['params'][1]['pass']; 120 } else { 121 return; 122 } 123 124 /** @var $passpolicy helper_plugin_passpolicy */ 125 $passpolicy = plugin_load('helper', 'passpolicy'); 126 if (!$passpolicy->checkPolicy($pass, $user)) { 127 // passpolicy not matched, throw error and stop modification 128 msg($this->getLang('badpasspolicy'), -1); 129 $event->preventDefault(); 130 $event->stopPropagation(); 131 } 132 } 133 134 /** 135 * Replace default password generator by policy aware one 136 * 137 * @param Event $event event object 138 * @param mixed $param 139 * @throws Exception 140 */ 141 public function handlePassgen(Event $event, $param) 142 { 143 /** @var $passpolicy helper_plugin_passpolicy */ 144 $passpolicy = plugin_load('helper', 'passpolicy'); 145 146 $event->data['password'] = $passpolicy->generatePassword($event->data['foruser']); 147 $event->preventDefault(); 148 } 149 150 /** 151 * Intercept the resendpwd action 152 * 153 * This supresses all hints on if a user exists or not 154 * 155 * @param Event $event 156 * @param $param 157 */ 158 public function handleResendPwd(Event $event, $param) 159 { 160 $act = act_clean($event->data); 161 if ($act != 'resendpwd') return; 162 163 $event->preventDefault(); 164 165 $action = new Resendpwd(); 166 try { 167 $action->checkPreconditions(); 168 } catch (ActionException $ignored) { 169 $event->data = 'show'; 170 return; 171 } 172 173 try { 174 $action->preProcess(); 175 } catch (ActionException $ignored) { 176 } 177 178 $this->fixResendMessages(); 179 } 180 181 /** 182 * Reuse the standard action UI for ResendPwd 183 * 184 * @param Event $event 185 * @param $param 186 */ 187 public function handleResendPwdUI(Event $event, $param) 188 { 189 $act = act_clean($event->data); 190 if ($act != 'resendpwd') return; 191 $event->preventDefault(); 192 (new Resendpwd())->tplContent(); 193 } 194 195 /** 196 * Replaces the resendPwd messages with neutral ones 197 * 198 * @return void 199 */ 200 protected function fixResendMessages() 201 { 202 global $MSG; 203 global $lang; 204 205 foreach ((array)$MSG as $key => $info) { 206 if ( 207 $info['msg'] == $lang['resendpwdnouser'] || $info['msg'] == $lang['resendpwdconfirm'] 208 ) { 209 unset($MSG[$key]); 210 msg($this->getLang('resendpwd'), 1); 211 } 212 } 213 } 214} 215