]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiUser.php
Kludgy patch to close a potential security hole.
[SourceForge/phpwiki.git] / lib / WikiUser.php
1 <?php rcs_id('$Id: WikiUser.php,v 1.5 2001-12-06 20:44:13 dairiki Exp $');
2
3 // It is anticipated that when userid support is added to phpwiki,
4 // this object will hold much more information (e-mail, home(wiki)page,
5 // etc.) about the user.
6    
7 // There seems to be no clean way to "log out" a user when using
8 // HTTP authentication.
9 // So we'll hack around this by storing the currently logged
10 // in username and other state information in a cookie.
11 class WikiUser 
12 {
13     // Arg $login_mode:
14     //   default:  Anonymous users okay.
15     //   'ANON_OK': Anonymous access is fine.
16     //   'REQUIRE_AUTH': User must be authenticated.
17     //   'LOGOUT':  Force logout.
18     //   'LOGIN':   Force authenticated login.
19     function WikiUser (&$request, $auth_mode = '') {
20         $this->_request = &$request;
21         // Restore from cookie.
22         $this->_restore();
23
24         // don't check for HTTP auth if there's nothing to worry about
25         //
26         // FIXME: the addition of this short-cut introduced a security hole.
27         //        Since $this->_restore can potentially restore $this from a
28         //        user provided cookie, a carefully constructed cookie can
29         //        be used to effectively log in (even as admin) without
30         //        a password.
31         //
32         //        For now, I'm disabling the code which saves/restores $this
33         //        in a cookie.  (Login state is still preserved in session vars.)
34         //        I'll work on a longer term solution.
35         
36         if (  $this->state == 'authorized' 
37               && $auth_mode != 'LOGIN' 
38               && $auth_mode != 'LOGOUT'  )
39             return;   
40
41         if ($this->state == 'authorized' && $auth_mode == 'LOGIN') {
42             // ...logout
43             $this->realm++;
44             $this->state = 'loggedout';
45         }
46       
47         if ($auth_mode != 'LOGOUT') {
48             $user = $this->_get_authenticated_userid();
49
50             if (!$user && $auth_mode != 'ANON_OK')
51                 $warning = $this->_demand_http_authentication(); //NORETURN
52         }
53         
54         if (empty($user)) {
55             // Authentication failed
56             if ($this->state == 'authorized')
57                 $this->realm++;
58             $this->state = 'loggedout';
59             $this->userid = $request->get('REMOTE_HOST');
60         }
61         else {
62             // Successful authentication
63             $this->state = 'authorized';
64             $this->userid = $user;
65         }
66
67         // Save state to cookie and/or session registry.
68         $this->_save($request);
69
70         if (isset($warning))
71             echo $warning;
72     }
73
74     function id () {
75         return $this->userid;
76     }
77
78     function authenticated_id() {
79         if ($this->is_authenticated())
80             return $this->id();
81         else
82             return $this->_request->get('REMOTE_ADDR');
83     }
84
85     function is_authenticated () {
86         return $this->state == 'authorized';
87     }
88          
89     function is_admin () {
90         return $this->is_authenticated() && $this->userid == ADMIN_USER;
91     }
92
93     function must_be_admin ($action = "") {
94         if (! $this->is_admin()) 
95             {
96                 if ($action)
97                     $to_what = sprintf(gettext("to perform action '%s'"), $action);
98                 else
99                     $to_what = gettext("to do that");
100                 ExitWiki(gettext("You must be logged in as an administrator")
101                          . " $to_what");
102             }
103     }
104
105     // This is a bit of a hack:
106     function setPreferences ($prefs) {
107         $req = &$this->_request;
108         $req->setCookieVar('WIKI_PREFS', $prefs, 365); // expire in a year.
109     }
110
111     function getPreferences () {
112         $req = &$this->_request;
113
114         $prefs = array('edit_area.width' => 80,
115                        'edit_area.height' => 22);
116
117         $saved = $req->getCookieVar('WIKI_PREFS');
118         
119         if (is_array($saved)) {
120             foreach ($saved as $key => $val) {
121                 if (isset($prefs[$key]) && !empty($val))
122                     $prefs[$key] = $val;
123             }
124         }
125
126         // Some sanity checks. (FIXME: should move somewhere else)
127         if (!($prefs['edit_area.width'] >= 30 && $prefs['edit_area.width'] <= 150))
128             $prefs['edit_area.width'] = 80;
129         if (!($prefs['edit_area.height'] >= 5 && $prefs['edit_area.height'] <= 80))
130             $prefs['edit_area.height'] = 22;
131         return $prefs;
132     }
133    
134     function _get_authenticated_userid () {
135         if ( ! ($user = $this->_get_http_authenticated_userid()) )
136             return false;
137        
138         switch ($this->state) {
139         case 'login':
140             // Either we just asked for a password, or cookies are not enabled.
141             // In either case, proceed with successful login.
142             return $user;
143         case 'loggedout':
144             // We're logged out.  Ignore http authed user.
145             return false;
146         default:
147             // FIXME: Can't reset auth cache on Mozilla (and probably others),
148             // so for now, just trust the saved state
149             return $this->userid;
150           
151             // Else, as long as the user hasn't changed, fine.
152             if ($user && $user != $this->userid)
153                 return false;
154             return $user;
155         }
156     }
157
158     function _get_http_authenticated_userid () {
159         global $WikiNameRegexp;
160
161         $userid = $this->_request->get('PHP_AUTH_USER');
162         $passwd = $this->_request->get('PHP_AUTH_PW');
163
164         if (!empty($userid) && $userid == ADMIN_USER) {
165             if (!empty($passwd) && $passwd == ADMIN_PASSWD)
166                 return $userid;
167         }
168         elseif (ALLOW_BOGO_LOGIN
169                 && preg_match('/\A' . $WikiNameRegexp . '\z/', $userid)) {
170             // FIXME: this shouldn't count as authenticated.
171             return $userid;
172         }
173         return false;
174     }
175    
176     function _demand_http_authentication () {
177         if (!defined('ADMIN_USER') || !defined('ADMIN_PASSWD')
178             || ADMIN_USER == '' || ADMIN_PASSWD =='') {
179             return
180                 "<p><b>"
181                 . gettext("You must set the administrator account and password before you can log in.")
182                 . "</b></p>\n";
183         }
184
185         // Request password
186         $this->userid = '';
187         $this->state = 'login';
188       
189         $this->_save();
190         $request = &$this->_request;
191         header('WWW-Authenticate: Basic realm="' . $this->realm . '"');
192         $request->setStatus("HTTP/1.0 401 Unauthorized");
193         echo "<p>" . gettext ("You entered an invalid login or password.") . "\n";
194         if (ALLOW_BOGO_LOGIN) {
195             echo "<p>";
196             echo gettext ("You can log in using any valid WikiWord as a user ID.") . "\n";
197             echo gettext ("(Any password will work, except, of course for the admin user.)") . "\n";
198         }
199       
200         ExitWiki();
201     }
202
203     function _copy($object) {
204         if (!is_object($object))
205             return false;
206         if (strtolower(get_class($object)) != 'wikiuser')
207             return false;
208
209         $this->userid = $object->userid;
210         $this->state = $object->state;
211         $this->realm = $object->realm;
212         return true;
213     }
214        
215     function _restore() {
216         $req = &$this->_request;
217         
218         if ( $this->_copy($req->getSessionVar('auth_state')) )
219             return;
220         // FIXME: Disable restore from cookie (see note in WikiUser().)
221         //elseif ( $this->_copy($req->getCookieVar('WIKI_AUTH')) )
222         //    return;
223         else {
224             // Default state.
225             $this->userid = '';
226             $this->state = 'login';
227             $this->realm = 'PhpWiki0000';
228         }
229     }
230
231     function _save() {
232         $req = &$this->_request;
233
234         $req->setSessionVar('auth_state', $this);
235         // FIXME: Disable restore from cookie (see note in WikiUser().)
236         //$req->setCookieVar('WIKI_AUTH', $this);
237     }
238 }
239
240 // Local Variables:
241 // mode: php
242 // tab-width: 8
243 // c-basic-offset: 4
244 // c-hanging-comment-ender-p: nil
245 // indent-tabs-mode: nil
246 // End:   
247 ?>