4 * Copyright (c) 2003, The Burgiss Group, LLC
5 * This source code is part of eWiki LiveUser Plugin.
7 * eWiki LiveUser Plugin is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
12 * eWiki LiveUser Plugin is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Wiki LiveUser Plugin; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * note: this file should be included by all ewiki liveuser plugin scripts. it
24 * defines a number of utility functions, instantiates required liveuser
25 * objects used throughout the plugin, and ensures the inclusion of liveuser's
28 * note: The following abstraction functions treat the name value of entities as
29 * a unique id (although it does not have to be) along with the existing
30 * numerical id (unique by default). For language entities, these functions will
31 * return the 2-letter language code as the unique identifier.
34 require_once('LiveUser/LiveUser.php');
35 require_once('LiveUser/Admin/Auth/Container/DB.php');
36 require_once('LiveUser/Admin/Perm/Container/DB_Complex.php');
37 require_once(dirname(__FILE__).'/liveuser_conf.php');
40 define('LEVENSTIEN_MIN',6);
42 /* fetch auth/perm admin objects */
43 $liveuserAuthAdmin = &new LiveUser_Admin_Auth_Container_DB($liveuserConfig['authContainers'][0]);
44 $liveuserPermAdmin = &new LiveUser_Admin_Perm_Container_DB_Complex($liveuserConfig['permContainer']);
47 * Translates a username or auth object user id into a perm object user id. The
48 * argument type may either be 'user' or 'user_id', to denote if the second
49 * id argument is a username or auth id.
51 * @param string type of second argument
52 * @param mixed id integer auth_id or username
53 * @return integer corresponding perm_id if user exists, false otherwise
55 function liveuser_getPermUserId($type, $id)
57 global $liveuserDB, $liveuserConfig;
59 $container_name = $liveuserConfig['authContainers'][0]['name'];
61 if (($type == 'user' || $type == 'user_id') &&
62 ($auth_id = liveuser_checkEntity($type, $id)) !== false) {
63 $perm_user_id = $liveuserDB->getOne('
64 SELECT liveuser_perm_users.perm_user_id
65 FROM liveuser_perm_users, liveuser_users
66 WHERE liveuser_users.auth_user_id = ? AND
67 liveuser_perm_users.auth_container_name = ? AND
68 liveuser_users.auth_user_id = liveuser_perm_users.auth_user_id',
69 array($auth_id, $container_name));
70 return (is_numeric($perm_user_id) ? $perm_user_id : false);
77 * Allows for duplicate-safe creation of LiveUser entities. The supplied entity
78 * name must be one of the following: language, application; area; right; group;
79 * or user. This function expects the following global variables:
81 * $liveuserAuthAdmin (of type LiveUser_Admin_Auth_Container_DB)
82 * $liveuserPermAdmin (of type LiveUser_Admin_Perm_Container_DB_Complex)
83 * $liveuserConfig (LiveUser configuration array)
85 * @param string type name of entity type to add
86 * @param array args array corresponding to argument list of the entity's add method
87 * @return mixed unique id (integer or string) of added entity on success, false on error
89 function liveuser_addEntity($type, $args)
91 global $liveuserAuthAdmin, $liveuserPermAdmin, $liveuserConfig;
94 // special case, add language, but return 2-letter code instead of numerical id
96 if (($languageId = liveuser_checkEntity($type, $args[0])) === false) {
97 $languageId = call_user_func_array(array(&$liveuserPermAdmin,'addLanguage'), $args);
98 // return 2-letter code for success, false otherwise
99 return (is_numeric($languageId) ? $args[0] : false);
103 // special case, add user to both auth and perm containers
106 if (($auth_id = liveuser_checkEntity($type, $args[0])) === false) {
107 $auth_id = call_user_func_array(array(&$liveuserAuthAdmin,'addUser'), $args);
108 $perm_id = $liveuserPermAdmin->addUser($auth_id, LIVEUSER_USER_TYPE_ID, null, $liveuserConfig['authContainers'][0]['name']);
110 return (is_numeric($auth_id) && is_numeric($perm_id) ? $auth_id : false);
112 // common cases, fetch unique name from arguments and proceed
125 // failure case, unknown type
131 // common check and create cases where type is: application, area, right, group
132 if (($entityId = liveuser_checkEntity($type, $argName)) === false) {
133 $entityId = call_user_func_array(array(&$liveuserPermAdmin,'add'.$type), $args);
135 return (is_numeric($entityId) ? $entityId : false);
139 * Checks for existing LiveUser entities. The supplied entity name must be one
140 * of the following: application; area; right; group; user; or language. These
141 * types will denote that the id parameter is in the form of the entity name. To
142 * denote that the id parameter is in the form of the numeric id, the suffix
143 * '_id' should be appended to the type names listed above. Depending on the
144 * supplied id parameter, this function will search for entities matching the
145 * numerical id before matching against the string name. All name id values must
146 * be non-numeric strings. This function expects the following global variables:
148 * $liveuserAuthAdmin (of type LiveUser_Admin_Auth_Container_DB)
149 * $liveuserPermAdmin (of type LiveUser_Admin_Perm_Container_DB_Complex)
151 * @param string type name of entity type to add
152 * @param mixed id integer id or unique name of entity to check for depending on type
153 * @return mixed unique id (integer or string) of entity if it exists, false otherwise
155 function liveuser_checkEntity($type, $id)
157 global $liveuserDB, $liveuserConfig, $liveuserAuthAdmin, $liveuserPermAdmin;
160 // special case, check for existing language and handle 2-character name
163 $languages = $liveuserPermAdmin->getLanguages();
164 foreach ($languages as $code => $language) {
165 if (($type == 'language' && $code == $id && strlen($id) == 2) ||
166 ($type == 'language_id' && $language['language_id'] == $id)) {
173 // special case, check against user entity sets in both auth and perm containers
176 $users = $liveuserAuthAdmin->getUsers();
178 foreach ($users as $user) {
179 if (($type == 'user' && $user['handle'] == $id) ||
180 ($type == 'user_id' && $user['auth_user_id'] == $id)) {
181 $auth_id = $user['auth_user_id'];
185 // no match if user_id is unset, otherwise proceed to check with perm container
186 if (!isset($auth_id)) {
188 } else if ($liveuserDB->getOne('SELECT 1 FROM '.$liveuserConfig['permContainer']['prefix'].'perm_users WHERE auth_user_id = ?', array((int)$auth_id)) == 1) {
195 // common cases, check against entity selection
200 $entities = call_user_func_array(array(&$liveuserPermAdmin, 'get'.$type.'s'), array());
201 foreach ($entities as $entity) {
202 if ($entity['name'] == $id) {
203 return $entity[$type.'_id'];
209 case 'application_id':
213 $entities = call_user_func_array(array(&$liveuserPermAdmin, 'get'.substr($type, 0, -3).'s'), array());
214 foreach ($entities as $entity) {
215 if ($entity[$type] == $id) {
216 return $entity[$type];
222 // failure case, unknown type or no match for entity
230 * Allows for removal of LiveUser entities. The supplied entity name must be one
231 * of the following: language, application; area; right; group; or user. These
232 * types imply that the id parameter is in the form of the entity name. To
233 * denote the id parameter as an entity numeric id, the suffix '_id' should be
234 * appended to the names. This function expects the following global variables:
236 * $liveuserAuthAdmin (of type LiveUser_Admin_Auth_Container_DB)
237 * $liveuserPermAdmin (of type LiveUser_Admin_Perm_Container_DB_Complex)
239 * @param string type name of entity type to remove
240 * @param mixed id integer id or unique name of entity to remove depending on type
241 * @return true if the entity existed and was removed, false otherwise
243 function liveuser_removeEntity($type, $id)
245 global $liveuserDB, $liveuserConfig, $liveuserAuthAdmin, $liveuserPermAdmin;
248 // special case, remove user to both auth and perm containers
251 // log removed users with ewiki_log
252 if (($auth_id = liveuser_checkEntity($type, $id)) !== false &&
253 ($perm_id = liveuser_getPermUserId('user_id', $auth_id)) !== false) {
254 // fetch authTable names
255 $authTable = $liveuserConfig['authContainers'][0]['authTable'];
257 // backup user preferences and groups of user being removed
258 $backup['prefs'] = $liveuserDB->getAll('SELECT '.LW_PREFIX.'_prefs_fields.field_name, '.LW_PREFIX.'_prefs_data.field_value
259 FROM '.LW_PREFIX.'_prefs_fields, '.LW_PREFIX.'_prefs_data
260 WHERE '.LW_PREFIX.'_prefs_data.user_id = ? AND '.LW_PREFIX.'_prefs_data.field_id = '.LW_PREFIX.'_prefs_fields.field_id',
261 array((int)$perm_id));
263 // direct sql required to fetch group_define_name
264 $backup['groups'] = $liveuserDB->getAll('SELECT liveuser_groups.group_define_name
265 FROM liveuser_groups, liveuser_groupusers
266 WHERE liveuser_groupusers.perm_user_id = ? AND liveuser_groupusers.group_id = liveuser_groups.group_id',
267 array((int)$perm_id));
269 // output serialized data to log file
270 $handle = $liveuserDB->getOne('SELECT handle FROM ! WHERE auth_user_id = ?',
271 array($authTable, (int)$auth_id));
272 ewiki_log('liveuser: removed user: '.$handle.'|'.serialize($backup), 1);
274 // remove records of user in auth/perm containers and the user's preferences
275 if ($liveuserPermAdmin->removeUser($perm_id) !== true) {
278 if ($liveuserAuthAdmin->removeUser($auth_id) !== true) {
281 return ($liveuserDB->query('DELETE FROM '.LW_PREFIX.'_prefs_data WHERE '.LW_PREFIX.'_prefs_data.user_id = ?',
282 array((int)$perm_id)) == DB_OK);
287 // common cases, fetch unique name from arguments and proceed
293 if (($entityId = liveuser_checkEntity($type, $id)) !== false) {
294 if (call_user_func_array(array(&$liveuserPermAdmin,'remove'.$type), $entityId) !== true) {
303 case 'application_id':
307 if (($entityId = liveuser_checkEntity($type, $id)) !== false) {
308 if (call_user_func_array(array(&$liveuserPermAdmin,'remove'.substr($type, 0, -3)), $entityId) !== true) {
316 // failure case, unknown type or no match for entity
324 * Allows for duplicate-safe creation of page permissions. If the permissions
325 * already exists, the page/right record will be updated, rather than inserted.
326 * Permissions may be specified as a combination of the page name and right_id,
327 * or the integer id of the permission may be supplied as the first parameter
328 * and the right_id parameter omitted. The preserveHigher parameter may be set
329 * to ensure that existing permissions higher than the permission being set
330 * are not overwritten, thus making the update have no effect.
332 * @param string type name of entity type to remove
333 * @param mixed page_id ewiki page name as a string or integer id
334 * @param int ring ewiki ring level
335 * @param int right_id LiveUser right id
336 * @param boolean preserveHigher does not overwrite higher permissions if true
338 * @return boolean true if permission was successfully added or modified, false otherwise
340 function liveuser_addPerm($page_id, $ring, $right_id = null, $preserveHigher = false)
344 if (($perm_id = liveuser_checkPerm($page_id, $right_id)) !== false) {
345 if ($preserveHigher) {
346 return ($liveuserDB->query('UPDATE '.LW_PREFIX.'_perms SET ring = ? WHERE id = ? AND ring > ?',
347 array((int)$ring, (int)$perm_id, (int)$ring)) == DB_OK);
349 return ($liveuserDB->query('UPDATE '.LW_PREFIX.'_perms SET ring = ? WHERE id = ?',
350 array((int)$ring, (int)$perm_id)) == DB_OK);
353 return ($liveuserDB->query('INSERT INTO '.LW_PREFIX.'_perms (pagename, ring, right_id) VALUES (?, ?, ?)',
354 array($page_id, (int)$ring, (int)$right_id)) == DB_OK);
360 * Checks if a page permission already exists in the database. Permissions
361 * may be specified as a combination of the page name and right_id, or the
362 * integer id of the permission may be supplied as the first and only parameter.
364 * @param mixed page_id ewiki page name as a string or integer id
366 * @param int right_id LiveUser right id
368 * @return mixed integer identifier if permission exists, false otherwise
370 function liveuser_checkPerm($page_id, $right_id = null)
376 if (is_null($right_id)) {
377 if (is_numeric($page_id)) {
378 $perm_id = $liveuserDB->getOne('SELECT id FROM '.LW_PREFIX.'_perms WHERE id = ?',
381 // non-numeric page_id with null right_id, invalid input
385 $perm_id = $liveuserDB->getOne('SELECT id FROM '.LW_PREFIX.'_perms WHERE pagename = ? AND right_id = ?',
386 array($page_id, (int)$right_id));
388 return (is_numeric($perm_id) ? $perm_id : false);
392 * Allows for removal of page permissions. Permissions may be specified as a
393 * combination of the page name and right_id, or the integer id of the permission
394 * may be supplied as the first and only parameter. The ring_min parameter may
395 * be used to limit the permissions that will be removed by ensuring that their
396 * ring level is at least the value given. The default value (0: highest access
397 * level) would allow all permissions to be removed.
399 * @param mixed page_id ewiki page name as a string or integer id
400 * @param int right_id LiveUser right id
401 * @param int ring_min remove only if ring level is >= this value
403 * @return boolean true if permission was successfully removed, false otherwise
405 function liveuser_removePerm($page_id, $right_id = null, $ring_min = 0)
409 if (($perm_id = liveuser_checkPerm($page_id, $right_id)) !== false) {
410 return ($liveuserDB->query('DELETE FROM '.LW_PREFIX.'_perms WHERE id = ? AND ring >= ?',
411 array((int)$perm_id, (int)$ring_min)) == DB_OK);
417 * Retrieves all permissions for a page. Permissions may be specified as a
418 * combination of the pagename and right_id, or the integer id of the permission
419 * may be supplied as the first and only parameter.
421 * @param string pagename ewiki page name as a string
422 * @param int ring_min retrieve only if ring level is >= this value
424 * @return mixed array of permissions for the page
426 function liveuser_getPerms($pagename, $ring_min = 0)
430 return $liveuserDB->getAll('SELECT * FROM '.LW_PREFIX.'_perms WHERE pagename = ? AND ring >= ?',
431 array($pagename, (int)$ring_min));
435 * Checks if a group has a right.
437 * @param integer group_id group_id
438 * @param integer right_id right id
439 * @return boolean true if group has the right, false otherwise
441 function liveuser_checkGroupRight($group_id, $right_id)
443 global $liveuserPermAdmin;
445 $groups = $liveuserPermAdmin->getGroups(array('where_group_id' => $group_id, 'with_rights' => true));
447 if (!is_array($groups)) {
451 // result of getGroups() is an array [result set] of arrays [groups]
452 foreach ($groups as $group) {
453 $rights = $group['rights'];
454 foreach($rights as $right) {
455 if ($right['right_id'] == $right_id) {
464 * Checks if a user is a member of a group.
466 * @param integer auth_id auth_id
467 * @param integer group_id group_id
468 * @return boolean true if user is in the group, false otherwise
470 function liveuser_checkGroupUser($group_id, $auth_id)
472 global $liveuserPermAdmin;
474 if (is_numeric($auth_id) && is_numeric($group_id) &&
475 is_numeric($perm_id = liveuser_getPermUserId('user_id', $auth_id))) {
476 $groups = $liveuserPermAdmin->getGroups(array('where_user_id' => $perm_id, 'where_group_id' => $group_id));
477 return (is_array($groups) && !empty($groups));
481 /* Generates passwords for liveuser_gui and liveuser_addusers_gui*/
482 function liveuser_generate_password(){
483 $pwd = ''; // to store generated password
484 $len = rand(LW_PASSWORD_LEN_MIN, LW_PASSWORD_LEN_MAX); // password length
487 // generate random number sequence of ascii characters
488 for ($i = 0; $i < $len; $i++) {
489 $num = rand(48, 122);
491 if ($num >= ord('a') && $num <= ord('z')) {
493 } else if ($num >= ord('A') && $num <= ord('Z')) {
495 } else if ($num >= ord('0') && $num <= ord('9')) {
497 } else if ($num >= ord('#') && $num <= ord('&')) {
499 } else if ($num >= ord('?') && $num <= ord('@')) {
505 }while(ewiki_check_passwd($pwd)!="good passwd");
510 function ewiki_calc_complexity($passlen, $groups){
511 //checks for password complexity
512 return intval(log10(pow($groups,$passlen))/log10(2));
515 /*Checks to see if the password meets or exceeds a given complexity.
516 Checks to see if the password is based off of the user name.
517 Checks to see if the password is or contains a dictionary word.
519 function ewiki_check_passwd(&$password, $username=NULL, $skipdict=0){
520 $lcase_password=strtolower($password);
521 $username=strtolower($username);
523 $a_complexity['[a-z]']=26; //lowercase letters
524 $a_complexity['[A-Z]']=26; //uppercase letters
525 $a_complexity['[0-9]']=10; //numbers
526 $a_complexity['[()<>[\]{}!-.?,\'";:]']=17; //punctuation
527 $a_complexity['[@#$%^&*_=+\\/|`~ ]']=16; //misc symbols
529 $passlen=strlen($password);
530 //determine the what sets are contained in the password
531 foreach($a_complexity as $regex => $count){
532 //echo("checking $regex ");
533 if(preg_match("/".$regex."/", $password)){
534 //echo("found $regex ");
535 $possibilities+=$count;
539 $complexity=ewiki_calc_complexity($passlen, $possibilities);
540 if($complexity<EWIKI_PASSWORD_COMPLEXITY){
541 return("CHPW_BADNEW_COMPLEXITY");
544 if(strlen($username)){
545 //checks to see if the password is based on the user name
546 $dist = levenshtein($username, $lcase_password); //check forward
547 $rdist = levenshtein(strrev($username), $lcase_password); //check backward
548 if($dist<=LEVENSTIEN_MIN || strstr($lcase_password, $username) || strstr($lcase_password, strrev($username)) || $rdist<=LEVENSTIEN_MIN){
549 return("CHPW_BADNEW_USERNAME");
553 if(!$skipdict&&EWIKI_PASS_DICT_PATH){
554 //gets dictionary and dumps it into a string (fastest way to read a large file)
555 $dictionary=file_get_contents(EWIKI_PASS_DICT_PATH, 1);
557 //checks that the file was read properly
559 return("read error");
562 $chunk_length=EWIKI_MIN_DICT_WORD_LENGTH;
564 do{ //length of chunk
566 $chunks=$passlen-$chunk_length;
567 do{ //position of beginning of chunk
568 $subword=substr($password, $position, $chunk_length);
569 if(strpos($dictionary, "\n".strtolower($subword)."\n")){
570 $wordsfound[$subword]=strlen($subword);
572 } while(++$position<=$chunks);
573 } while(++$chunk_length<=$passlen);
575 unset($dictionary); //the dictionary is huge, no need to keep it anymore
577 if(isset($wordsfound)){
581 foreach($wordsfound as $word => $cred){
582 //echo("found $word ");
583 $password = str_replace($word, $cred, $password);
585 //echo(" calculating complexity of $password");
586 if(ewiki_calc_complexity(strlen($password),$possibilities)<EWIKI_PASSWORD_COMPLEXITY){
587 return ('CHPW_BADNEW_DICTIONARY');
592 //the password passed all the checks...its good enough...for now
593 return("good passwd");
598 // initializes default language to 'EN'
599 $liveuserPermAdmin->setCurrentLanguage(liveuser_addEntity('language', array('EN', 'English', 'English')));