2 rcs_id('$Id: WikiGroup.php,v 1.51 2004-11-27 14:39:04 rurban Exp $');
4 Copyright (C) 2003, 2004 $ThePhpWikiProgrammingTeam
6 This file is part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 if (!defined('GROUP_METHOD') or
24 !in_array(GROUP_METHOD,
25 array('NONE','WIKIPAGE','DB','FILE','LDAP')))
26 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
28 /* Special group names for ACL */
29 define('GROUP_EVERY', _("Every"));
30 define('GROUP_ANONYMOUS', _("Anonymous Users"));
31 define('GROUP_BOGOUSER', _("Bogo Users"));
32 define('GROUP_HASHOMEPAGE', _("HasHomePage"));
33 define('GROUP_SIGNED', _("Signed Users"));
34 define('GROUP_AUTHENTICATED', _("Authenticated Users"));
35 define('GROUP_ADMIN', _("Administrators"));
36 define('GROUP_OWNER', _("Owner"));
37 define('GROUP_CREATOR', _("Creator"));
40 * WikiGroup is an abstract class to provide the base functions for determining
41 * group membership for a specific user. Some functions are user independent.
43 * Limitation: For the current user only. This must be fixed to be able to query
44 * for membership of any user.
46 * WikiGroup is an abstract class with three functions:
47 * <ol><li />Provide the static method getGroup with will return the proper
49 * <li />Provide an interface for subclasses to implement.
50 * <li />Provide fallover methods (with error msgs) if not impemented in subclass.
52 * Do not ever instantiate this class. Use: $group = &WikiGroup::getGroup();
53 * This will instantiate the proper subclass.
55 * @author Joby Walker <zorloc@imperium.org>
61 /** User object if different from current user */
63 /** The global WikiRequest object */
65 /** Array of groups $username is confirmed to belong to */
67 /** boolean if not the current user */
68 var $not_current = false;
71 * Initializes a WikiGroup object which should never happen. Use:
72 * $group = &WikiGroup::getGroup();
73 * @param object $request The global WikiRequest object -- ignored.
75 function WikiGroup($not_current = false) {
76 $this->not_current = $not_current;
77 //$this->request =& $GLOBALS['request'];
81 * Gets the current username from the internal user object
82 * and erases $this->membership if is different than
83 * the stored $this->username
84 * @return string Current username.
86 function _getUserName(){
88 $user = (!empty($this->user)) ? $this->user : $request->getUser();
89 $username = $user->getID();
90 if ($username != $this->username) {
91 $this->membership = array();
92 $this->username = $username;
94 if (!$this->not_current)
100 * Static method to return the WikiGroup subclass used in this wiki. Controlled
101 * by the constant GROUP_METHOD.
102 * @param object $request The global WikiRequest object.
103 * @return object Subclass of WikiGroup selected via GROUP_METHOD.
105 function getGroup($not_current = false){
106 switch (GROUP_METHOD){
108 return new GroupNone($not_current);
111 return new GroupWikiPage($not_current);
114 if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
115 return new GroupDB_ADODB($not_current);
116 } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
117 return new GroupDb_PearDB($not_current);
119 trigger_error("GROUP_METHOD = DB: Unsupported dbtype "
120 . $GLOBALS['DBParams']['dbtype'],
125 return new GroupFile($not_current);
128 return new GroupLDAP($not_current);
131 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
132 return new WikiGroup($not_current);
136 /** ACL PagePermissions will need those special groups based on the User status only.
139 function specialGroup($group){
140 return in_array($group,$this->specialGroups());
143 function _specialGroup($group){
144 return in_array($group,$this->_specialGroups());
147 function specialGroups(){
159 function _specialGroups(){
172 * Determines if the current user is a member of a group.
174 * This method is an abstraction. The group is ignored, an error is sent, and
175 * false (not a member of the group) is returned.
176 * @param string $group Name of the group to check for membership (ignored).
177 * @return boolean True if user is a member, else false (always false).
179 function isMember($group){
180 if (isset($this->membership[$group]))
181 return $this->membership[$group];
182 if ($this->specialGroup($group)) {
183 return $this->isSpecialMember($group);
185 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
186 'isMember', GROUP_METHOD),
192 function isSpecialMember($group){
195 if (isset($this->membership[$group]))
196 return $this->membership[$group];
197 $user = (!empty($this->user)) ? $this->user : $request->getUser();
200 return $this->membership[$group] = true;
201 case GROUP_ANONYMOUS:
202 return $this->membership[$group] = ! $user->isSignedIn();
204 return $this->membership[$group] = (isa($user,'_BogoUser')
205 and $user->_level >= WIKIAUTH_BOGO);
207 return $this->membership[$group] = $user->isSignedIn();
208 case GROUP_AUTHENTICATED:
209 return $this->membership[$group] = $user->isAuthenticated();
211 return $this->membership[$group] = (isset($user->_level)
212 and $user->_level == WIKIAUTH_ADMIN);
214 trigger_error(__sprintf("Undefined method %s for special group %s",
222 * Determines all of the groups of which the current user is a member.
224 * This method is an abstraction. An error is sent and an empty
226 * @return array Array of groups to which the user belongs (always empty).
228 function getAllGroupsIn(){
229 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
230 'getAllGroupsIn', GROUP_METHOD),
235 function _allUsers() {
236 static $result = array();
241 /* WikiPage users: */
242 $dbh =& $request->_dbi;
243 $page_iter = $dbh->getAllPages();
245 while ($page = $page_iter->next()) {
246 if ($page->isUserPage())
247 $users[] = $page->_pagename;
250 /* WikiDB users from prefs (not from users): */
252 $dbi = _PassUser::getAuthDbh();
256 if ($dbi and $dbh->getAuthParam('pref_select')) {
258 $sql = preg_replace('/SELECT .+ FROM/i','SELECT userid FROM',
259 $dbh->getAuthParam('pref_select'));
260 //don't strip WHERE, only the userid stuff.
261 $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*["\']\$userid[\'"]/i','\\1 AND 1', $sql);
262 $sql = str_replace('WHERE AND 1','',$sql);
263 if (isa($dbi, 'ADOConnection')) {
264 $db_result = $dbi->Execute($sql);
265 foreach ($db_result->GetArray() as $u) {
266 $users = array_merge($users,array_values($u));
268 } elseif (isa($dbi, 'DB_common')) { // PearDB
269 $users = array_merge($users, $dbi->getCol($sql));
273 /* WikiDB users from users: */
274 // Fixme: don't strip WHERE, only the userid stuff.
275 if ($dbi and $dbh->getAuthParam('auth_user_exists')) {
276 //don't strip WHERE, only the userid stuff.
277 $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*["\']\$userid[\'"]/i','\\1 AND 1',
278 $dbh->getAuthParam('auth_user_exists'));
279 $sql = str_replace('WHERE AND 1','', $sql);
280 if (isa($dbi, 'ADOConnection')) {
281 $db_result = $dbi->Execute($sql);
282 foreach ($db_result->GetArray() as $u) {
283 $users = array_merge($users, array_values($u));
285 } elseif (isa($dbi, 'DB_common')) {
286 $users = array_merge($users, $dbi->getCol($sql));
290 // remove empty and duplicate users
292 foreach ($users as $u) {
293 if (empty($u) or in_array($u,$result)) continue;
300 * Determines all of the members of a particular group.
302 * This method is an abstraction. The group is ignored, an error is sent,
303 * and an empty array is returned
304 * @param string $group Name of the group to get the full membership list of.
305 * @return array Array of usernames that have joined the group (always empty).
307 function getMembersOf($group){
308 if ($this->specialGroup($group)) {
309 return $this->getSpecialMembersOf($group);
311 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
312 'getMembersOf', GROUP_METHOD),
317 function getSpecialMembersOf($group) {
318 //$request = &$this->request;
319 $all = $this->_allUsers();
324 case GROUP_ANONYMOUS:
327 foreach ($all as $u) {
328 if (isWikiWord($u)) $users[] = $u;
332 foreach ($all as $u) {
333 $user = WikiUser($u);
334 if ($user->isSignedIn()) $users[] = $u;
337 case GROUP_AUTHENTICATED:
338 foreach ($all as $u) {
339 $user = WikiUser($u);
340 if ($user->isAuthenticated()) $users[] = $u;
344 foreach ($all as $u) {
345 $user = WikiUser($u);
346 if (isset($user->_level) and $user->_level == WIKIAUTH_ADMIN)
351 trigger_error(__sprintf("Unknown special group '%s'", $group),
357 * Add the current or specified user to a group.
359 * This method is an abstraction. The group and user are ignored, an error
360 * is sent, and false (not added) is always returned.
361 * @param string $group User added to this group.
362 * @param string $user Username to add to the group (default = current user).
363 * @return bool On true user was added, false if not.
365 function setMemberOf($group, $user = false){
366 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
367 'setMemberOf', GROUP_METHOD),
373 * Remove the current or specified user to a group.
375 * This method is an abstraction. The group and user are ignored, and error
376 * is sent, and false (not removed) is always returned.
377 * @param string $group User removed from this group.
378 * @param string $user Username to remove from the group (default = current user).
379 * @return bool On true user was removed, false if not.
381 function removeMemberOf($group, $user = false){
382 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
383 'removeMemberOf', GROUP_METHOD),
390 * GroupNone disables all Group funtionality
392 * All of the GroupNone functions return false or empty values to indicate failure or
393 * no results. Use GroupNone if group controls are not desired.
394 * @author Joby Walker <zorloc@imperium.org>
396 class GroupNone extends WikiGroup{
401 * Ignores the parameter provided.
402 * @param object $request The global WikiRequest object - ignored.
404 function GroupNone() {
405 //$this->request = &$GLOBALS['request'];
410 * Determines if the current user is a member of a group.
412 * The group is ignored and false (not a member of the group) is returned.
413 * @param string $group Name of the group to check for membership (ignored).
414 * @return boolean True if user is a member, else false (always false).
416 function isMember($group){
417 if ($this->specialGroup($group)) {
418 return $this->isSpecialMember($group);
425 * Determines all of the groups of which the current user is a member.
427 * The group is ignored and an empty array (a member of no groups) is returned.
428 * @param string $group Name of the group to check for membership (ignored).
429 * @return array Array of groups to which the user belongs (always empty).
431 function getAllGroupsIn(){
436 * Determines all of the members of a particular group.
438 * The group is ignored and an empty array (a member of no groups) is returned.
439 * @param string $group Name of the group to check for membership (ignored).
440 * @return array Array of groups user belongs to (always empty).
442 function getMembersOf($group){
449 * GroupWikiPage provides group functionality via pages within the Wiki.
451 * GroupWikiPage is the Wiki way of managing a group. Every group will have
452 * a page. To modify the membership of the group, one only needs to edit the
453 * membership list on the page.
454 * @author Joby Walker <zorloc@imperium.org>
456 class GroupWikiPage extends WikiGroup{
461 * Initializes the three superclass instance variables
462 * @param object $request The global WikiRequest object.
464 function GroupWikiPage() {
465 //$this->request = &$GLOBALS['request'];
466 $this->username = $this->_getUserName();
467 //$this->username = null;
468 $this->membership = array();
472 * Determines if the current user is a member of a group.
474 * To determine membership in a particular group, this method checks the
475 * superclass instance variable $membership to see if membership has
476 * already been determined. If not, then the group page is parsed to
477 * determine membership.
478 * @param string $group Name of the group to check for membership.
479 * @return boolean True if user is a member, else false.
481 function isMember($group){
482 if (isset($this->membership[$group])) {
483 return $this->membership[$group];
486 $group_page = $request->getPage($group);
487 if ($this->_inGroupPage($group_page)) {
488 $this->membership[$group] = true;
491 $this->membership[$group] = false;
492 // Let grouppages override certain defaults, such as members of admin
493 if ($this->specialGroup($group)) {
494 return $this->isSpecialMember($group);
500 * Private method to take a WikiDB_Page and parse to determine if the
501 * current_user is a member of the group.
502 * @param object $group_page WikiDB_Page object for the group's page
503 * @return boolean True if user is a member, else false.
506 function _inGroupPage($group_page, $strict=false){
507 $group_revision = $group_page->getCurrentRevision();
508 if ($group_revision->hasDefaultContents()) {
509 $group = $group_page->getName();
510 if ($strict) trigger_error(sprintf(_("Group page '%s' does not exist"), $group),
514 $contents = $group_revision->getContent();
515 $match = '/^\s*[\*\#]+\s*\[?' . $this->username . '\]?\s*$/';
516 foreach ($contents as $line){
517 if (preg_match($match, $line)) {
525 * Determines all of the groups of which the current user is a member.
527 * Checks the root Group page ('CategoryGroup') for the list of all groups,
528 * then checks each group to see if the current user is a member.
529 * @param string $group Name of the group to check for membership.
530 * @return array Array of groups to which the user belongs.
532 function getAllGroupsIn(){
533 $membership = array();
535 $specialgroups = $this->specialGroups();
536 foreach ($specialgroups as $group) {
537 $this->membership[$group] = $this->isSpecialMember($group);
541 $dbh = &$request->_dbi;
542 $master_page = $request->getPage(CATEGORY_GROUP_PAGE);
543 $master_list = $master_page->getLinks(true);
544 while ($group_page = $master_list->next()){
545 $group = $group_page->getName();
546 $this->membership[$group] = $this->_inGroupPage($group_page);
548 foreach ($this->membership as $g => $bool) {
549 if ($bool) $membership[] = $g;
555 * Determines all of the members of a particular group.
557 * Checks a group's page to return all the current members. Currently this
558 * method is disabled and triggers an error and returns an empty array.
559 * @param string $group Name of the group to get the full membership list of.
560 * @return array Array of usernames that have joined the group (always empty).
562 function getMembersOf($group){
563 if ($this->specialGroup($group))
564 return $this->getSpecialMembersOf($group);
566 $group_page = $GLOBALS['request']->getPage($group);
567 $group_revision = $group_page->getCurrentRevision();
568 if ($group_revision->hasDefaultContents()) {
569 trigger_error(sprintf(_("Group %s does not exist"),$group), E_USER_WARNING);
572 $contents = $group_revision->getContent();
573 // This is not really a reliable way to check if a string is a username. But better than nothing.
574 $match = '/^(\s*[\*\#]+\s*\[?)(\w+)(\]?\s*)$/';
576 foreach ($contents as $line){
577 if (preg_match($match, $line, $matches)){
578 $members[] = $matches[2];
586 * GroupDb is configured by $DbAuthParams[] statements
591 class GroupDb extends WikiGroup {
593 var $_is_member, $_group_members, $_user_groups;
598 * @param object $request The global WikiRequest object. ignored
601 global $DBAuthParams, $DBParams;
602 //$this->request = &$GLOBALS['request'];
603 $this->username = $this->_getUserName();
604 $this->membership = array();
606 if (empty($DBAuthParams['group_members']) or
607 empty($DBAuthParams['user_groups']) or
608 empty($DBAuthParams['is_member'])) {
609 trigger_error(_("No or not enough GROUP_DB SQL statements defined"),
611 return new GroupNone();
613 // FIXME: This only works with ENABLE_USER_NEW
614 if (empty($this->user)) {
615 // use _PassUser::prepare instead
616 if (isa($request->getUser(),'_PassUser'))
617 $user = $request->getUser();
619 $user = new _PassUser($this->username);
620 } elseif (!isa($this->user, '_PassUser')) {
621 $user = new _PassUser($this->username);
623 $user =& $this->user;
625 if (isa($this->user, '_PassUser')) { // TODO: safety by Charles Corrigan
626 $this->_is_member = $user->prepare($DBAuthParams['is_member'],
627 array('userid','groupname'));
628 $this->_group_members = $user->prepare($DBAuthParams['group_members'],'groupname');
629 $this->_user_groups = $user->prepare($DBAuthParams['user_groups'],'userid');
630 $this->dbh = $user->_auth_dbi;
640 class GroupDb_PearDB extends GroupDb {
643 * Determines if the current user is a member of a database group.
645 * @param string $group Name of the group to check for membership.
646 * @return boolean True if user is a member, else false.
648 function isMember($group) {
649 if (isset($this->membership[$group])) {
650 return $this->membership[$group];
653 $db_result = $dbh->query(sprintf($this->_is_member,
654 $dbh->quote($this->username),
655 $dbh->quote($group)));
656 if ($db_result->numRows() > 0) {
657 $this->membership[$group] = true;
660 $this->membership[$group] = false;
661 // Let override certain defaults, such as members of admin
662 if ($this->specialGroup($group))
663 return $this->isSpecialMember($group);
668 * Determines all of the groups of which the current user is a member.
670 * then checks each group to see if the current user is a member.
671 * @param string $group Name of the group to check for membership.
672 * @return array Array of groups to which the user belongs.
674 function getAllGroupsIn(){
675 $membership = array();
678 $db_result = $dbh->query(sprintf($this->_user_groups, $dbh->quote($this->username)));
679 if ($db_result->numRows() > 0) {
680 while (list($group) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
681 $membership[] = $group;
682 $this->membership[$group] = true;
686 $specialgroups = $this->specialGroups();
687 foreach ($specialgroups as $group) {
688 if ($this->isMember($group)) {
689 $membership[] = $group;
696 * Determines all of the members of a particular group.
698 * Checks a group's page to return all the current members. Currently this
699 * method is disabled and triggers an error and returns an empty array.
700 * @param string $group Name of the group to get the full membership list of.
701 * @return array Array of usernames that have joined the group.
703 function getMembersOf($group){
707 $db_result = $dbh->query(sprintf($this->_group_members,$dbh->quote($group)));
708 if ($db_result->numRows() > 0) {
709 while (list($userid) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
710 $members[] = $userid;
713 // add certain defaults, such as members of admin
714 if ($this->specialGroup($group))
715 $members = array_merge($members, $this->getSpecialMembersOf($group));
725 class GroupDb_ADODB extends GroupDb {
728 * Determines if the current user is a member of a database group.
730 * @param string $group Name of the group to check for membership.
731 * @return boolean True if user is a member, else false.
733 function isMember($group) {
734 if (isset($this->membership[$group])) {
735 return $this->membership[$group];
738 $rs = $dbh->Execute(sprintf($this->_is_member,$dbh->qstr($this->username),
739 $dbh->qstr($group)));
743 if ($rs->numRows() > 0) {
744 $this->membership[$group] = true;
749 $this->membership[$group] = false;
750 if ($this->specialGroup($group))
751 return $this->isSpecialMember($group);
757 * Determines all of the groups of which the current user is a member.
758 * then checks each group to see if the current user is a member.
760 * @param string $group Name of the group to check for membership.
761 * @return array Array of groups to which the user belongs.
763 function getAllGroupsIn(){
764 $membership = array();
767 $rs = $dbh->Execute(sprintf($this->_user_groups, $dbh->qstr($this->username)));
768 if (!$rs->EOF and $rs->numRows() > 0) {
770 $group = reset($rs->fields);
771 $membership[] = $group;
772 $this->membership[$group] = true;
778 $specialgroups = $this->specialGroups();
779 foreach ($specialgroups as $group) {
780 if ($this->isMember($group)) {
781 $membership[] = $group;
788 * Determines all of the members of a particular group.
790 * @param string $group Name of the group to get the full membership list of.
791 * @return array Array of usernames that have joined the group.
793 function getMembersOf($group){
796 $rs = $dbh->Execute(sprintf($this->_group_members,$dbh->qstr($group)));
797 if (!$rs->EOF and $rs->numRows() > 0) {
799 $members[] = reset($rs->fields);
804 // add certain defaults, such as members of admin
805 if ($this->specialGroup($group))
806 $members = array_merge($members, $this->getSpecialMembersOf($group));
812 * GroupFile is configured by AUTH_GROUP_FILE
813 * groupname: user1 user2 ...
817 class GroupFile extends WikiGroup {
822 * @param object $request The global WikiRequest object.
824 function GroupFile(){
825 //$this->request = &$GLOBALS['request'];
826 $this->username = $this->_getUserName();
827 //$this->username = null;
828 $this->membership = array();
830 if (!defined('AUTH_GROUP_FILE')) {
831 trigger_error(_("AUTH_GROUP_FILE not defined"), E_USER_WARNING);
834 if (!file_exists(AUTH_GROUP_FILE)) {
835 trigger_error(sprintf(_("Cannot open AUTH_GROUP_FILE %s"), AUTH_GROUP_FILE),
839 require_once('lib/pear/File_Passwd.php');
840 $this->_file = new File_Passwd(AUTH_GROUP_FILE,false,AUTH_GROUP_FILE.".lock");
844 * Determines if the current user is a member of a group.
846 * To determine membership in a particular group, this method checks the
847 * superclass instance variable $membership to see if membership has
848 * already been determined. If not, then the group file is parsed to
849 * determine membership.
850 * @param string $group Name of the group to check for membership.
851 * @return boolean True if user is a member, else false.
853 function isMember($group) {
854 //$request = $this->request;
855 //$username = $this->username;
856 if (isset($this->membership[$group])) {
857 return $this->membership[$group];
860 if (is_array($this->_file->users)) {
861 foreach ($this->_file->users as $g => $u) {
862 $users = explode(' ',$u);
863 if (in_array($this->username,$users)) {
864 $this->membership[$group] = true;
869 $this->membership[$group] = false;
870 if ($this->specialGroup($group))
871 return $this->isSpecialMember($group);
876 * Determines all of the groups of which the current user is a member.
878 * then checks each group to see if the current user is a member.
879 * @param string $group Name of the group to check for membership.
880 * @return array Array of groups to which the user belongs.
882 function getAllGroupsIn(){
883 //$username = $this->_getUserName();
884 $membership = array();
886 if (is_array($this->_file->users)) {
887 foreach ($this->_file->users as $group => $u) {
888 $users = explode(' ',$u);
889 if (in_array($this->username,$users)) {
890 $this->membership[$group] = true;
891 $membership[] = $group;
896 $specialgroups = $this->specialGroups();
897 foreach ($specialgroups as $group) {
898 if ($this->isMember($group)) {
899 $this->membership[$group] = true;
900 $membership[] = $group;
907 * Determines all of the members of a particular group.
909 * Return all the current members.
910 * @param string $group Name of the group to get the full membership list of.
911 * @return array Array of usernames that have joined the group.
913 function getMembersOf($group){
915 if (!empty($this->_file->users[$group])) {
916 $members = explode(' ',$this->_file->users[$group]);
918 if ($this->specialGroup($group)) {
919 $members = array_merge($members, $this->getSpecialMembersOf($group));
926 * Ldap is configured in index.php
930 class GroupLdap extends WikiGroup {
935 * @param object $request The global WikiRequest object.
937 function GroupLdap(){
938 //$this->request = &$GLOBALS['request'];
939 $this->username = $this->_getUserName();
940 $this->membership = array();
942 if (!defined("LDAP_AUTH_HOST")) {
943 trigger_error(_("LDAP_AUTH_HOST not defined"), E_USER_WARNING);
946 // We should ignore multithreaded environments, not generally windows.
948 if (! function_exists('ldap_connect') and (!isWindows() or isCGI())) {
949 // on MacOSX >= 4.3 you'll need PHP_SHLIB_SUFFIX instead.
950 dl("ldap".defined('PHP_SHLIB_SUFFIX') ? PHP_SHLIB_SUFFIX : DLL_EXT);
951 if (! function_exists('ldap_connect')) {
952 trigger_error(_("No LDAP in this PHP version"), E_USER_WARNING);
956 if (!defined("LDAP_BASE_DN"))
957 define("LDAP_BASE_DN",'');
958 $this->base_dn = LDAP_BASE_DN;
959 // if no users ou (organizational unit) is defined,
960 // then take out the ou= from the base_dn (if exists) and append a default
961 // from users and group
963 if (strstr(LDAP_BASE_DN, "ou="))
964 $this->base_dn = preg_replace("/(ou=\w+,)?()/","\$2", LDAP_BASE_DN);
966 if (!isset($this->user) or !isa($this->user, '_LDAPPassUser'))
967 $this->_user = new _LDAPPassUser('LdapGroupTest'); // to have a valid username
969 $this->_user =& $this->user;
973 * Determines if the current user is a member of a group.
976 * @param string $group Name of the group to check for membership.
977 * @return boolean True if user is a member, else false.
979 function isMember($group) {
980 if (isset($this->membership[$group])) {
981 return $this->membership[$group];
983 //$request = $this->request;
984 //$username = $this->_getUserName();
985 $this->membership[$group] = in_array($this->username,$this->getMembersOf($group));
986 if ($this->membership[$group])
988 if ($this->specialGroup($group))
989 return $this->isSpecialMember($group);
993 * Determines all of the groups of which the current user is a member.
995 * @param string $group Name of the group to check for membership.
996 * @return array Array of groups to which the user belongs.
998 function getAllGroupsIn(){
999 //$request = &$this->request;
1000 //$username = $this->_getUserName();
1001 $membership = array();
1003 $specialgroups = $this->specialGroups();
1004 foreach ($specialgroups as $group) {
1005 if ($this->isMember($group)) {
1006 $this->membership[$group] = true;
1007 $membership[] = $group;
1011 // must be a valid LDAP server, and username must not contain a wildcard
1012 if ($ldap = $this->_user->_init()) {
1013 $st_search = LDAP_SEARCH_FIELD ? LDAP_SEARCH_FIELD."=".$this->username
1014 : "uid=".$this->username;
1015 $sr = ldap_search($ldap, (LDAP_OU_USERS ? LDAP_OU_USERS : "ou=Users")
1016 .($this->base_dn ? ",".$this->base_dn : ''),
1019 $this->_user->_free();
1020 return $this->membership;
1022 $info = ldap_get_entries($ldap, $sr);
1023 if (empty($info["count"])) {
1024 $this->_user->_free();
1025 return $this->membership;
1027 for ($i = 0; $i < $info["count"]; $i++) {
1028 if ($info[$i]["gidNumber"]["count"]) {
1029 $gid = $info[$i]["gidnumber"][0];
1030 $sr2 = ldap_search($ldap, (LDAP_OU_GROUP ? LDAP_OU_GROUP : "ou=Groups")
1031 .($this->base_dn ? ",".$this->base_dn : ''),
1034 $info2 = ldap_get_entries($ldap, $sr2);
1035 if (!empty($info2["count"]))
1036 $membership[] = $info2[0]["cn"][0];
1041 trigger_error(fmt("Unable to connect to LDAP server %s", LDAP_AUTH_HOST),
1044 $this->_user->_free();
1045 //ldap_close($ldap);
1046 $this->membership = $membership;
1051 * Determines all of the members of a particular group.
1053 * Return all the members of the given group. LDAP just returns the gid of each user
1054 * @param string $group Name of the group to get the full membership list of.
1055 * @return array Array of usernames that have joined the group.
1057 function getMembersOf($group){
1059 if ($ldap = $this->_user->_init()) {
1060 $base_dn = (LDAP_OU_GROUP ? LDAP_OU_GROUP : "ou=Groups")
1061 .($this->base_dn ? ",".$this->base_dn : '');
1062 $sr = ldap_search($ldap, $base_dn, "cn=$group");
1064 $info = ldap_get_entries($ldap, $sr);
1066 $info = array('count' => 0);
1067 trigger_error("LDAP_SEARCH: base=\"$base_dn\" \"(cn=$group)\" failed", E_USER_NOTICE);
1069 $base_dn = (LDAP_OU_USERS ? LDAP_OU_USERS : "ou=Users")
1070 .($this->base_dn ? ",".$this->base_dn : '');
1071 for ($i = 0; $i < $info["count"]; $i++) {
1072 $gid = $info[$i]["gidNumber"][0];
1073 //uid=* would be better probably
1074 $sr2 = ldap_search($ldap, $base_dn, "gidNumber=$gid");
1076 $info2 = ldap_get_entries($ldap, $sr2);
1077 for ($j = 0; $j < $info2["count"]; $j++) {
1078 $members[] = $info2[$j]["cn"][0];
1081 trigger_error("LDAP_SEARCH: base=\"$base_dn\" \"(gidNumber=$gid)\" failed", E_USER_NOTICE);
1085 $this->_user->_free();
1086 //ldap_close($ldap);
1088 if ($this->specialGroup($group)) {
1089 $members = array_merge($members, $this->getSpecialMembersOf($group));
1095 // $Log: not supported by cvs2svn $
1096 // Revision 1.50 2004/11/24 18:58:41 rurban
1099 // Revision 1.49 2004/11/23 13:06:30 rurban
1100 // several fixes and suggestions by Charles Corrigan:
1101 // * fix GROUP_BOGO_USER check
1102 // * allow group pages to have the link to the user page in [ ] brackets
1103 // * fix up the implementation of GroupWikiPage::getMembersOf and allow the
1104 // user page to be linked in [ ] brackets
1105 // * added _OWNER and _CREATOR to special wikigroups
1106 // * check against those two for group membership also, not only the user.
1108 // Revision 1.48 2004/11/19 19:22:03 rurban
1109 // ModeratePage part1: change status
1111 // Revision 1.47 2004/11/19 13:23:47 rurban
1113 // Another catch by Charles Corrigan: check against the dbi backend, not the WikiDB class.
1115 // Revision 1.46 2004/11/18 09:52:23 rurban
1116 // more safety, requested by Charles Corrigan
1118 // Revision 1.45 2004/11/17 20:06:30 rurban
1119 // possible group fix
1121 // Revision 1.44 2004/11/11 10:31:26 rurban
1122 // Disable default options in config-dist.ini
1123 // Add new CATEGORY_GROUP_PAGE root page: Default: Translation of "CategoryGroup"
1124 // Clarify more options.
1126 // Revision 1.43 2004/11/10 15:29:21 rurban
1127 // * requires newer Pear_DB (as the internal one): quote() uses now escapeSimple for strings
1128 // * ACCESS_LOG_SQL: fix cause request not yet initialized
1129 // * WikiDB: moved SQL specific methods upwards
1130 // * new Pear_DB quoting: same as ADODB and as newer Pear_DB.
1131 // fixes all around: WikiGroup, WikiUserNew SQL methods, SQL logging
1133 // Revision 1.42 2004/11/01 10:43:56 rurban
1134 // seperate PassUser methods into seperate dir (memory usage)
1135 // fix WikiUser (old) overlarge data session
1136 // remove wikidb arg from various page class methods, use global ->_dbi instead
1139 // Revision 1.41 2004/09/17 14:21:28 rurban
1140 // fix LDAP ou= issue, wrong strstr arg order
1142 // Revision 1.40 2004/06/29 06:48:02 rurban
1143 // Improve LDAP auth and GROUP_LDAP membership:
1144 // no error message on false password,
1145 // added two new config vars: LDAP_OU_USERS and LDAP_OU_GROUP with GROUP_METHOD=LDAP
1146 // fixed two group queries (this -> user)
1147 // stdlib: ConvertOldMarkup still flawed
1149 // Revision 1.39 2004/06/28 15:39:28 rurban
1150 // fixed endless recursion in WikiGroup: isAdmin()
1152 // Revision 1.38 2004/06/27 10:24:19 rurban
1153 // suggestion by Paul Henry
1155 // Revision 1.37 2004/06/25 14:29:18 rurban
1156 // WikiGroup refactoring:
1157 // global group attached to user, code for not_current user.
1158 // improved helpers for special groups (avoid double invocations)
1159 // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1160 // fixed a XHTML validation error on userprefs.tmpl
1162 // Revision 1.36 2004/06/16 13:21:05 rurban
1163 // stabilize on failing ldap queries or bind
1165 // Revision 1.35 2004/06/16 12:08:25 rurban
1168 // Revision 1.34 2004/06/16 11:51:35 rurban
1169 // catch dl error on Windows
1171 // Revision 1.33 2004/06/15 10:40:35 rurban
1172 // minor WikiGroup cleanup: no request param, start of current user independency
1174 // Revision 1.32 2004/06/15 09:15:52 rurban
1175 // IMPORTANT: fixed passwd handling for passwords stored in prefs:
1176 // fix encrypted usage, actually store and retrieve them from db
1177 // fix bogologin with passwd set.
1178 // fix php crashes with call-time pass-by-reference (references wrongly used
1179 // in declaration AND call). This affected mainly Apache2 and IIS.
1180 // (Thanks to John Cole to detect this!)
1182 // Revision 1.31 2004/06/03 18:06:29 rurban
1183 // fix file locking issues (only needed on write)
1184 // fixed immediate LANG and THEME in-session updates if not stored in prefs
1185 // advanced editpage toolbars (search & replace broken)
1187 // Revision 1.30 2004/06/03 09:39:51 rurban
1188 // fix LDAP injection (wildcard in username) detected by Steve Christey, MITRE
1190 // Revision 1.29 2004/05/16 22:07:35 rurban
1191 // check more config-default and predefined constants
1192 // various PagePerm fixes:
1193 // fix default PagePerms, esp. edit and view for Bogo and Password users
1194 // implemented Creator and Owner
1195 // BOGOUSERS renamed to BOGOUSER
1196 // fixed syntax errors in signin.tmpl
1198 // Revision 1.28 2004/05/15 22:54:49 rurban
1199 // fixed important WikiDB bug with DEBUG > 0: wrong assertion
1200 // improved SetAcl (works) and PagePerms, some WikiGroup helpers.
1202 // Revision 1.27 2004/05/06 13:56:40 rurban
1203 // Enable the Administrators group, and add the WIKIPAGE group default root page.
1205 // Revision 1.26 2004/04/07 23:13:18 rurban
1206 // fixed pear/File_Passwd for Windows
1207 // fixed FilePassUser sessions (filehandle revive) and password update
1209 // Revision 1.25 2004/03/29 10:40:36 rurban
1210 // GroupDb_PearDB fetchmode fix
1212 // Revision 1.24 2004/03/14 16:26:22 rurban
1215 // Revision 1.23 2004/03/12 23:20:58 rurban
1216 // pref fixes (base64)
1218 // Revision 1.22 2004/03/12 15:48:07 rurban
1219 // fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1220 // simplified lib/stdlib.php:explodePageList
1222 // Revision 1.21 2004/03/12 11:18:24 rurban
1223 // fixed ->membership chache
1225 // Revision 1.20 2004/03/12 10:47:30 rurban
1226 // fixed GroupDB for ADODB
1228 // Revision 1.19 2004/03/11 16:27:30 rurban
1229 // fixed GroupFile::getMembersOf for special groups
1230 // added authenticated bind for GroupLdap (Windows AD) as in WikiUserNew
1232 // Revision 1.18 2004/03/11 13:30:47 rurban
1233 // fixed File Auth for user and group
1234 // missing only getMembersOf(Authenticated Users),getMembersOf(Every),getMembersOf(Signed Users)
1236 // Revision 1.17 2004/03/10 15:38:48 rurban
1237 // store current user->page and ->action in session for WhoIsOnline
1238 // better WhoIsOnline icon
1239 // fixed WhoIsOnline warnings
1241 // Revision 1.15 2004/03/09 12:11:57 rurban
1242 // prevent from undefined DBAuthParams warning
1244 // Revision 1.14 2004/03/08 19:30:01 rurban
1245 // fixed Theme->getButtonURL
1246 // AllUsers uses now WikiGroup (also DB User and DB Pref users)
1247 // PageList fix for empty pagenames
1249 // Revision 1.13 2004/03/08 18:17:09 rurban
1250 // added more WikiGroup::getMembersOf methods, esp. for special groups
1251 // fixed $LDAP_SET_OPTIONS
1252 // fixed _AuthInfo group methods
1254 // Revision 1.12 2004/02/23 21:30:25 rurban
1255 // more PagePerm stuff: (working against 1.4.0)
1256 // ACL editing and simplification of ACL's to simple rwx------ string
1259 // Revision 1.11 2004/02/07 10:41:25 rurban
1260 // fixed auth from session (still double code but works)
1262 // fixed DbPassUser upgrade and policy=old
1265 // Revision 1.10 2004/02/03 09:45:39 rurban
1266 // LDAP cleanup, start of new Pref classes
1268 // Revision 1.9 2004/02/01 09:14:11 rurban
1269 // Started with Group_Ldap (not yet ready)
1270 // added new _AuthInfo plugin to help in auth problems (warning: may display passwords)
1271 // fixed some configurator vars
1272 // renamed LDAP_AUTH_SEARCH to LDAP_BASE_DN
1273 // changed PHPWIKI_VERSION from 1.3.8a to 1.3.8pre
1274 // USE_DB_SESSION defaults to true on SQL
1275 // changed GROUP_METHOD definition to string, not constants
1276 // changed sample user DBAuthParams from UPDATE to REPLACE to be able to
1277 // create users. (Not to be used with external databases generally, but
1278 // with the default internal user table)
1280 // fixed the IndexAsConfigProblem logic. this was flawed:
1281 // scripts which are the same virtual path defined their own lib/main call
1282 // (hmm, have to test this better, phpwiki.sf.net/demo works again)
1284 // Revision 1.8 2004/01/27 23:23:39 rurban
1285 // renamed ->Username => _userid for consistency
1286 // renamed mayCheckPassword => mayCheckPass
1287 // fixed recursion problem in WikiUserNew
1288 // fixed bogo login (but not quite 100% ready yet, password storage)
1290 // Revision 1.7 2004/01/26 16:52:40 rurban
1291 // added GroupDB and GroupFile classes
1293 // Revision 1.6 2003/12/07 19:29:11 carstenklapp
1294 // Code Housecleaning: fixed syntax errors. (php -l *.php)
1296 // Revision 1.5 2003/02/22 20:49:55 dairiki
1297 // Fixes for "Call-time pass by reference has been deprecated" errors.
1299 // Revision 1.4 2003/01/21 04:02:39 zorloc
1300 // Added Log entry and page footer.
1306 // c-basic-offset: 4
1307 // c-hanging-comment-ender-p: nil
1308 // indent-tabs-mode: nil