|
1 <?php |
|
2 /**!info** |
|
3 { |
|
4 "Plugin Name" : "HTTP authentication", |
|
5 "Plugin URI" : "http://enanocms.org/plugin/httpauth", |
|
6 "Description" : "Allows authentication to Enano via HTTP authentication.", |
|
7 "Author" : "Dan Fuhry", |
|
8 "Version" : "1.0", |
|
9 "Author URI" : "http://enanocms.org/", |
|
10 "Auth plugin" : true |
|
11 } |
|
12 **!*/ |
|
13 |
|
14 /* |
|
15 * HTTP authentication plugin for Enano |
|
16 * (C) 2014 Dan Fuhry |
|
17 * |
|
18 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
19 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
20 * |
|
21 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
22 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
23 */ |
|
24 |
|
25 if ( getConfig('http_auth_enable', 0) == 1 ) |
|
26 { |
|
27 $plugins->attachHook('compile_template', 'http_auth_attach_headers($this);'); |
|
28 $plugins->attachHook('login_form_html', 'http_auth_login_html();'); |
|
29 } |
|
30 |
|
31 function http_auth_attach_headers(&$template) |
|
32 { |
|
33 global $db, $session, $paths, $template, $plugins; // Common objects |
|
34 |
|
35 $template->add_header('<script type="text/javascript" src="' . scriptPath . '/plugins/httpauth/login-hook.js"></script>'); |
|
36 } |
|
37 |
|
38 function http_auth_login_html() |
|
39 { |
|
40 global $db, $session, $paths, $template, $plugins; // Common objects |
|
41 |
|
42 global $output; |
|
43 |
|
44 ob_end_clean(); |
|
45 |
|
46 $return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page(); |
|
47 $qs = ( isset($_GET['level']) ) ? 'level=' . $_GET['level'] : ''; |
|
48 |
|
49 $uri = makeUrlNS('Special', 'LoginHTTP/' . $return, $qs); |
|
50 |
|
51 redirect($uri, '', '', 0); |
|
52 exit; |
|
53 } |
|
54 |
|
55 // Registration blocking hook |
|
56 if ( getConfig('http_auth_disable_local', 0) == 1 ) |
|
57 { |
|
58 $plugins->attachHook('ucp_register_validate', 'http_auth_reg_block($error);'); |
|
59 } |
|
60 |
|
61 function http_auth_reg_block(&$error) |
|
62 { |
|
63 $error = 'Registration on this website is disabled because HTTP authentication is configured. Please log in using a valid username and password, and an account will be created for you automatically.'; |
|
64 } |
|
65 |
|
66 $plugins->attachHook('session_started', 'http_auth_add_special();'); |
|
67 |
|
68 function http_auth_add_special() |
|
69 { |
|
70 register_special_page('LoginHTTP', 'Login with HTTP Authentication', true); |
|
71 } |
|
72 |
|
73 function page_Special_LoginHTTP() |
|
74 { |
|
75 global $db, $session, $paths, $template, $plugins; // Common objects |
|
76 |
|
77 global $output; |
|
78 |
|
79 if ( isset($_GET['level']) ) { |
|
80 $result = array('result' => 'error'); |
|
81 |
|
82 if ( !empty($_SERVER['REMOTE_USER']) ) { |
|
83 $level = intval($_GET['level']); |
|
84 if ( $level > USER_LEVEL_MEMBER ) { |
|
85 $username = $db->escape(strtolower($_SERVER['REMOTE_USER'])); |
|
86 |
|
87 $q = $db->sql_query("SELECT user_id, password, user_level FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
88 if ( !$q ) |
|
89 $db->_die(); |
|
90 |
|
91 if ( $db->numrows() == 1 ) { |
|
92 $row = $db->fetchrow(); |
|
93 |
|
94 if ( $row['user_level'] < $level ) { |
|
95 die_friendly('Access denied', '<p>Not permitted to authenticate at this level.</p>'); |
|
96 } |
|
97 |
|
98 $session->register_session($row['user_id'], $_SERVER['REMOTE_USER'], $row['password'], $level, $remember); |
|
99 |
|
100 $result = array( |
|
101 'result' => 'success', |
|
102 'sid' => $session->sid_super |
|
103 ); |
|
104 } |
|
105 |
|
106 $db->free_result(); |
|
107 } |
|
108 } |
|
109 |
|
110 if ( isset($_GET['ajax']) ) { |
|
111 $output = new Output_Naked; |
|
112 header('Content-type: text/javascript'); |
|
113 echo json_encode($result); |
|
114 |
|
115 return; |
|
116 } |
|
117 } |
|
118 else |
|
119 { |
|
120 if ( empty($_SERVER['REMOTE_USER']) ) { |
|
121 die_friendly('No HTTP authentication supplied', '<p>This site is configured for HTTP authentication, but none was supplied by the webserver software. Please verify your webserver configuration.</p>'); |
|
122 } |
|
123 |
|
124 http_auth_do_login(); |
|
125 } |
|
126 |
|
127 $return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page(); |
|
128 redirect(makeUrl($return), 'Logged in', 'You have successfully logged in using HTTP authentication. You will be momentarily taken to your destination.', 3); |
|
129 } |
|
130 |
|
131 function http_auth_do_login() |
|
132 { |
|
133 global $db, $session, $paths, $template, $plugins; // Common objects |
|
134 |
|
135 $user = $_SERVER['REMOTE_USER']; |
|
136 |
|
137 $username = $db->escape(strtolower($user)); |
|
138 |
|
139 $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
140 if ( !$q ) |
|
141 $db->_die(); |
|
142 |
|
143 if ( $db->numrows() < 1 ) |
|
144 { |
|
145 // This user doesn't exist. |
|
146 // Is creating it our job? |
|
147 if ( getConfig('http_auth_disable_local', 0) == 1 ) |
|
148 { |
|
149 // Yep, register him |
|
150 $email = strtolower($user) . '@' . getConfig('http_auth_email_domain', 'localhost'); |
|
151 $random_pass = md5(microtime() . mt_rand()); |
|
152 // load the language |
|
153 $session->register_guest_session(); |
|
154 $reg_result = $session->create_user($user, $random_pass, $email); |
|
155 if ( $reg_result != 'success' ) |
|
156 { |
|
157 // o_O |
|
158 // Registration failed. |
|
159 die_friendly('HTTP authentication error', '<p>Your username and password were valid, but there was a problem instanciating your local user account: ' . $reg_result . '.</p>'); |
|
160 } |
|
161 // Get user ID |
|
162 $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
163 if ( !$q ) |
|
164 $db->_die(); |
|
165 if ( $db->numrows() < 1 ) { |
|
166 die_friendly('HTTP authentication error', '<p>Your username and password were valid, but there was a problem getting your user ID.</p>'); |
|
167 } |
|
168 $row = $db->fetchrow(); |
|
169 $db->free_result(); |
|
170 // Quick - lock the account |
|
171 $q = $db->sql_query('UPDATE ' . table_prefix . "users SET password = 'Locked by HTTP auth plugin', password_salt = 'Locked by HTTP auth plugin' WHERE user_id = {$row['user_id']};"); |
|
172 if ( !$q ) |
|
173 $db->_die(); |
|
174 |
|
175 $row['password'] = 'Locked by HTTP auth plugin'; |
|
176 } |
|
177 else |
|
178 { |
|
179 // Nope. Just let Enano fail it properly. |
|
180 die_friendly('User does not exist', '<p>You\'ve attempted to log in with an account that doesn\'t exist, and the HTTP Authentication plugin is not configured to auto-create new accounts.</p>'); |
|
181 } |
|
182 } |
|
183 else |
|
184 { |
|
185 $row = $db->fetchrow(); |
|
186 $db->free_result(); |
|
187 } |
|
188 |
|
189 $session->register_session($row['user_id'], $user, $row['password'], $level, $remember); |
|
190 } |
|
191 |
|
192 // |
|
193 // ADMIN |
|
194 // |
|
195 |
|
196 $plugins->attachHook('session_started', 'http_auth_session_hook();'); |
|
197 |
|
198 if ( getConfig('http_auth_disable_local', 0) == 1 ) |
|
199 { |
|
200 $plugins->attachHook('common_post', 'http_auth_tou_hook();'); |
|
201 } |
|
202 |
|
203 function http_auth_session_hook() |
|
204 { |
|
205 global $db, $session, $paths, $template, $plugins; // Common objects |
|
206 |
|
207 // Register the admin page |
|
208 $paths->addAdminNode('adm_cat_security', 'HTTP Authentication', 'HTTPAuthConfig'); |
|
209 |
|
210 // Disable password change |
|
211 if ( getConfig('http_auth_disable_local', 0) == 1 && $session->user_level < USER_LEVEL_ADMIN ) |
|
212 { |
|
213 $link_text = getConfig('http_auth_password_text', false); |
|
214 if ( empty($link_text) ) |
|
215 $link_text = false; |
|
216 $link_url = str_replace('%u', $session->username, getConfig('http_auth_password_url', '')); |
|
217 if ( empty($link_url) ) |
|
218 $link_url = false; |
|
219 $session->disable_password_change($link_url, $link_text); |
|
220 } |
|
221 } |
|
222 |
|
223 function clean_server_redirect_vars() |
|
224 { |
|
225 foreach ( $_SERVER as $key => $value ) { |
|
226 if ( preg_match($regexp = '/^(REDIRECT_)*/', $key) ) |
|
227 { |
|
228 $newkey = preg_replace($regexp, '', $key); |
|
229 if ( !isset($_SERVER[$newkey]) ) |
|
230 { |
|
231 $_SERVER[$newkey] = $value; |
|
232 } |
|
233 } |
|
234 } |
|
235 } |
|
236 |
|
237 function http_auth_tou_hook() |
|
238 { |
|
239 global $db, $session, $paths, $template, $plugins; // Common objects |
|
240 |
|
241 // Are we supposed to fail if no authentication information is presented? |
|
242 // first strip REDIRECT_* from $_SERVER variables |
|
243 clean_server_redirect_vars(); |
|
244 |
|
245 if ( getConfig('http_auth_mode', 'guest') === 'noguest' && empty($_SERVER['REMOTE_USER']) ) |
|
246 { |
|
247 die_friendly('No authentication provided', '<p>This Enano website is configured to require HTTP authentication for all pages, but none was provided by the webserver software. Please check your webserver configuration.</p>'); |
|
248 } |
|
249 |
|
250 if ( !empty($_SERVER['REMOTE_USER']) && !$session->user_logged_in && !in_array($paths->page, array('Special:Login', 'Special:LoginHTTP', 'Special:Logout')) ) { |
|
251 http_auth_do_login(); |
|
252 redirect($paths->page, '', '', 0); |
|
253 } |
|
254 |
|
255 // Are we pending TOU acceptance? |
|
256 if ( $session->user_logged_in && !$session->on_critical_page() && trim(getConfig('register_tou', '')) != '' ) |
|
257 { |
|
258 $q = $db->sql_query('SELECT account_active FROM ' . table_prefix . "users WHERE user_id = {$session->user_id};"); |
|
259 if ( !$q ) |
|
260 $db->_die(); |
|
261 |
|
262 list($active) = $db->fetchrow_num(); |
|
263 $db->free_result(); |
|
264 if ( $active == 1 ) |
|
265 { |
|
266 // Pending TOU accept |
|
267 // Basically, what we do here is force the user to accept the TOU and record it by setting account_active to 2 instead of a 1 |
|
268 // A bit of a hack, but hey, it works, at least in 1.1.8. |
|
269 // In 1.1.7, it just breaks your whole account, and $session->on_critical_page() is broken in 1.1.7 so you won't even be able |
|
270 // to go the admin CP and re-activate yourself. Good times... erhm, sorry. |
|
271 |
|
272 if ( isset($_POST['tou_agreed']) && $_POST['tou_agreed'] === 'I accept the terms and conditions displayed on this site' ) |
|
273 { |
|
274 // Accepted |
|
275 $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 2 WHERE user_id = {$session->user_id};"); |
|
276 if ( !$q ) |
|
277 $db->_die(); |
|
278 |
|
279 return true; |
|
280 } |
|
281 |
|
282 global $output, $lang; |
|
283 $output->set_title('Terms of Use'); |
|
284 $output->header(); |
|
285 |
|
286 ?> |
|
287 <p>Please read and accept the following terms:</p> |
|
288 |
|
289 <div style="border: 1px solid #000000; height: 300px; width: 60%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: #FFF; margin: 0 auto; padding: 4px;"> |
|
290 <?php |
|
291 $terms = getConfig('register_tou', ''); |
|
292 echo RenderMan::render($terms); |
|
293 ?> |
|
294 </div> |
|
295 |
|
296 <form method="post"> |
|
297 <p style="text-align: center;"> |
|
298 <label> |
|
299 <input tabindex="7" type="checkbox" name="tou_agreed" value="I accept the terms and conditions displayed on this site" /> |
|
300 <b><?php echo $lang->get('user_reg_lbl_field_tou'); ?></b> |
|
301 </label> |
|
302 </p> |
|
303 <p style="text-align: center;"> |
|
304 <input type="submit" value="Continue" /> |
|
305 </p> |
|
306 </form> |
|
307 |
|
308 <?php |
|
309 |
|
310 $output->footer(); |
|
311 |
|
312 $db->close(); |
|
313 exit; |
|
314 } |
|
315 } |
|
316 } |
|
317 |
|
318 function page_Admin_HTTPAuthConfig() |
|
319 { |
|
320 // Security check |
|
321 global $db, $session, $paths, $template, $plugins; // Common objects |
|
322 if ( $session->auth_level < USER_LEVEL_ADMIN ) |
|
323 return false; |
|
324 |
|
325 if ( isset($_POST['submit']) ) |
|
326 { |
|
327 setConfig('http_auth_enable', isset($_POST['http_auth_enable']) ? '1' : '0'); |
|
328 setConfig('http_auth_disable_local', isset($_POST['http_auth_disable_local']) ? '1' : '0'); |
|
329 setConfig('http_auth_mode', isset($_POST['http_auth_mode']) && in_array($_POST['http_auth_mode'], array('guest', 'noguest')) ? $_POST['http_auth_mode'] : 'guest'); |
|
330 setConfig('http_auth_password_text', $_POST['http_auth_password_text']); |
|
331 setConfig('http_auth_password_url', $_POST['http_auth_password_url']); |
|
332 setConfig('http_auth_email_domain', $_POST['http_auth_email_domain']); |
|
333 |
|
334 echo '<div class="info-box">Your changes have been saved.</div>'; |
|
335 } |
|
336 |
|
337 acp_start_form(); |
|
338 ?> |
|
339 <div class="tblholder"> |
|
340 <table border="0" cellspacing="1" cellpadding="4"> |
|
341 <tr> |
|
342 <th colspan="2"> |
|
343 HTTP Authentication Configuration |
|
344 </th> |
|
345 </tr> |
|
346 |
|
347 <!-- HTTP enable --> |
|
348 |
|
349 <tr> |
|
350 <td class="row2" style="width: 50%;"> |
|
351 Enable HTTP authentication: |
|
352 </td> |
|
353 <td class="row1" style="width: 50%;"> |
|
354 <label> |
|
355 <input type="checkbox" name="http_auth_enable" <?php if ( getConfig('http_auth_enable', 0) ) echo 'checked="checked" '; ?>/> |
|
356 Enabled |
|
357 </label> |
|
358 </td> |
|
359 </tr> |
|
360 |
|
361 <!-- Block local auth --> |
|
362 |
|
363 <tr> |
|
364 <td class="row2"> |
|
365 Enforce HTTP for single-sign-on:<br /> |
|
366 <small>Use this option to force HTTP passwords and accounts to be used, regardless of local accounts, except for administrators.</small> |
|
367 </td> |
|
368 <td class="row1"> |
|
369 <label> |
|
370 <input type="checkbox" name="http_auth_disable_local" <?php if ( getConfig('http_auth_disable_local', 0) ) echo 'checked="checked" '; ?>/> |
|
371 Enabled |
|
372 </label> |
|
373 </td> |
|
374 </tr> |
|
375 |
|
376 <!-- Auth mode --> |
|
377 |
|
378 <tr> |
|
379 <td class="row2" rowspan="2"> |
|
380 Guest access mode:<br /> |
|
381 <small>You can allow guests to browse the site without logging in, and configure your webserver to require authentication only on the login page URL given below. Or, you can require authentication across the whole site. In the latter case, if the webserver fails to provide any authentication state, page loads will fail.</small> |
|
382 </td> |
|
383 <td class="row1"> |
|
384 <label> |
|
385 <input type="radio" name="http_auth_mode" value="guest" <?php if ( getConfig('http_auth_mode', 'guest') === 'guest' ) echo 'checked="checked" '; ?>/> |
|
386 Guests allowed |
|
387 </label> |
|
388 |
|
389 <label> |
|
390 <input type="radio" name="http_auth_mode" value="noguest" <?php if ( getConfig('http_auth_mode', 'guest') === 'noguest' ) echo 'checked="checked" '; ?>/> |
|
391 Fail without authentication |
|
392 </label> |
|
393 </td> |
|
394 </tr> |
|
395 |
|
396 <tr> |
|
397 <td class="row3"> |
|
398 Login page URL: |
|
399 <input size="45" type="text" readonly="readonly" value="<?php echo htmlspecialchars(preg_replace('/[?&]auth=[a-f0-9]+/', '', makeUrlComplete('Special', 'LoginHTTP'))); ?>" /> |
|
400 |
|
401 <br /> |
|
402 <small>Set this URL to require authentication in your webserver's configuration.</small> |
|
403 </td> |
|
404 </tr> |
|
405 |
|
406 <!-- E-mail domain --> |
|
407 |
|
408 <tr> |
|
409 <td class="row2"> |
|
410 E-mail address domain for autoregistered users:<br /> |
|
411 <small>When a user is automatically registered, this domain will be used as the domain for their e-mail address. This way, activation e-mails will |
|
412 (ideally) reach the user.</small> |
|
413 </td> |
|
414 <td class="row1"> |
|
415 <input type="text" name="http_auth_email_domain" value="<?php echo htmlspecialchars(getConfig('http_auth_email_domain', '')); ?>" size="30" /> |
|
416 </td> |
|
417 </tr> |
|
418 |
|
419 <!-- Site password change link --> |
|
420 |
|
421 <tr> |
|
422 <td class="row2"> |
|
423 External password management link:<br /> |
|
424 <small>Enter a URL here to link to from Enano's Change Password page. Leave blank to not display a link. The text "%u" will be replaced with the user's username.</small> |
|
425 </td> |
|
426 <td class="row1"> |
|
427 Link text: <input type="text" name="http_auth_password_text" value="<?php echo htmlspecialchars(getConfig('http_auth_password_text', '')); ?>" size="30" /><br /> |
|
428 Link URL: <input type="text" name="http_auth_password_url" value="<?php echo htmlspecialchars(getConfig('http_auth_password_url', '')); ?>" size="30" /> |
|
429 </td> |
|
430 </tr> |
|
431 |
|
432 <tr> |
|
433 <th class="subhead" colspan="2"> |
|
434 <input type="submit" name="submit" value="Save changes" /> |
|
435 </th> |
|
436 </tr> |
|
437 </table> |
|
438 </div> |
|
439 <?php |
|
440 echo '</form>'; |
|
441 } |