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