]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiGroup.php
fix LDAP injection (wildcard in username) detected by Steve Christey, MITRE
[SourceForge/phpwiki.git] / lib / WikiGroup.php
1 <?php
2 rcs_id('$Id: WikiGroup.php,v 1.30 2004-06-03 09:39:51 rurban Exp $');
3 /*
4  Copyright (C) 2003, 2004 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
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.
12
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.
17
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
21  */
22
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);
27     
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"));
38
39 /**
40  * WikiGroup is an abstract class to provide the base functions for determining
41  * group membership. 
42  * Limitation: For the current user only.
43  * 
44  * WikiGroup is an abstract class with three functions:
45  * <ol><li />Provide the static method getGroup with will return the proper
46  *         subclass.
47  *     <li />Provide an interface for subclasses to implement.
48  *     <li />Provide fallover methods (with error msgs) if not impemented in subclass.
49  * </ol>
50  * Do not ever instantiate this class use: $group = &WikiGroup::getGroup($request);
51  * This will instantiate the proper subclass.
52  *
53  * @author Joby Walker <zorloc@imperium.org>
54  * @author Reini Urban
55  */ 
56 class WikiGroup{
57     /** User name */
58     var $username = '';
59     /** The global WikiRequest object */
60     var $request;
61     /** Array of groups $username is confirmed to belong to */
62     var $membership;
63     
64     /**
65      * Initializes a WikiGroup object which should never happen.  Use:
66      * $group = &WikiGroup::getGroup($request);
67      * @param object $request The global WikiRequest object -- ignored.
68      */ 
69     function WikiGroup(&$request){    
70         $this->request = $request;
71     }
72
73     /**
74      * Gets the current username and erases $this->membership if is different than
75      * the stored $this->username
76      * @return string Current username.
77      */ 
78     function _getUserName(){
79         $request = &$this->request;
80         $user = $request->getUser();
81         $username = $user->getID();
82         if ($username != $this->username) {
83             $this->membership = array();
84             $this->username = $username;
85         }
86         return $username;
87     }
88     
89     /**
90      * Static method to return the WikiGroup subclass used in this wiki.  Controlled
91      * by the constant GROUP_METHOD.
92      * @param object $request The global WikiRequest object.
93      * @return object Subclass of WikiGroup selected via GROUP_METHOD.
94      */ 
95     function getGroup($request){
96         switch (GROUP_METHOD){
97             case "NONE": 
98                 return new GroupNone($request);
99                 break;
100             case "WIKIPAGE":
101                 return new GroupWikiPage($request);
102                 break;
103             case "DB":
104                 if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
105                     return new GroupDB_ADODB($request);
106                 } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
107                     return new GroupDb_PearDB($request);
108                 } else {
109                     trigger_error("GROUP_METHOD = DB: Unsupported dbtype " . $DBParams['dbtype'],
110                                   E_USER_ERROR);
111                 }
112                 break;
113             case "FILE": 
114                 return new GroupFile($request);
115                 break;
116             case "LDAP": 
117                 return new GroupLDAP($request);
118                 break;
119             default:
120                 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
121                 return new WikiGroup($request);
122         }
123     }
124
125     /* ACL PagePermissions will need those special groups based on the User status only */
126     function specialGroup($group){
127         return in_array($group,$this->specialGroups());
128     }
129     function _specialGroup($group){
130         return in_array($group,$this->_specialGroups());
131     }
132
133     function specialGroups(){
134         return array(
135                      GROUP_EVERY,
136                      GROUP_ANONYMOUS,
137                      GROUP_BOGOUSER,
138                      GROUP_SIGNED,
139                      GROUP_AUTHENTICATED,
140                      GROUP_ADMIN);
141     }
142     function _specialGroups(){
143         return array(
144                      "_EVERY",
145                      "_ANONYMOUS",
146                      "_BOGOUSER",
147                      "_SIGNED",
148                      "_AUTHENTICATED",
149                      "_ADMIN");
150     }
151
152     /**
153      * Determines if the current user is a member of a group.
154      * 
155      * This method is an abstraction.  The group is ignored, an error is sent, and
156      * false (not a member of the group) is returned.
157      * @param string $group Name of the group to check for membership (ignored).
158      * @return boolean True if user is a member, else false (always false).
159      */ 
160     function isMember($group){
161         if (isset($this->membership[$group]))
162             return $this->membership[$group];
163         if ($this->specialGroup($group)) {
164             $request = &$this->request;
165             $user = $request->getUser();
166             switch ($group) {
167             case GROUP_EVERY:           return $this->membership[$group] = true;
168             case GROUP_ANONYMOUS:       return $this->membership[$group] = ! $user->isSignedIn();
169             case GROUP_BOGOUSER:        return $this->membership[$group] = (isa($user,'_BogoUser') and 
170                                                                             $user->_level >= WIKIAUTH_BOGO);
171             case GROUP_SIGNED:          return $this->membership[$group] = $user->isSignedIn();
172             case GROUP_AUTHENTICATED:   return $this->membership[$group] = $user->isAuthenticated();
173             case GROUP_ADMIN:           return $this->membership[$group] = $user->isAdmin();
174             default:
175                 trigger_error(__sprintf("Undefined method %s for special group %s",
176                                         'isMember',$group),
177                               E_USER_WARNING);
178             }
179         } else {
180             trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
181                                     'isMember',GROUP_METHOD),
182                           E_USER_WARNING);
183         }
184         return false;
185     }
186     
187     /**
188      * Determines all of the groups of which the current user is a member.
189      * 
190      * This method is an abstraction.  An error is sent and an empty 
191      * array is returned.
192      * @return array Array of groups to which the user belongs (always empty).
193      */ 
194     function getAllGroupsIn(){
195         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
196                                 'getAllGroupsIn',GROUP_METHOD),
197                       E_USER_WARNING);
198         return array();
199     }
200
201     function _allUsers() {
202         static $result = array();
203         if (!empty($result))
204                 return $result;
205                 
206         /* WikiPage users: */
207         $dbh = $this->request->getDbh();
208         $page_iter = $dbh->getAllPages();
209         $users = array();
210         while ($page = $page_iter->next()) {
211             if ($page->isUserPage())
212                 $users[] = $page->_pagename;
213         }
214
215         /* WikiDB users from prefs (not from users): */
216         $dbi = _PassUser::getAuthDbh();
217         if ($dbi and !empty($GLOBALS['DBAuthParams']['pref_select'])) {
218             //get prefs table
219             $sql = preg_replace('/SELECT .+ FROM/i','SELECT userid FROM',$GLOBALS['DBAuthParams']['pref_select']);
220             //don't strip WHERE, only the userid stuff.
221             $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*"\$userid"/i','\\1 AND 1',$sql);
222             $sql = str_replace('WHERE AND 1','',$sql);
223             if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
224                 $db_result = $dbi->Execute($sql);
225                 foreach ($db_result->GetArray() as $u) {
226                    $users = array_merge($users,array_values($u));
227                 }
228             } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
229                 $users = array_merge($users,$dbi->getCol($sql));
230             }
231         }
232
233         /* WikiDB users from users: */
234         // Fixme: don't strip WHERE, only the userid stuff.
235         if ($dbi and !empty($GLOBALS['DBAuthParams']['auth_user_exists'])) {
236             //don't strip WHERE, only the userid stuff.
237             $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*"\$userid"/i','\\1 AND 1',$GLOBALS['DBAuthParams']['auth_user_exists']);
238             $sql = str_replace('WHERE AND 1','',$sql);
239             if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
240                 $db_result = $dbi->Execute($sql);
241                 foreach ($db_result->GetArray() as $u) {
242                    $users = array_merge($users,array_values($u));
243                 }
244             } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
245                 $users = array_merge($users,$dbi->getCol($sql));
246             }
247         }
248
249         // remove empty and duplicate users
250         $result = array();
251         foreach ($users as $u) {
252             if (empty($u) or in_array($u,$result)) continue;
253             $result[] = $u;
254         }
255         return $result;
256     }
257
258     /**
259      * Determines all of the members of a particular group.
260      * 
261      * This method is an abstraction.  The group is ignored, an error is sent, 
262      * and an empty array is returned
263      * @param string $group Name of the group to get the full membership list of.
264      * @return array Array of usernames that have joined the group (always empty).
265      */ 
266     function getMembersOf($group){
267         if ($this->specialGroup($group)) {
268             //$request = &$this->request;
269             $all = $this->_allUsers();
270             $users = array();
271             switch ($group) {
272             case GROUP_EVERY:           
273                 return $all;
274             case GROUP_ANONYMOUS:       
275                 return $users;
276             case GROUP_BOGOUSER:
277                 foreach ($all as $u) {
278                     if (isWikiWord($user)) $users[] = $u;
279                 }
280                 return $users;
281             case GROUP_SIGNED:          
282                 foreach ($all as $u) {
283                     $user = WikiUser($u);
284                     if ($user->isSignedIn()) $users[] = $u;
285                 }
286                 return $users;
287             case GROUP_AUTHENTICATED:
288                 foreach ($all as $u) {
289                     $user = WikiUser($u);
290                     if ($user->isAuthenticated()) $users[] = $u;
291                 }
292                 return $users;
293             case GROUP_ADMIN:           
294                 foreach ($all as $u) {
295                     $user = WikiUser($u);
296                     if ($user->isAdmin()) $users[] = $u;
297                 }
298                 return $users;
299             default:
300                 trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
301                                         'getMembersOf',GROUP_METHOD),
302                               E_USER_WARNING);
303             }
304         }
305         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
306                                 'getMembersOf',GROUP_METHOD),
307                       E_USER_WARNING);
308         return array();
309     }
310
311     /**
312      * Add the current or specified user to a group.
313      * 
314      * This method is an abstraction.  The group and user are ignored, an error 
315      * is sent, and false (not added) is always returned.
316      * @param string $group User added to this group.
317      * @param string $user Username to add to the group (default = current user).
318      * @return bool On true user was added, false if not.
319      */ 
320     function setMemberOf($group, $user = false){
321         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
322                                 'setMemberOf',GROUP_METHOD),
323                       E_USER_WARNING);
324         return false;
325     }
326     
327     /**
328      * Remove the current or specified user to a group.
329      * 
330      * This method is an abstraction.  The group and user are ignored, and error
331      * is sent, and false (not removed) is always returned.
332      * @param string $group User removed from this group.
333      * @param string $user Username to remove from the group (default = current user).
334      * @return bool On true user was removed, false if not.
335      */ 
336     function removeMemberOf($group, $user = false){
337         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
338                                 'removeMemberOf',GROUP_METHOD),
339                       E_USER_WARNING);
340         return false;
341     }
342 }
343
344 /**
345  * GroupNone disables all Group funtionality
346  * 
347  * All of the GroupNone functions return false or empty values to indicate failure or 
348  * no results.  Use GroupNone if group controls are not desired.
349  * @author Joby Walker <zorloc@imperium.org>
350  */ 
351 class GroupNone extends WikiGroup{
352
353     /**
354      * Constructor
355      * 
356      * Ignores the parameter provided.
357      * @param object $request The global WikiRequest object - ignored.
358      */ 
359     function GroupNone(&$request){
360         $this->request = &$request;
361         return;
362     }    
363
364     /**
365      * Determines if the current user is a member of a group.
366      * 
367      * The group is ignored and false (not a member of the group) is returned.
368      * @param string $group Name of the group to check for membership (ignored).
369      * @return boolean True if user is a member, else false (always false).
370      */ 
371     function isMember($group){
372         if ($this->specialGroup($group)) {
373             return WikiGroup::isMember($group);
374         } else {
375             return false;
376         }
377     }
378     
379     /**
380      * Determines all of the groups of which the current user is a member.
381      * 
382      * The group is ignored and an empty array (a member of no groups) is returned.
383      * @param string $group Name of the group to check for membership (ignored).
384      * @return array Array of groups to which the user belongs (always empty).
385      */ 
386     function getAllGroupsIn(){
387         return array();
388     }
389
390     /**
391      * Determines all of the members of a particular group.
392      * 
393      * The group is ignored and an empty array (a member of no groups) is returned.
394      * @param string $group Name of the group to check for membership (ignored).
395      * @return array Array of groups user belongs to (always empty).
396      */ 
397     function getMembersOf($group){
398         return array();
399     }
400
401 }
402
403 /**
404  * GroupWikiPage provides group functionality via pages within the Wiki.
405  * 
406  * GroupWikiPage is the Wiki way of managing a group.  Every group will have 
407  * a page. To modify the membership of the group, one only needs to edit the 
408  * membership list on the page.
409  * @author Joby Walker <zorloc@imperium.org>
410  */ 
411 class GroupWikiPage extends WikiGroup{
412     
413     /**
414      * Constructor
415      * 
416      * Initializes the three superclass instance variables
417      * @param object $request The global WikiRequest object.
418      */ 
419     function GroupWikiPage(&$request){
420         $this->request = &$request;
421         $this->username = $this->_getUserName();
422         //$this->username = null;
423         $this->membership = array();
424     }
425
426     /**
427      * Determines if the current user is a member of a group.
428      * 
429      * To determine membership in a particular group, this method checks the 
430      * superclass instance variable $membership to see if membership has 
431      * already been determined.  If not, then the group page is parsed to 
432      * determine membership.
433      * @param string $group Name of the group to check for membership.
434      * @return boolean True if user is a member, else false.
435      */ 
436     function isMember($group){
437         $request = $this->request;
438         //$username = $this->_getUserName();
439         if (isset($this->membership[$group])) {
440             return $this->membership[$group];
441         }
442         $group_page = $request->getPage($group);
443         if ($this->_inGroupPage($group_page)) {
444             $this->membership[$group] = true;
445             return true;
446         }
447         $this->membership[$group] = false;
448         // Let grouppages override certain defaults, such as members of admin
449         if ($this->specialGroup($group))
450             return WikiGroup::isMember($group);
451         return false;
452     }
453     
454     /**
455     * Private method to take a WikiDB_Page and parse to determine if the
456     * current_user is a member of the group.
457     * @param object $group_page WikiDB_Page object for the group's page
458     * @return boolean True if user is a member, else false.
459     * @access private
460     */
461     function _inGroupPage($group_page,$strict=false){
462         $group_revision = $group_page->getCurrentRevision();
463         if ($group_revision->hasDefaultContents()) {
464             $group = $group_page->getName();
465             if ($strict) trigger_error(sprintf(_("Group page '%s' does not exist"),$group), E_USER_WARNING);
466             return false;
467         }
468         $contents = $group_revision->getContent();
469         $match = '/^\s*[\*\#]+\s*' . $this->username . '\s*$/';
470         foreach ($contents as $line){
471             if (preg_match($match, $line)) {
472                 return true;
473             }
474         }
475         return false;
476     }
477     
478     /**
479      * Determines all of the groups of which the current user is a member.
480      * 
481      * Checks the root Group page ('CategoryGroup') for the list of all groups, 
482      * then checks each group to see if the current user is a member.
483      * @param string $group Name of the group to check for membership.
484      * @return array Array of groups to which the user belongs.
485      */ 
486     function getAllGroupsIn(){
487         $request = &$this->request;
488         //$username = $this->_getUserName();
489         $membership = array();
490
491         $specialgroups = $this->specialGroups();
492         foreach ($specialgroups as $group) {
493             $this->membership[$group] = $this->isMember($group);
494         }
495
496         $dbh = &$request->getDbh();
497         $master_page = $request->getPage(_("CategoryGroup"));
498         $master_list = $master_page->getLinks(true);
499         while ($group_page = $master_list->next()){
500             $group = $group_page->getName();
501             $this->membership[$group] = $this->_inGroupPage($group_page);
502         }
503         foreach ($this->membership as $g => $bool) {
504             if ($bool) $membership[] = $g;
505         }
506         return $membership;
507     }
508
509     /**
510      * Determines all of the members of a particular group.
511      * 
512      * Checks a group's page to return all the current members.  Currently this
513      * method is disabled and triggers an error and returns an empty array.
514      * @param string $group Name of the group to get the full membership list of.
515      * @return array Array of usernames that have joined the group (always empty).
516      */ 
517     function getMembersOf($group){
518         if ($this->specialGroup($group))
519             return WikiGroup::getMembersOf($group);
520
521         trigger_error("GroupWikiPage::getMembersOf is not yet implimented",
522                       E_USER_WARNING);
523         return array();
524         /*
525         * Waiting for a reliable way to check if a string is a username.
526         $request = $this->request;
527         $user = $this->user;
528         $group_page = $request->getPage($group);
529         $group_revision = $group_page->getCurrentRevision();
530         if ($group_revision->hasDefaultContents()) {
531             trigger_error("Group $group does not exist", E_USER_WARNING);
532             return false;
533         }
534         $contents = $group_revision->getContent();
535         $match = '/^(\s*[\*\#]+\s*)(\w+)(\s*)$/';
536         $members = array();
537         foreach($contents as $line){
538             $matches = array();
539             if(preg_match($match, $line, $matches)){
540                 $members[] = $matches[2];
541             }
542         }
543         return $members;
544         */
545     }
546 }
547
548 /**
549  * GroupDb is configured by $DbAuthParams[] statements
550  * 
551  * Fixme: adodb
552  * @author ReiniUrban
553  */ 
554 class GroupDb extends WikiGroup {
555     
556     var $_is_member, $_group_members, $_user_groups;
557
558     /**
559      * Constructor
560      * 
561      * @param object $request The global WikiRequest object. ignored
562      */ 
563     function GroupDb(&$request){
564         global $DBAuthParams, $DBParams;
565         $this->request = &$request;
566         $this->username = $this->_getUserName();
567         $this->membership = array();
568
569         if (empty($DBAuthParams['group_members']) or 
570             empty($DBAuthParams['user_groups']) or
571             empty($DBAuthParams['is_member'])) {
572             trigger_error(_("No or not enough GROUP_DB SQL statements defined"), E_USER_WARNING);
573             return new GroupNone(&$request);
574         }
575         // use _PassUser::prepare instead
576         if (isa($request->_user,'_PassUser'))
577             $user =& $request->_user;
578         else
579             $user = new _PassUser($this->username);
580         $this->_is_member = $user->prepare($DBAuthParams['is_member'],
581                                            array('userid','groupname'));
582         $this->_group_members = $user->prepare($DBAuthParams['group_members'],'groupname');
583         $this->_user_groups = $user->prepare($DBAuthParams['user_groups'],'userid');
584         $this->dbh = $user->_auth_dbi;
585     }
586 }
587
588 /**
589  * PearDB methods
590  * 
591  * @author ReiniUrban
592  */ 
593 class GroupDb_PearDB extends GroupDb {
594     
595     /**
596      * Determines if the current user is a member of a database group.
597      * 
598      * @param string $group Name of the group to check for membership.
599      * @return boolean True if user is a member, else false.
600      */ 
601     function isMember($group) {
602         if (isset($this->membership[$group])) {
603             return $this->membership[$group];
604         }
605         $dbh = & $this->dbh;
606         $db_result = $dbh->query(sprintf($this->_is_member,$dbh->quote($this->username),
607                                          $dbh->quote($group)));
608         if ($db_result->numRows() > 0) {
609             $this->membership[$group] = true;
610             return true;
611         }
612         $this->membership[$group] = false;
613         // Let override certain defaults, such as members of admin
614         if ($this->specialGroup($group))
615             return WikiGroup::isMember($group);
616         return false;
617     }
618     
619     /**
620      * Determines all of the groups of which the current user is a member.
621      * 
622      * then checks each group to see if the current user is a member.
623      * @param string $group Name of the group to check for membership.
624      * @return array Array of groups to which the user belongs.
625      */ 
626     function getAllGroupsIn(){
627
628         $membership = array();
629         $specialgroups = $this->specialGroups();
630         foreach ($specialgroups as $group) {
631             if ($this->isMember($group)) {
632                 $membership[] = $group;
633             }
634         }
635         $dbh = & $this->dbh;
636         $db_result = $dbh->query(sprintf($this->_user_groups,$dbh->quote($this->username)));
637         if ($db_result->numRows() > 0) {
638             while (list($group) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
639                 $membership[] = $group;
640                 $this->membership[$group] = true;
641             }
642         }
643         return $membership;
644     }
645
646     /**
647      * Determines all of the members of a particular group.
648      * 
649      * Checks a group's page to return all the current members.  Currently this
650      * method is disabled and triggers an error and returns an empty array.
651      * @param string $group Name of the group to get the full membership list of.
652      * @return array Array of usernames that have joined the group.
653      */ 
654     function getMembersOf($group){
655
656         $members = array();
657         $dbh = & $this->dbh;
658         $db_result = $dbh->query(sprintf($this->_group_members,$dbh->quote($group)));
659         if ($db_result->numRows() > 0) {
660             while (list($userid) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
661                 $members[] = $userid;
662             }
663         }
664         // add certain defaults, such as members of admin
665         if ($this->specialGroup($group))
666             $members = array_merge($memebrs,WikiGroup::getMembersOf($group));
667         return $members;
668     }
669 }
670
671 /**
672  * ADODB methods
673  * 
674  * @author ReiniUrban
675  */ 
676 class GroupDb_ADODB extends GroupDb {
677
678     /**
679      * Determines if the current user is a member of a database group.
680      * 
681      * @param string $group Name of the group to check for membership.
682      * @return boolean True if user is a member, else false.
683      */ 
684     function isMember($group) {
685         if (isset($this->membership[$group])) {
686             return $this->membership[$group];
687         }
688         $dbh = & $this->dbh;
689         $rs = $dbh->Execute(sprintf($this->_is_member,$dbh->qstr($this->username),
690                                     $dbh->qstr($group)));
691         if ($rs->EOF) {
692             $rs->Close();
693         } else {
694             if ($rs->numRows() > 0) {
695                 $this->membership[$group] = true;
696                 $rs->Close();
697                 return true;
698             }
699         }
700         $this->membership[$group] = false;
701         if ($this->specialGroup($group))
702             return WikiGroup::isMember($group);
703
704         return false;
705     }
706     
707     /**
708      * Determines all of the groups of which the current user is a member.
709      * then checks each group to see if the current user is a member.
710      *
711      * @param string $group Name of the group to check for membership.
712      * @return array Array of groups to which the user belongs.
713      */ 
714     function getAllGroupsIn(){
715
716         $membership = array();
717         $specialgroups = $this->specialGroups();
718         foreach ($specialgroups as $group) {
719             if ($this->isMember($group)) {
720                 $membership[] = $group;
721             }
722         }
723         $dbh = & $this->dbh;
724         $rs = $dbh->Execute(sprintf($this->_user_groups,$dbh->qstr($this->username)));
725         if (!$rs->EOF and $rs->numRows() > 0) {
726             while (!$rs->EOF) {
727                 $group = reset($rs->fields);
728                 $membership[] = $group;
729                 $this->membership[$group] = true;
730                 $rs->MoveNext();
731             }
732         }
733         $rs->Close();
734         return $membership;
735     }
736
737     /**
738      * Determines all of the members of a particular group.
739      * 
740      * @param string $group Name of the group to get the full membership list of.
741      * @return array Array of usernames that have joined the group.
742      */ 
743     function getMembersOf($group){
744         $members = array();
745         $dbh = & $this->dbh;
746         $rs = $dbh->Execute(sprintf($this->_group_members,$dbh->qstr($group)));
747         if (!$rs->EOF and $rs->numRows() > 0) {
748             while (!$rs->EOF) {
749                 $members[] = reset($rs->fields);
750                 $rs->MoveNext();
751             }
752         }
753         $rs->Close();
754         // add certain defaults, such as members of admin
755         if ($this->specialGroup($group))
756             $members = array_merge($members,WikiGroup::getMembersOf($group));
757         return $members;
758     }
759 }
760
761 /**
762  * GroupFile is configured by AUTH_GROUP_FILE
763  * groupname: user1 user2 ...
764  * 
765  * @author ReiniUrban
766  */ 
767 class GroupFile extends WikiGroup {
768     
769     /**
770      * Constructor
771      * 
772      * @param object $request The global WikiRequest object.
773      */ 
774     function GroupFile(&$request){
775         $this->request = &$request;
776         $this->username = $this->_getUserName();
777         //$this->username = null;
778         $this->membership = array();
779
780         if (!defined('AUTH_GROUP_FILE')) {
781             trigger_error(_("AUTH_GROUP_FILE not defined"), E_USER_WARNING);
782             return false;
783         }
784         if (!file_exists(AUTH_GROUP_FILE)) {
785             trigger_error(sprintf(_("Cannot open AUTH_GROUP_FILE %s"), AUTH_GROUP_FILE), E_USER_WARNING);
786             return false;
787         }
788         require_once('lib/pear/File_Passwd.php');
789         $this->_file = new File_Passwd(AUTH_GROUP_FILE,true);
790     }
791
792     /**
793      * Determines if the current user is a member of a group.
794      * 
795      * To determine membership in a particular group, this method checks the 
796      * superclass instance variable $membership to see if membership has 
797      * already been determined.  If not, then the group file is parsed to 
798      * determine membership.
799      * @param string $group Name of the group to check for membership.
800      * @return boolean True if user is a member, else false.
801      */ 
802     function isMember($group) {
803         //$request = $this->request;
804         //$username = $this->username;
805         if (isset($this->membership[$group])) {
806             return $this->membership[$group];
807         }
808
809         if (is_array($this->_file->users)) {
810           foreach ($this->_file->users as $g => $u) {
811             $users = explode(' ',$u);
812             if (in_array($this->username,$users)) {
813                 $this->membership[$group] = true;
814                 return true;
815             }
816           }
817         }
818         $this->membership[$group] = false;
819         if ($this->specialGroup($group))
820             return WikiGroup::isMember($group);
821         return false;
822     }
823     
824     /**
825      * Determines all of the groups of which the current user is a member.
826      * 
827      * then checks each group to see if the current user is a member.
828      * @param string $group Name of the group to check for membership.
829      * @return array Array of groups to which the user belongs.
830      */ 
831     function getAllGroupsIn(){
832         //$username = $this->_getUserName();
833         $membership = array();
834
835         $specialgroups = $this->specialGroups();
836         foreach ($specialgroups as $group) {
837             if ($this->isMember($group)) {
838                 $this->membership[$group] = true;
839                 $membership[] = $group;
840             }
841         }
842         if (is_array($this->_file->users)) {
843           foreach ($this->_file->users as $group => $u) {
844             $users = explode(' ',$u);
845             if (in_array($this->username,$users)) {
846                 $this->membership[$group] = true;
847                 $membership[] = $group;
848             }
849           }
850         }
851         return $membership;
852     }
853
854     /**
855      * Determines all of the members of a particular group.
856      * 
857      * Return all the current members.
858      * @param string $group Name of the group to get the full membership list of.
859      * @return array Array of usernames that have joined the group.
860      */ 
861     function getMembersOf($group){
862         $members = array();
863         if (!empty($this->_file->users[$group])) {
864             $members = explode(' ',$this->_file->users[$group]);
865         }
866         if ($this->specialGroup($group)) {
867             $members = array_merge($members,WikiGroup::getMembersOf($group));
868         }
869         return $members;
870     }
871 }
872
873 /**
874  * Ldap is configured in index.php
875  * 
876  * @author ReiniUrban
877  */ 
878 class GroupLdap extends WikiGroup {
879     
880     /**
881      * Constructor
882      * 
883      * @param object $request The global WikiRequest object.
884      */ 
885     function GroupLdap(&$request){
886         $this->request = &$request;
887         $this->username = $this->_getUserName();
888         $this->membership = array();
889
890         if (!defined("LDAP_AUTH_HOST")) {
891             trigger_error(_("LDAP_AUTH_HOST not defined"), E_USER_WARNING);
892             return false;
893         }
894         if (! function_exists('ldap_connect')) {
895             dl("ldap".DLL_EXT);
896             if (! function_exists('ldap_connect')) {
897                 trigger_error(_("No LDAP in this PHP version"), E_USER_WARNING);
898                 return false;
899             }
900         }
901         if (!defined("LDAP_BASE_DN"))
902             define("LDAP_BASE_DN",'');
903         $this->base_dn = LDAP_BASE_DN;
904         if (strstr("ou=",LDAP_BASE_DN))
905             $this->base_dn = preg_replace("/(ou=\w+,)?()/","\$2",LDAP_BASE_DN);
906     }
907
908     /**
909      * Determines if the current user is a member of a group.
910      * Not ready yet!
911      * 
912      * @param string $group Name of the group to check for membership.
913      * @return boolean True if user is a member, else false.
914      */ 
915     function isMember($group) {
916         if ($this->specialGroup($group))
917             return WikiGroup::isMember($group);
918
919         if (isset($this->membership[$group])) {
920             return $this->membership[$group];
921         }
922         //$request = $this->request;
923         //$username = $this->_getUserName();
924
925         $this->membership[$group] = in_array($this->username,$this->getMembersOf($group));
926         return $this->membership[$group];
927     }
928     
929     /**
930      * Determines all of the groups of which the current user is a member.
931      *
932      * @param string $group Name of the group to check for membership.
933      * @return array Array of groups to which the user belongs.
934      */ 
935     function getAllGroupsIn(){
936         //$request = &$this->request;
937         //$username = $this->_getUserName();
938         $membership = array();
939
940         $specialgroups = $this->specialGroups();
941         foreach ($specialgroups as $group) {
942             if ($this->isMember($group)) {
943                 $this->membership[$group] = true;
944                 $membership[] = $group;
945             }
946         }
947         // must be a valid LDAP server, and username must not contain a wildcard
948         if ($ldap = ldap_connect(LDAP_AUTH_HOST) and !strstr($this->username,'*')) { 
949             if (defined('LDAP_AUTH_USER'))
950                 if (defined('LDAP_AUTH_PASSWORD'))
951                     // Windows Active Directory Server is strict
952                     $r = @ldap_bind($ldap,LDAP_AUTH_USER,LDAP_AUTH_PASSWORD); 
953                 else
954                     $r = @ldap_bind($ldap,LDAP_AUTH_USER); 
955             else
956                 $r = @ldap_bind($ldap); // this is an anonymous bind
957             if (!empty($LDAP_SET_OPTION)) {
958                 foreach ($LDAP_SET_OPTION as $key => $value) {
959                     ldap_set_option($ldap,$key,$value);
960                 }
961             }
962             $st_search = defined('LDAP_SEARCH_FIELD') 
963                 ? LDAP_SEARCH_FIELD."=".$this->username
964                 : "uid=".$this->username;
965             $sr = ldap_search($ldap, "ou=Users,".$this->base_dn,$st_search);
966             $info = ldap_get_entries($ldap, $sr);
967             for ($i = 0; $i < $info["count"]; $i++) {
968                 if ($info[$i]["gidnumber"]["count"]) {
969                   $gid = $info[$i]["gidnumber"][0];
970                   $sr2 = ldap_search($ldap, "ou=Groups,".$this->base_dn,"gidNumber=$gid");
971                   $info2 = ldap_get_entries($ldap, $sr2);
972                   if ($info2["count"])
973                     $membership[] =  $info2[0]["cn"][0];
974                 }
975             }
976         } else {
977             trigger_error(fmt("Unable to connect to LDAP server %s", LDAP_AUTH_HOST), 
978                           E_USER_WARNING);
979         }
980         ldap_close($ldap);
981         $this->membership = $membership;
982         return $membership;
983     }
984
985     /**
986      * Determines all of the members of a particular group.
987      * 
988      * Return all the members of the given group. LDAP just returns the gid of each user
989      * @param string $group Name of the group to get the full membership list of.
990      * @return array Array of usernames that have joined the group.
991      */ 
992     function getMembersOf($group){
993         if ($this->specialGroup($group))
994             return WikiGroup::getMembersOf($group);
995
996         $members = array();
997         if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
998             $r = @ldap_bind($ldap);                 // this is an anonymous bind
999             $sr = ldap_search($ldap, "ou=Groups,".$this->base_dn,"cn=$group");
1000             $info = ldap_get_entries($ldap, $sr);
1001             for ($i = 0; $i < $info["count"]; $i++) {
1002                 $gid = $info[$i]["gidnumber"][0];
1003                 //uid=* would be better probably
1004                 $sr2 = ldap_search($ldap, "ou=Users,".$this->base_dn,"gidNumber=$gid");
1005                 $info2 = ldap_get_entries($ldap, $sr2);
1006                 for ($j = 0; $j < $info2["count"]; $j++) {
1007                     $members[] = $info2[$j]["cn"][0];
1008                 }
1009             }
1010         }
1011         ldap_close($ldap);
1012         return $members;
1013     }
1014 }
1015
1016 // $Log: not supported by cvs2svn $
1017 // Revision 1.29  2004/05/16 22:07:35  rurban
1018 // check more config-default and predefined constants
1019 // various PagePerm fixes:
1020 //   fix default PagePerms, esp. edit and view for Bogo and Password users
1021 //   implemented Creator and Owner
1022 //   BOGOUSERS renamed to BOGOUSER
1023 // fixed syntax errors in signin.tmpl
1024 //
1025 // Revision 1.28  2004/05/15 22:54:49  rurban
1026 // fixed important WikiDB bug with DEBUG > 0: wrong assertion
1027 // improved SetAcl (works) and PagePerms, some WikiGroup helpers.
1028 //
1029 // Revision 1.27  2004/05/06 13:56:40  rurban
1030 // Enable the Administrators group, and add the WIKIPAGE group default root page.
1031 //
1032 // Revision 1.26  2004/04/07 23:13:18  rurban
1033 // fixed pear/File_Passwd for Windows
1034 // fixed FilePassUser sessions (filehandle revive) and password update
1035 //
1036 // Revision 1.25  2004/03/29 10:40:36  rurban
1037 // GroupDb_PearDB fetchmode fix
1038 //
1039 // Revision 1.24  2004/03/14 16:26:22  rurban
1040 // copyright line
1041 //
1042 // Revision 1.23  2004/03/12 23:20:58  rurban
1043 // pref fixes (base64)
1044 //
1045 // Revision 1.22  2004/03/12 15:48:07  rurban
1046 // fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1047 // simplified lib/stdlib.php:explodePageList
1048 //
1049 // Revision 1.21  2004/03/12 11:18:24  rurban
1050 // fixed ->membership chache
1051 //
1052 // Revision 1.20  2004/03/12 10:47:30  rurban
1053 // fixed GroupDB for ADODB
1054 //
1055 // Revision 1.19  2004/03/11 16:27:30  rurban
1056 // fixed GroupFile::getMembersOf for special groups
1057 // added authenticated bind for GroupLdap (Windows AD) as in WikiUserNew
1058 //
1059 // Revision 1.18  2004/03/11 13:30:47  rurban
1060 // fixed File Auth for user and group
1061 // missing only getMembersOf(Authenticated Users),getMembersOf(Every),getMembersOf(Signed Users)
1062 //
1063 // Revision 1.17  2004/03/10 15:38:48  rurban
1064 // store current user->page and ->action in session for WhoIsOnline
1065 // better WhoIsOnline icon
1066 // fixed WhoIsOnline warnings
1067 //
1068 // Revision 1.15  2004/03/09 12:11:57  rurban
1069 // prevent from undefined DBAuthParams warning
1070 //
1071 // Revision 1.14  2004/03/08 19:30:01  rurban
1072 // fixed Theme->getButtonURL
1073 // AllUsers uses now WikiGroup (also DB User and DB Pref users)
1074 // PageList fix for empty pagenames
1075 //
1076 // Revision 1.13  2004/03/08 18:17:09  rurban
1077 // added more WikiGroup::getMembersOf methods, esp. for special groups
1078 // fixed $LDAP_SET_OPTIONS
1079 // fixed _AuthInfo group methods
1080 //
1081 // Revision 1.12  2004/02/23 21:30:25  rurban
1082 // more PagePerm stuff: (working against 1.4.0)
1083 //   ACL editing and simplification of ACL's to simple rwx------ string
1084 //   not yet working.
1085 //
1086 // Revision 1.11  2004/02/07 10:41:25  rurban
1087 // fixed auth from session (still double code but works)
1088 // fixed GroupDB
1089 // fixed DbPassUser upgrade and policy=old
1090 // added GroupLdap
1091 //
1092 // Revision 1.10  2004/02/03 09:45:39  rurban
1093 // LDAP cleanup, start of new Pref classes
1094 //
1095 // Revision 1.9  2004/02/01 09:14:11  rurban
1096 // Started with Group_Ldap (not yet ready)
1097 // added new _AuthInfo plugin to help in auth problems (warning: may display passwords)
1098 // fixed some configurator vars
1099 // renamed LDAP_AUTH_SEARCH to LDAP_BASE_DN
1100 // changed PHPWIKI_VERSION from 1.3.8a to 1.3.8pre
1101 // USE_DB_SESSION defaults to true on SQL
1102 // changed GROUP_METHOD definition to string, not constants
1103 // changed sample user DBAuthParams from UPDATE to REPLACE to be able to
1104 //   create users. (Not to be used with external databases generally, but
1105 //   with the default internal user table)
1106 //
1107 // fixed the IndexAsConfigProblem logic. this was flawed:
1108 //   scripts which are the same virtual path defined their own lib/main call
1109 //   (hmm, have to test this better, phpwiki.sf.net/demo works again)
1110 //
1111 // Revision 1.8  2004/01/27 23:23:39  rurban
1112 // renamed ->Username => _userid for consistency
1113 // renamed mayCheckPassword => mayCheckPass
1114 // fixed recursion problem in WikiUserNew
1115 // fixed bogo login (but not quite 100% ready yet, password storage)
1116 //
1117 // Revision 1.7  2004/01/26 16:52:40  rurban
1118 // added GroupDB and GroupFile classes
1119 //
1120 // Revision 1.6  2003/12/07 19:29:11  carstenklapp
1121 // Code Housecleaning: fixed syntax errors. (php -l *.php)
1122 //
1123 // Revision 1.5  2003/02/22 20:49:55  dairiki
1124 // Fixes for "Call-time pass by reference has been deprecated" errors.
1125 //
1126 // Revision 1.4  2003/01/21 04:02:39  zorloc
1127 // Added Log entry and page footer.
1128 //
1129
1130 // Local Variables:
1131 // mode: php
1132 // tab-width: 8
1133 // c-basic-offset: 4
1134 // c-hanging-comment-ender-p: nil
1135 // indent-tabs-mode: nil
1136 // End:
1137 ?>