lib.php 12 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
/**
 * This program is part of Mahara
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * @package    mahara
Nigel McNie's avatar
Nigel McNie committed
20
 * @subpackage auth
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 * @author     Nigel McNie <nigel@catalyst.net.nz>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006,2007 Catalyst IT Ltd http://catalyst.net.nz
 *
 */

defined('INTERNAL') || die();

/** Exception - unknown user */
class AuthUnknownUserException extends Exception {}

/**
 * Base authentication class. Provides a common interface with which
 * authentication can be carried out for system users.
 */
abstract class Auth {

    /**
     * Given a username, password and institution, attempts to log the use in.
     *
     * @param string $username  The username to attempt to authenticate
     * @param string $password  The password to use for the attempt
     * @param string $institute The institution the user belongs to
     * @return bool             Whether the authentication was successful
     * @throws AuthUnknownUserException  If the user is unknown to the
     *                                   authentication method
     */
    public static abstract function authenticate_user_account($username, $password, $institute);

    /**
     * Given a username, returns a hash of information about a user.
     *
     * @param string $username The username to look up information for
     * @return array           The information for the user
     * @throws AuthUnknownUserException If the user is unknown to the
     *                                  authentication method
     */
58
    public static abstract function get_user_info($username);
59
60
61
62
63
64
65

    /**
     * Returns a hash of information that will be rendered into a form
     * when configuring authentication.
     *
     * @return array
     */
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
    public static function get_configuration_form() {
    }

    protected static function build_form($method, $elements) {
        if (count($elements)) {
            $elements['submit'] = array(
                'type' => 'submit',
                'value' => 'Update'
            );
            $elements['method'] = array(
                'type' => 'hidden',
                'value' => $method 
            );
            return array(
                'name' => 'auth',
                'elements' => $elements
            );
        }
        return false;
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    }

    /**
     * Returns the internal name of the auth plugin.
     */
    public static function get_internal_name() {
        return get_string('internalname', 'auth');
    }

    /**
     * Returns the human readable name of the auth plugin.
     */
    public static function get_name() {
        return get_string('name', 'auth');
    }

    /**
     * Returns the descriptoin of the auth plugin
     */
    public static function get_description() {
        return get_string('description', 'auth');
    }

}


/**
 * Handles authentication by setting up a session for a user if they are logged
 * in.
 *
 * This function combined with the Session class is smart - if the user is not
 * logged in then they do not get a session, which prevents simple curl hits
 * or search engine crawls to a page from getting sessions they won't use.
 *
 * Once the user has a session, they keep it even if the log out, so it can
 * be reused. The session does expire, but the expiry time is typically a week
 * or more.
 */
function auth_setup () {
124
    global $SESSION, $USER;
125
126
127
128
129
130
131
132
133
134
135

    // If the system is not installed, let the user through in the hope that
    // they can fix this little problem :)
    if (!get_config('installed')) {
        log_dbg('system not installed, letting user through');
        return;
    }

    // Check the time that the session is set to log out. If the user does
    // not have a session, this time will be 0.
    $sessionlogouttime = $SESSION->get('logout_time');
136
    if ($sessionlogouttime > time()) {
137
138
139
140
        if (isset($_GET['logout'])) {
            log_dbg('logging user ' . $SESSION->get('username') . ' out');
            $SESSION->logout();
            $SESSION->add_ok_msg(get_string('loggedoutok', 'mahara'));
141
            redirect(get_config('wwwroot'));
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
        }
        // The session is still active, so continue it.
        log_dbg('session still active from previous time');
        return $SESSION->renew();
    }
    else if ($sessionlogouttime > 0) {
        // The session timed out
        log_dbg('session timed out');
        $SESSION->logout();
        $SESSION->add_info_msg(get_string('sessiontimedout', 'mahara'));
        // @todo<nigel>: if page is public, no need to show the login page again
        auth_draw_login_page();
        exit;
    }
    else {
        // There is no session, so we check to see if one needs to be started.
        // First, check if the page is public or the site is configured to be public.
        // @todo<nigel>: implement this :)
        if (false) {
            // No need to hand out a session for such pages
            return;
        }

165
166
167
168
169
170
171
        // Build login form. If the form is submitted it will be handled here,
        // and set $USER for us.
        require_once('form.php');
        $form = new Form(auth_get_login_form());
        if ($USER) {
            log_dbg('user logged in just fine');
            return;
172
        }
173
174
175
176
        
        log_dbg('no session or old session, and page is private hello');
        auth_draw_login_page($form);
        exit;
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    }
}

/**
 * Given an institution, returns the authentication method used by it.
 *
 * @return string
 * @todo<nigel>: Currently, the system doesn't have a concept of institution
 * at the database level, so the internal authentication method is assumed.
 */
function auth_get_authtype_for_institution($institution) {
    return 'internal';
}

/**
 * Creates and displays the transient login page
 *
 */
195
196
197
198
199
200
201
202
function auth_draw_login_page($form=null) {
    if ($form != null) {
        $loginform = $form->build();
    }
    else {
        require_once('form.php');
        $loginform = form(auth_get_login_form());
    }
203
    $smarty = smarty();
204
205
206
207
    $smarty->assign('login_form', $loginform);
    $smarty->display('login.tpl');
    exit;
}
208

209
210
211
212
213
214
/**
 * Returns the definition of the login form
 *
 * @return array
 */
function auth_get_login_form() {
215
216
217
218
219
220
221
222
223
224
    $elements = array(
        'login' => array(
            'type'   => 'fieldset',
            'legend' => get_string('login', 'mahara'),
            'elements' => array(
                'login_username' => array(
                    'type'        => 'text',
                    'title'       => get_string('username', 'mahara'),
                    'description' => get_string('usernamedesc', 'mahara'),
                    'help'        => get_string('usernamehelp', 'mahara'),
225
226
227
                    'rules' => array(
                        'required'    => true
                    )
228
229
230
231
232
233
                ),
                'login_password' => array(
                    'type'        => 'password',
                    'title'       => get_string('password', 'mahara'),
                    'description' => get_string('passworddesc', 'mahara'),
                    'help'        => get_string('passwordhelp', 'mahara'),
234
235
236
237
                    'value'       => '',
                    'rules' => array(
                        'required'    => true
                    )
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
                )
            )
        ),

        'submit' => array(
            'type'  => 'submit',
            'value' => get_string('login', 'mahara')
        )
    );

    // The login page is completely transient, and it is smart because it
    // remembers the GET and POST data sent to it and resends that on
    // afterwards. 
    $action = '';
    if ($_GET) {
        if (isset($_GET['logout'])) {
            // You can log the user out on any particular page by appending
            // ?logout to the URL. In this case, we don't want the "action"
            // of the url to include that, or be blank, else the next time
            // the user logs in they will be logged out again.
            $action = hsc(substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?')));
        } else {
            $action = '?';
            foreach ($_GET as $key => $value) {
                if ($key != 'logout') {
                    $action .= hsc($key) . '=' . hsc($value) . '&amp;';
                }
            }
            $action = substr($action, 0, -5);
        }
    }
    if ($_POST) {
        foreach ($_POST as $key => $value) {
            // @todo<nigel>: probably won't pass arrays properly
            if (!isset($elements[$key]) && !isset($elements['login']['elements'][$key])) {
                $elements[$key] = array(
                    'type'  => 'hidden',
                    'value' => $value
                );
            }
        }
    }

    $form = array(
282
283
284
        'name'     => 'login',
        'method'   => 'post',
        'action'   => $action,
285
286
287
        'elements' => $elements
    );

288
    return $form;
289
290
}

291

292
/**
293
294
295
296
 * Helper for validating the login form
 *
 * @param Form $form The form that is being validated
 * @param array $values  The submitted values
297
 */
298
function login_validate($form, $values) {
299
    if (!$form->get_error('login_username') && !validate_username($values['login_username'])) {
300
301
302
303
304
        $form->set_error('login_username', get_string('Username is not in valid form, it can only'
           . ' contain alphanumeric characters, underscores, full stops and @ symbols', 'mahara'));
    }
}

305
/**
306
307
 * Called when the login form is submittd. Validates the user and password, and
 * if they are valid, starts a new session for the user.
308
 *
309
 * @param array $values The submitted values
310
 */
311
function login_submit($values) {
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    global $SESSION, $USER;

    log_dbg('auth details supplied, attempting to log user in');
    $username    = $values['login_username'];
    $password    = $values['login_password'];
    $institution = (isset($values['login_institution'])) ? $values['login_institution'] : 0;
            
    $authtype = auth_get_authtype_for_institution($institution);
    safe_require('auth', $authtype, 'lib.php', 'require_once');
    $authclass = 'Auth_' . ucfirst($authtype);
    try {
        if (call_static_method($authclass, 'authenticate_user_account', $username, $password, $institution)) {
            log_dbg('user ' . $username . ' logged in OK');
            $USER = call_static_method($authclass, 'get_user_info', $username);
            $SESSION->login($USER);
            $USER->logout_time = $SESSION->get('logout_time');
        }
        else {
            // Login attempt failed
            log_dbg('login attempt FAILED');
            $SESSION->add_err_msg(get_string('loginfailed', 'mahara'));
        }
    }
    catch (AuthUnknownUserException $e) {
        log_dbg('unknown user ' . $username);
        $SESSION->add_err_msg(get_string('loginfailed', 'mahara'));
    }
339
340
341
}

// handles form submission when an admin options form is submitted
342
// @todo<nigel>: allow each auth method to have a validation method
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
function auth_submit($values) {
    global $SESSION, $db;
    $db->StartTrans();

    foreach ($values as $key => $value) {
        if (!in_array($key, array('submit', 'method'))) {
            set_config_plugin('auth', $values['method'], $key, $value);
        }
    }
    if ($db->HasFailedTrans()) {
        $db->CompleteTrans();
        throw new Exception('Could not update the configuration options for the auth method');
    }
    $db->CompleteTrans();
    $SESSION->add_ok_msg(get_string('authconfigurationoptionssaved') . ' ' . get_config_plugin('auth', $values['method'], $key));
358
359
360
}

?>