1<?php 2/** 3 * Federated Login for DokuWiki - file-based data storage class 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @link http://www.dokuwiki.org/plugin:fedauth 7 * @author Aoi Karasu <aoikarasu@gmail.com> 8 */ 9 10// The filestore requires Dokuwiki 11if (!defined('DOKU_INC')) die(); 12 13// Constants for known core changelog line types. 14// Use these in place of string literals for more readable code. 15if (!defined('FEDAUTH_CHANGE_TYPE_CREATE')) define('FEDAUTH_CHANGE_TYPE_CREATE', 'C'); 16if (!defined('FEDAUTH_CHANGE_TYPE_DELETE')) define('FEDAUTH_CHANGE_TYPE_DELETE', 'D'); 17if (!defined('FEDAUTH_CHANGE_TYPE_REFRESH')) define('FEDAUTH_CHANGE_TYPE_REFRESH', 'R'); 18 19/** 20 * Federated login file-based data storage class. Handles all 21 * i/o operations performed on authentication user data. 22 * 23 * @author Aoi Karasu <aoikarasu@gmail.com> 24 */ 25class fa_filestore { 26 27 /** 28 * Base data storage path, eg. [dw_root]/data/users/ 29 */ 30 var $root = ''; 31 32 /** 33 * Base providers configuration path, eg. [dw_root]/conf/fedauth/ 34 */ 35 var $conf = ''; 36 37 /** 38 * Array containing user identity entries. 39 */ 40 var $userData = null; 41 42 /** 43 * Array containing the identities from single provider 44 * service associated with local usernames (accounts). 45 */ 46 var $claimedIdentities = null; 47 48 /** 49 * Creates the class instance. 50 */ 51 function __construct() { 52 $this->root = DOKU_INC . 'data/users/'; 53 $this->conf = DOKU_CONF . 'fedauth/'; 54 io_makeFileDir($this->root); 55 io_makeFileDir($this->conf); 56// print "<pre>" . $this->root . "\n" . $this->conf . "</pre>"; 57 } 58 59 /** 60 * Loads the identities from single provider service 61 * associated with local usernames (accounts) from a file. 62 * 63 * @param string $providerId provider identifier 64 * @return mixed identity+username pairs array or false on failure 65 */ 66 function &getClaimedIdentities($providerId) { 67 if (is_array($this->claimedIdentities)) { 68 return $this->claimedIdentities; 69 } 70 $lines = @file_get_contents($this->provFN($providerId)); 71 $lines = explode("\n", $lines); 72 if (empty($lines)) return false; 73 74 $data = array(); 75 foreach ($lines as $value) { 76 $tmp = $this->parseClaimedIdentitiesLine($value); 77 if ($tmp !== false) { 78 $data[] = $tmp; 79 } 80 } 81 $this->claimedIdentities = $data; 82 return $this->claimedIdentities; 83 } 84 85 /** 86 * Returns a username associated with a claimed identity. 87 * 88 * @param string $providerId provider identifier 89 * @param string $claimedId claimed identifier 90 * @return mixed username or false on failure 91 */ 92 function getUsernameByIdentity($providerId, $claimedId) { 93 if (($entries =& $this->getClaimedIdentities($providerId)) === false) return false; 94 95 $strip = array("\t", "\n"); 96 $claimedId = str_replace($strip, '', $claimedId); 97 foreach ($entries as $entry) { 98 if ($entry['ident'] == $claimedId) { 99 return $entry['user']; 100 } 101 } 102 return false; 103 } 104 105 /** 106 * Parses a claimed identity line into it's components. 107 * 108 * @param string $line claimed identity line to parse 109 * @return mixed claimed identity entry array or false on failure 110 */ 111 function parseClaimedIdentitiesLine($line) { 112 $tmp = explode("\t", $line); 113 if ($tmp!==false && count($tmp)>1) { 114 $info = array( 115 'ident' => $tmp[0], // user identity 116 'user' => $tmp[1]); // local username 117 return $info; 118 } 119 return false; 120 } 121 122 /** 123 * Loads user authentication data from file. 124 * 125 * @return mixed user authentication data array or false on failure 126 */ 127 function &getUserData() { 128 if (is_array($this->userData)) { 129 return $this->userData; 130 } 131 $lines = @file_get_contents($this->userFN()); 132 $lines = explode("\n", $lines); 133 if (empty($lines)) return false; 134 135 $data = array(); 136 foreach ($lines as $value) { 137 $tmp = $this->parseUserDataLine($value); 138 if ($tmp !== false) { 139 $data[] = $tmp; 140 } 141 } 142 $this->userData = $data; 143 return $this->userData; 144 } 145 146 /** 147 * Searches for user authentication data entry by identity. 148 * 149 * @param string $claimedId identity associated with the user data entry 150 * @return mixed user authentication data array or false on failure 151 */ 152 function getUserDataEntry($claimedId) { 153 if (($entries =& $this->getUserData()) === false) return false; 154 155 $strip = array("\t", "\n"); 156 $claimedId = str_replace($strip, '', $claimedId); 157 foreach ($entries as $entry) { 158 if ($entry['ident'] == $claimedId) { 159 return $entry; 160 } 161 } 162 return false; 163 } 164 165 /** 166 * Parses an user data line into it's components. 167 * 168 * @param string $line user data line to parse 169 * @return mixed user data entry array or false on failure 170 */ 171 function parseUserDataLine($line) { 172 $tmp = explode("\t", $line); 173 if ($tmp!==false && count($tmp)>1) { 174 $info = array( 175 'id' => $tmp[0], // provider id 176 'ident' => $tmp[1], // user identity 177 'last' => (int)$tmp[2]); // last used 178 return $info; 179 } 180 return false; 181 } 182 183 /** 184 * Adds an entry to the user authentication data file. 185 * 186 * @param string $providerId identifier of the auth provider service 187 * @param string $claimedId user authentication identity 188 * @param int $date (optional) timestamp of the change 189 */ 190 function addUserDataEntry($providerId, $claimedId, $date=null) { 191 if (!$date) $date = time(); //use current time if none supplied 192 $user = $_SERVER['REMOTE_USER']; 193 194 $strip = array("\t", "\n"); 195 $data = array( 196 'id' => $providerId, // provider id 197 'ident' => str_replace($strip, '', $claimedId), // user identity 198 'last' => $date); // last used 199 $provline = array( 200 'ident' => $data['ident'], 201 'user' => $user); 202 203 $dataline = implode("\t", $data) . "\n"; 204 $provline = implode("\t", $provline) . "\n"; 205 io_saveFile($this->userFN($user), $dataline, true); //user data 206 io_saveFile($this->provFN($providerId), $provline, true); //global provider identities 207 $this->addLogEntry($date, $providerId, $claimedId, FEDAUTH_CHANGE_TYPE_CREATE); 208 209 $this->userData = null; // force reload userData on next get 210 } 211 212 /** 213 * Deletes user authentication data entry from the data file by identity. 214 * 215 * @param string $claimedId identity associated with the user data entry 216 * @return mixed deleted entry or false on failure 217 */ 218 function deleteUserDataEntry($claimedId) { 219 if ($entry = $this->getUserDataEntry($claimedId)) { 220 $user = $_SERVER['REMOTE_USER']; 221 $provline = array( 222 'ident' => $entry['ident'], 223 'user' => $user); 224 $dataline = implode("\t", $entry) . "\n"; 225 $provline = implode("\t", $provline) . "\n"; 226 io_deleteFromFile($this->userFN($user), $dataline); 227 io_deleteFromFile($this->provFN($entry['id']), $provline); 228 $this->addLogEntry(time(), $entry['id'], $claimedId, FEDAUTH_CHANGE_TYPE_DELETE); 229 $this->userData = null; // force reload userData on next get 230 return $entry; 231 } 232 return false; 233 } 234 235 /** 236 * Updates last used time for user authentication data entry by identity. 237 * 238 * @param string $claimedId identity associated with the user data entry 239 */ 240 function refreshUserDataEntry($claimedId) { 241 if ($entry = $this->getUserDataEntry($claimedId)) { 242 $user = $_SERVER['REMOTE_USER']; 243 $dataline = implode("\t", $entry) . "\n"; 244 io_deleteFromFile($this->userFN($user), $dataline); 245 $entry['last'] = time(); 246 $dataline = implode("\t", $entry) . "\n"; 247 io_saveFile($this->userFN($user), $dataline, true); 248 $this->addLogEntry(time(), $entry['id'], $claimedId, FEDAUTH_CHANGE_TYPE_REFRESH); 249 $this->userData = null; // force reload userData on next get 250 } 251 } 252 253 /** 254 * Parses a changelog line into it's components. 255 * 256 * @param string $line changelog line to parse 257 * @return mixed changelog entry array or false on failure 258 */ 259 function parseChangelogLine($line) { 260 $tmp = explode("\t", $line); 261 if ($tmp!==false && count($tmp)>1) { 262 $info = array( 263 'date' => (int)$tmp[0], 264 'ip' => $tmp[1], 265 'type' => $tmp[2], 266 'id' => $tmp[3], 267 'ident' => $tmp[4], 268 'user' => $tmp[5]); 269 return $info; 270 } 271 return false; 272 } 273 274 /** 275 * Adds an entry to the user authentication data changelog. 276 * 277 * @param int $date timestamp of the change 278 * @param string $providerId identifier of the auth provider service 279 * @param string $claimedId user authentication identity 280 * @param string $type type of the change see FEDAUTH_CHANGE_TYPE_* 281 * @param bool $isExternal (optional) is change made by user or as result of internal cleanup 282 */ 283 function addLogEntry($date, $providerId, $claimedId, $type, $isExternal=true) { 284 if (!$date) $date = time(); //use current time if none supplied 285 $remote = ($isExternal) ? clientIP(true) : '127.0.0.1'; 286 $user = ($isExternal) ? $_SERVER['REMOTE_USER'] : ''; 287 288 $strip = array("\t", "\n"); 289 $logline = array( 290 'date' => $date, 291 'ip' => $remote, 292 'type' => str_replace($strip, '', $type), 293 'id' => $providerId, 294 'ident' => str_replace($strip, '', $claimedId), 295 'user' => $user); 296 297 // add the changelog line 298 $logline = implode("\t", $logline) . "\n"; 299 $refresh = ($type == FEDAUTH_CHANGE_TYPE_REFRESH); 300 io_saveFile($this->userFN($user, $refresh ? 'activity' : 'changes'), $logline, true); //user data changelog 301 // don't use global provider changelog to log sign-ins; too much data 302 if (!$refresh) { 303 io_saveFile($this->provFN($providerId, 'changes'), $logline, true); 304 } 305 } 306 307 /** 308 * Returns the full path to the provider file with user identities. 309 * Note: use the extension parameter to get related (eg. meta) file path. 310 * 311 * @param string $provid provider identifier 312 * @param string $ext (optional) file extension 313 * @return string the full path 314 */ 315 function provFN($provid, $ext='conf') { 316 return $this->conf . $provid . '.' . $ext; 317 } 318 319 /** 320 * Returns the full path to the user file with all associated idenities. 321 * Note: use the extension parameter to get related (eg. meta) file path. 322 * 323 * @param string $username (optional) the username; leave empty to autodetect 324 * @param string $ext (optional) file extension 325 * @return string the full path 326 */ 327 function userFN($username='', $ext='data') { 328 if (empty($username)) $username = $_SERVER['REMOTE_USER']; 329 return $this->root . substr($username, 0, 1) . '/' . $username . '/fedauth.' . $ext; 330 } 331 332} /* fa_filestore */ 333 334/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 335