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