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