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