1<?php 2/** 3 * DokuWiki Plugin authhiorgserver (Auth Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author HiOrg Server GmbH <support@hiorg-server.de> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) { 11 die(); 12} 13 14class auth_plugin_authhiorgserver extends DokuWiki_Auth_Plugin { 15 16 private $ssourl = ""; 17 private $data = array(); 18 private $triedsilent = false; 19 private $usersepchar = "@"; 20 21 /** 22 * Constructor. 23 */ 24 public function __construct() { 25 parent::__construct(); // for compatibility 26 27 $this->cando['addUser'] = false; // can Users be created? 28 $this->cando['delUser'] = false; // can Users be deleted? 29 $this->cando['modLogin'] = false; // can login names be changed? 30 $this->cando['modPass'] = false; // can passwords be changed? 31 $this->cando['modName'] = false; // can real names be changed? 32 $this->cando['modMail'] = false; // can emails be changed? 33 $this->cando['modGroups'] = false; // can groups be changed? 34 $this->cando['getUsers'] = false; // can a (filtered) list of users be retrieved? 35 $this->cando['getUserCount']= false; // can the number of users be retrieved? 36 $this->cando['getGroups'] = false; // can a list of available groups be retrieved? 37 $this->cando['logout'] = true; // can the user logout again? (eg. not possible with HTTP auth) 38 $this->cando['external'] = true; // does the module do external auth checking? 39 40 // $this->loadConfig(); // deprecated seit 2012 41 42 $this->ssourl = $this->getConf('ssourl'); 43 $ov = $this->getConf('ov'); 44 if(!empty($ov)) { 45 $this->ssourl = $this->addUrlParams($this->ssourl,array("ov"=>$ov)); 46 } 47 48 $this->data = array(); 49 50 $this->triedsilent = (isset($_SESSION[DOKU_COOKIE]['auth']['hiorg']['triedsilent']) 51 && ($_SESSION[DOKU_COOKIE]['auth']['hiorg']['triedsilent'] == true)); 52 53 $this->success = true; 54 } 55 56 57 /** 58 * Log off the current user 59 */ 60 public function logOff() { 61 $url = $this->addUrlParams($this->ssourl,array("logout"=>1,"token"=>$this->data["token"],"weiter"=> $this->myUrl())); 62 63 $this->data = array(); 64 $_SESSION[DOKU_COOKIE]['auth']['hiorg'] = array("triedsilent"=>true); 65 66 send_redirect($url); 67 } 68 69 /** 70 * Do all authentication 71 * 72 * @param string $user Username 73 * @param string $pass Cleartext Password 74 * @param bool $sticky Cookie should not expire 75 * @return bool true on successful auth 76 */ 77 public function trustExternal($user, $pass, $sticky = false) { 78 79 if ($this->loadUserInfoFromSession()) { 80 $this->setGlobalConfig(); 81 return true; 82 } 83 84 global $ACT; 85 if($ACT == "login") { 86 $this->processSSO(); 87 88 $this->setGlobalConfig(); 89 $this->saveUserInfoToSession(); 90 91 } elseif(!$this->triedsilent) { 92 $_SESSION[DOKU_COOKIE]['auth']['hiorg']['triedsilent'] = $this->triedsilent = true; 93 $this->SSOsilent(); 94 } 95 96 return true; 97 } 98 99 function myUrl($urlParameters = '') { 100 // global $ID; // ist zu diesem Zeitpunkt noch nicht initialisiert 101 $ID = getID(); 102 return wl($ID, $urlParameters, true, '&'); 103 } 104 105 function processSSO() { 106 107 // 1. Schritt: noch kein gueltiges Token vom HiOrg-Server erhalten 108 if(empty($_GET["token"])) { 109 $ziel = $this->addUrlParams($this->ssourl,array("weiter"=> $this->myUrl(array("do"=>"login")), // do=login, damit wir für den 2. Schritt wieder hier landen 110 "getuserinfo"=>"name,vorname,username,email,user_id")); 111 send_redirect($ziel); 112 } 113 114 // 2. Schritt: Token vom HiOrg-Server erhalten: jetzt Login ueberpruefen und Nutzerdaten abfragen 115 $token = $_GET["token"]; 116 117 $url = $this->addUrlParams($this->ssourl,array("token"=>$token)); 118 $daten = $this->getUrl($url); 119 120 if(mb_substr( $daten ,0,2) != "OK") { 121 nice_die("Login beim HiOrg-Server fehlgeschlagen!"); 122 } 123 $daten = unserialize(base64_decode(mb_substr( $daten , 3))); 124 125 // wenn per Konfig auf eine Organisation festgelegt, Cross-Logins abfangen: 126 $ov = $this->getConf('ov'); 127 if( !empty($ov) && ($daten["ov"] != $ov) ) { 128 nice_die("Falsches Organisationskuerzel: ".$daten["ov"]. ", erwartet: ".$ov); 129 } 130 131 // $daten = array("name"=>"Hansi", "vorname"=>"Tester", "username"=>"admin", "email"=>"test@test.de", "user_id"=>"abcde12345", "ov"=>"xxx"); 132 133 $this->data = array("uid" => $daten["user_id"], 134 "user" => $this->buildUser($daten["username"],$daten["ov"]), 135 "name" => $this->buildName($daten["vorname"], $daten["name"]), 136 "mail" => $daten["email"], 137 "token"=> $token); 138 $this->data["grps"] = $this->getGroups($this->data["user"]); 139 140 return true; 141 } 142 143 function SSOsilent() { 144 $ziel = $this->addUrlParams($this->ssourl, array("weiter" => $this->myUrl(array("do"=>"login")), // do=login, damit wir für den 2. Schritt wieder hier landen 145 "getuserinfo" => "name,vorname,username,email,user_id", 146 "silent" => $this->myUrl())); 147 send_redirect($ziel); 148 } 149 150 function getGroups($user) { 151 if(empty($user)) { 152 return ""; 153 } 154 155 $ov = trim($this->getConf("ov")); 156 157 global $conf; 158 $return = array($this->cleanGroup($conf["defaultgroup"])); 159 160 $groups = array("group1"=>$this->getConf("group1_name"), 161 "group2"=>$this->getConf("group2_name"), 162 "admin" =>"admin"); 163 164 foreach($groups as $name => $group) { 165 $users = $this->getConf($name."_users"); 166 if(!empty($group) && !empty($users)) { 167 if(!empty($ov)) { // ov automatisch ergänzen, wenn bekannt und nicht genannt 168 $userary = explode(",",$users); 169 $users = ""; 170 foreach($userary as $u) { 171 if(strpos($u,$this->usersepchar)===false) { 172 $u = $this->buildUser($u, $ov); 173 } 174 $users .= "," . $u; 175 } 176 } 177 if(strpos($users,$user)!==false) { 178 $return[] = $this->cleanGroup($group); 179 } 180 } 181 } 182 183 return $return; 184 } 185 186 function buildUser($user, $ov="") { 187 if(empty($ov)) { 188 $ov = trim($this->getConf("ov")); 189 } 190 return $this->cleanUser($user) . $this->usersepchar . $this->cleanUser($ov); 191 } 192 193 function buildName($vorname, $name) { 194 switch ($this->getConf('syncname')) { 195 case 'vname': 196 return substr($vorname,0,1).". ".$name; 197 case 'vona': 198 return substr($vorname,0,2).substr($name,0,2); 199 case 'vn': 200 return substr($vorname,0,1).substr($name,0,1); 201 default: 202 return $vorname." ".$name; 203 } 204 } 205 206 function loadUserInfoFromSession() { 207 if(isset($_SESSION[DOKU_COOKIE]['auth']['hiorg'])) { 208 $data = $_SESSION[DOKU_COOKIE]['auth']['hiorg']; 209 if(empty($data) || !is_array($data) || empty($data["token"])) { 210 return false; 211 } else { 212 $this->data = $data; 213 return true; 214 } 215 } 216 return false; 217 } 218 219 function saveUserInfoToSession() { 220 if(!empty($this->data["token"])) { 221 $_SESSION[DOKU_COOKIE]['auth']['hiorg'] = $this->data; 222 return true; 223 } 224 return false; 225 } 226 227 function setGlobalConfig() { 228 global $USERINFO; 229 $USERINFO['name'] = $this->data['name']; 230 $USERINFO['mail'] = $this->data['mail']; 231 $USERINFO['grps'] = $this->data['grps']; 232 $_SERVER['REMOTE_USER'] = $this->data['user']; 233 $_SESSION[DOKU_COOKIE]['auth']['user'] = $this->data['user']; 234 $_SESSION[DOKU_COOKIE]['auth']['pass'] = ""; 235 $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 236 return true; 237 } 238 239 /** 240 * Helper: builds URL by adding parameters 241 * 242 * @param string $url URL 243 * @param array $params additional parameters 244 * @return string 245 */ 246 function addUrlParams($url, $params) { 247 if(!is_array($params) || empty($params)) { 248 return $url; 249 } 250 251 $parary = array(); 252 $p = strpos($url,"?"); 253 if($p!==false) { 254 foreach(explode("&",substr($url,$p+1)) as $par) { 255 $q = strpos($par,"="); 256 $parary[substr($par,0,$q)] = substr($par,$q+1); 257 } 258 $url = substr($url,0,$p); 259 } 260 261 foreach($params as $par => $val) { 262 $parary[rawurlencode($par)] = rawurlencode($val); 263 } 264 265 $ret = $url; 266 $sep = "?"; 267 foreach($parary as $par => $val) { 268 $ret .= $sep . $par . "=" . $val; 269 $sep = "&"; 270 } 271 return $ret; 272 } 273 274 /** 275 * Helper: fetches external URL via GET 276 * 277 * @param string $url URL 278 * @return string 279 */ 280 function getUrl($url) { 281 $http = new DokuHTTPClient(); 282 $daten = $http->get($url); 283 284 // Workarounds, o.g. Klasse macht manchmal Probleme: 285 if(empty($daten)) { 286 if(function_exists("curl_init")) { 287 $ch = curl_init($url); 288 curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); 289 curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); 290 $daten = curl_exec($ch); 291 curl_close($ch); 292 293 } else { 294 if (!ini_get("allow_url_fopen") && version_compare(phpversion(), "4.3.4", "<=")) { 295 ini_set("allow_url_fopen", "1"); 296 } 297 if ($fp = @fopen($url, "r")) { 298 $daten = ""; 299 while (!feof($fp)) $daten.= fread($fp, 1024); 300 fclose($fp); 301 } 302 } 303 } 304 305 return $daten; 306 } 307 308 /** 309 * Return user info 310 * 311 * Returns info about the given user needs to contain 312 * at least these fields: 313 * 314 * name string full name of the user 315 * mail string email addres of the user 316 * grps array list of groups the user is in 317 * 318 * @param string $user the user name 319 * @return array containing user data or false 320 */ 321 /*public function getUserData($user) { 322 // FIXME implement 323 return false; 324 }*/ 325 326 327 /** 328 * Bulk retrieval of user data [implement only where required/possible] 329 * 330 * Set getUsers capability when implemented 331 * 332 * @param int $start index of first user to be returned 333 * @param int $limit max number of users to be returned 334 * @param array $filter array of field/pattern pairs, null for no filter 335 * @return array list of userinfo (refer getUserData for internal userinfo details) 336 */ 337 //public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 338 // FIXME implement 339 // return array(); 340 //} 341 342 /** 343 * Return a count of the number of user which meet $filter criteria 344 * [should be implemented whenever retrieveUsers is implemented] 345 * 346 * Set getUserCount capability when implemented 347 * 348 * @param array $filter array of field/pattern pairs, empty array for no filter 349 * @return int 350 */ 351 //public function getUserCount($filter = array()) { 352 // FIXME implement 353 // return 0; 354 //} 355 356 /** 357 * Retrieve groups [implement only where required/possible] 358 * 359 * Set getGroups capability when implemented 360 * 361 * @param int $start 362 * @param int $limit 363 * @return array 364 */ 365 //public function retrieveGroups($start = 0, $limit = 0) { 366 // FIXME implement 367 // return array(); 368 //} 369 370 /** 371 * Return case sensitivity of the backend 372 * 373 * When your backend is caseinsensitive (eg. you can login with USER and 374 * user) then you need to overwrite this method and return false 375 * 376 * @return bool 377 */ 378 public function isCaseSensitive() { 379 return false; 380 } 381 382 /** 383 * Sanitize a given username 384 * 385 * This function is applied to any user name that is given to 386 * the backend and should also be applied to any user name within 387 * the backend before returning it somewhere. 388 * 389 * This should be used to enforce username restrictions. 390 * 391 * @param string $user username 392 * @return string the cleaned username 393 */ 394 395 public function cleanUser($user) { 396 global $conf; 397 return cleanID(str_replace(':', $conf['sepchar'], $user)); 398 } 399 /** 400 * Sanitize a given groupname 401 * 402 * This function is applied to any groupname that is given to 403 * the backend and should also be applied to any groupname within 404 * the backend before returning it somewhere. 405 * 406 * This should be used to enforce groupname restrictions. 407 * 408 * Groupnames are to be passed without a leading '@' here. 409 * 410 * @param string $group groupname 411 * @return string the cleaned groupname 412 */ 413 public function cleanGroup($group) { 414 global $conf; 415 return cleanID(str_replace(':', $conf['sepchar'], $group)); 416 } 417 418 /** 419 * Check Session Cache validity [implement only where required/possible] 420 * 421 * DokuWiki caches user info in the user's session for the timespan defined 422 * in $conf['auth_security_timeout']. 423 * 424 * This makes sure slow authentication backends do not slow down DokuWiki. 425 * This also means that changes to the user database will not be reflected 426 * on currently logged in users. 427 * 428 * To accommodate for this, the user manager plugin will touch a reference 429 * file whenever a change is submitted. This function compares the filetime 430 * of this reference file with the time stored in the session. 431 * 432 * This reference file mechanism does not reflect changes done directly in 433 * the backend's database through other means than the user manager plugin. 434 * 435 * Fast backends might want to return always false, to force rechecks on 436 * each page load. Others might want to use their own checking here. If 437 * unsure, do not override. 438 * 439 * @param string $user - The username 440 * @return bool 441 */ 442 //public function useSessionCache($user) { 443 // FIXME implement 444 //} 445} 446 447// vim:ts=4:sw=4:et: 448