lib.php 14.5 KB
Newer Older
1
2
3
4
5
6
<?php
/**
 *
 * @package    mahara
 * @subpackage auth-browserid
 * @author     Francois Marier <francois@catalyst.net.nz>
7
8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
Aaron Wells's avatar
Aaron Wells committed
9
 *
10
11
12
13
14
15
16
17
18
19
20
 */

defined('INTERNAL') || die();
require_once(get_config('docroot') . 'auth/lib.php');
require_once(get_config('docroot') . 'lib/institution.php');

class AuthBrowserid extends Auth {
    public function __construct($id = null) {
        $this->has_instance_config = true;
        $this->type = 'browserid';

21
        $this->config['weautocreateusers'] = 0;
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
        if (!empty($id)) {
            return $this->init($id);
        }
        return true;
    }

    public function init($id) {
        $this->ready = parent::init($id);
        return $this->ready;
    }

    public function authenticate_user_account($user, $password) {
        // Authentication is done elsewhere in Javascript
        return false;
    }

    public function can_auto_create_users() {
        // The normal user auto creation process doesn't work for this backend
        return false;
    }

    public function create_new_user($email) {
        if (!$this->config['weautocreateusers']) {
            return null;
        }

        if (record_exists('artefact_internal_profile_email', 'email', $email)) {
49
            throw new AccountAutoCreationException(get_string('emailalreadyclaimed', 'auth.browserid', $email));
50
51
52
        }

        if (record_exists('usr', 'username', $email)) {
53
            throw new AccountAutoCreationException(get_string('emailclaimedasusername', 'auth.browserid', $email));
54
55
        }

56
        // Personal details are currently not provided by the Persona API.
57
        $user = new stdClass();
58
59
60
61
62
        $user->username = $email;
        $user->firstname = '';
        $user->lastname = '';
        $user->email = $email;

63
        // no need for a password on Persona accounts
64
65
66
67
        $user->password = '';
        $user->passwordchange = 0;
        $user->authinstance = $this->instanceid;

68
69
70
71
72
        // Set default values to activate this user
        $user->deleted = 0;
        $user->expiry = null;
        $user->suspendedcusr = null;

73
74
75
76
77
78
79
80
        $user->id = create_user($user, array(), $this->institution);

        return $user;
    }
}

class PluginAuthBrowserid extends PluginAuth {

81
82
    const BROWSERID_VERIFIER_URL = 'https://verifier.login.persona.org/verify';

83
    private static $default_config = array(
84
        'weautocreateusers' => 0,
85
86
87
88
89
90
91
92
93
94
95
96
97
    );

    public static function has_config() {
        return false;
    }

    public static function get_config_options() {
        return array();
    }

    public static function has_instance_config() {
        return true;
    }
98
99
100
101
102
    /**
     * Implement the function is_usable()
     *
     * @return boolean true if the BrowserID verifier is usable, false otherwise
     */
103
    public static function is_usable() {
104
105
106
107
        if ( extension_loaded('curl')) {
            return self::is_available();
        }
        return false;
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    }

    public static function get_instance_config_options($institution, $instance = 0) {

        if ($instance > 0) {
            $current_config = get_records_menu('auth_instance_config', 'instance', $instance, '', 'field, value');

            if ($current_config == false) {
                $current_config = array();
            }

            foreach (self::$default_config as $key => $value) {
                if (array_key_exists($key, $current_config)) {
                    self::$default_config[$key] = $current_config[$key];
                }
            }
        }

        $elements = array(
            'instance' => array(
                'type'  => 'hidden',
                'value' => $instance,
            ),
            'institution' => array(
                'type'  => 'hidden',
                'value' => $institution,
            ),
            'authname' => array(
                'type'  => 'hidden',
                'value' => 'browserid',
            ),
            'instancename' => array(
                'type'  => 'hidden',
141
                'value' => 'Persona',
142
143
144
145
146
147
            ),
            'authname' => array(
                'type'  => 'hidden',
                'value' => 'browserid',
            ),
            'weautocreateusers' => array(
148
                'type'         => 'switchbox',
149
150
151
152
153
154
155
156
                'title'        => get_string('weautocreateusers', 'auth'),
                'defaultvalue' => self::$default_config['weautocreateusers'],
                'help'         => true
            ),
        );

        return array(
            'elements' => $elements,
157
            'renderer' => 'div'
158
159
160
        );
    }

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    /**
     * Function to check a BrowserID verifier status
     * @return boolean true if the verifier is available, false otherwise
     */
    public static function is_available(){
        // Send a test assertion to the verification service
        $request = array(
                CURLOPT_URL        => self::BROWSERID_VERIFIER_URL,
                CURLOPT_POST       => 1,
                CURLOPT_POSTFIELDS => 'request=1'
        );
        $response = mahara_http_request($request);
        if (!empty($response->data)) {
            $jsondata = json_decode($response->data);
            return !empty($jsondata);
        }
        return false;
    }

Son Nguyen's avatar
Son Nguyen committed
180
    public static function save_instance_config_options($values, Pieform $form) {
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

        $authinstance = new stdClass();

        if ($values['instance'] > 0) {
            $values['create'] = false;
            $current = get_records_assoc('auth_instance_config', 'instance', $values['instance'], '', 'field, value');
            $authinstance->id = $values['instance'];
        }
        else {
            $values['create'] = true;
            $lastinstance = get_records_array('auth_instance', 'institution', $values['institution'], 'priority DESC', '*', '0', '1');

            if ($lastinstance == false) {
                $authinstance->priority = 0;
            }
            else {
                $authinstance->priority = $lastinstance[0]->priority + 1;
            }
        }

        $authinstance->institution  = $values['institution'];
        $authinstance->authname     = $values['authname'];
        $authinstance->instancename = $values['instancename'];

        if ($values['create']) {
            $values['instance'] = insert_record('auth_instance', $authinstance, 'id', true);
        }
        else {
            update_record('auth_instance', $authinstance, array('id' => $values['instance']));
        }

        if (empty($current)) {
            $current = array();
        }

        self::$default_config = array('weautocreateusers' => $values['weautocreateusers']);

        foreach(self::$default_config as $field => $value) {
            $record = new stdClass();
            $record->instance = $values['instance'];
            $record->field = $field;
            $record->value = $value;

            if ($values['create'] || !array_key_exists($field, $current)) {
                insert_record('auth_instance_config', $record);
            }
            else {
                update_record('auth_instance_config', $record, array('instance' => $values['instance'], 'field' => $field));
            }
        }

        return $values;
    }

    /**
236
     * Add a Persona link/button.
237
238
239
240
     */
    public static function login_form_elements() {
        return array(
            'loginbrowserid' => array(
241
                'value' => '<div class="login-externallink"><a class="persona-button btn btn-primary btn-xs" href="javascript:window.browserid_login()"><span>' . get_string('login', 'auth.browserid') . '</span></a></div>'
242
243
244
245
246
            )
        );
    }

    /**
247
     * Load all of the Javascript needed to retrieve Personas from
248
249
250
     * the browser.
     */
    public static function login_form_js() {
251
        global $HEADDATA, $SESSION;
252
        $HEADDATA[] = '<script src="https://login.persona.org/include.js" type="application/javascript"></script>';
253
        $wwwroot = get_config('wwwroot');
254
255
256
257
258
        $returnurl = hsc(get_relative_script_path());
        // We can't use $USER->get('sesskey') because there is no $USER object yet.
        $sesskey = get_random_key();
        $SESSION->set('browseridsesskey', $sesskey);

259
260
261
        return <<< EOF
<form id="browserid-form" action="{$wwwroot}auth/browserid/login.php" method="post">
<input id="browserid-assertion" type="hidden" name="assertion" value="">
262
263
<input id="browserid-returnurl" type="hidden" name="returnurl" value="{$returnurl}">
<input id="browserid-sesskey" type="hidden" name="sesskey" value="{$sesskey}">
264
265
266
<input style="display: none" type="submit">
</form>

267
<script type="application/javascript">
268
function browserid_login() {
269
    navigator.id.get(function(assertion) {
270
271
272
273
274
275
276
277
278
279
280
281
282
        if (assertion) {
            document.getElementById('browserid-assertion').setAttribute('value', assertion);
            document.getElementById('browserid-form').submit();
        }
   });
}
</script>
EOF;
    }

    public static function need_basic_login_form() {
        return false;
    }
283
284
285
286
287
288
289
290
291
292
293

    public static function postinst($fromversion) {
        // Deactivate for new installs or if not in use.
        if ($fromversion == 0 || 0 == count_records('auth_instance', 'authname', 'browserid')) {
            set_field('auth_installed', 'active', 0, 'name', 'browserid');
        }
    }

    public static function can_be_disabled() {
        return true;
    }
294
295
296

    public static function is_deprecated() {
        return true;
297
    }
298
299
300
}

class BrowserIDUser extends LiveUser {
301
    public function login($username, $password=null) {
302
303
304
305
        // This will do one of 3 things
        // 1 - If a user has an account, log them in
        // 2 - If a user doesn't have an account, and there is an auth method (which also has weautocreate), create acc and login
        // 3 - If a user doesn't have an account, and there is more than one auth method, show a registration page
306
307
308
309
310
311
312
313
314
        $sql = "SELECT
                    a.id, i.name AS institutionname
                FROM
                    {auth_instance} a
                JOIN
                    {institution} i ON a.institution = i.name
                WHERE
                    a.authname = 'browserid' AND
                    i.suspended = 0";
315
        $authinstances = get_records_sql_array($sql, array());
316
        if (!$authinstances) {
317
            throw new ConfigException(get_string('browseridnotenabled', 'auth.browserid'));
318
319
        }

320
321
        $autocreate = array(); // Remember the authinstances that are happy to create users

322
323
324
325
326
        foreach ($authinstances as $authinstance) {
            $auth = AuthFactory::create($authinstance->id);

            $institutionjoin = '';
            $institutionwhere = '';
327
            $sqlvalues = array($username);
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
            if ($authinstance->institutionname != 'mahara') {
                // Make sure that user is in the right institution
                $institutionjoin = 'JOIN {usr_institution} ui ON ui.usr = u.id';
                $institutionwhere = 'AND ui.institution = ?';
                $sqlvalues[] = $authinstance->institutionname;
            }

            $sql = "SELECT
                        u.*,
                        " . db_format_tsfield('u.expiry', 'expiry') . ",
                        " . db_format_tsfield('u.lastlogin', 'lastlogin') . ",
                        " . db_format_tsfield('u.lastlastlogin', 'lastlastlogin') . ",
                        " . db_format_tsfield('u.lastaccess', 'lastaccess') . ",
                        " . db_format_tsfield('u.suspendedctime', 'suspendedctime') . ",
                        " . db_format_tsfield('u.ctime', 'ctime') . "
                    FROM
                        {usr} u
                    JOIN
                        {artefact_internal_profile_email} a ON a.owner = u.id
                    $institutionjoin
                    WHERE
                        a.verified = 1 AND
                        a.email = ?
                    $institutionwhere";
            $user = get_record_sql($sql, $sqlvalues);
            if (!$user) {
354
355
356
357
358
359
360
361
                if ($auth->weautocreateusers) {
                    if ($authinstance->institutionname == 'mahara') {
                        array_unshift($autocreate, $auth); // Try "No Instititution" first when creating users below
                    }
                    else {
                        $autocreate[] = $auth;
                    }
                }
362
363
364
365
366
367
368
369
370
371
372
373
                continue; // skip to the next auth_instance
            }

            if (is_site_closed($user->admin)) {
                return false;
            }
            ensure_user_account_is_active($user);

            $this->authenticate($user, $auth->instanceid);
            return true;
        }

374
        foreach ($autocreate as $auth) {
375
            if (!$user = $auth->create_new_user($username)) {
376
377
                continue;
            }
378
            $this->authenticate($user, $auth->instanceid);
379
            return;
380
        }
381

382
383
384
        // Autocreation failed; try registration.
        list($form, $registerconfirm) = auth_generate_registration_form('register', 'browserid', '/register.php');
        if (!$form) {
385
            throw new AuthUnknownUserException(get_string('emailnotfound', 'auth.browserid', $username));
386
        }
387
388
389
        if (record_exists('usr', 'email', $username)
                || record_exists('artefact_internal_profile_email', 'email', $username)) {
            throw new AuthUnknownUserException(get_string('emailalreadytaken', 'auth.internal', $username));
390
391
392
        }
        $form['elements']['email'] = array(
            'type' => 'hidden',
393
            'value' => $username
394
395
396
397
398
399
400
401
402
403
404
405
        );
        $form['elements']['authtype'] = array(
            'type' => 'hidden',
            'value' => 'browserid'
        );
        list($formhtml, $js) = auth_generate_registration_form_js($form, $registerconfirm);

        $registerdescription = get_string('registerwelcome');
        if ($registerterms = get_config('registerterms')) {
            $registerdescription .= ' ' . get_string('registeragreeterms');
        }
        $registerdescription .= ' ' . get_string('registerprivacy');
406
        define('TITLE', get_string('register', 'auth.browserid'));
407
        $smarty = smarty();
408
409
410
411
        $smarty->assign('register_form', $formhtml);
        $smarty->assign('registerdescription', $registerdescription);
        if ($registerterms) {
            $smarty->assign('termsandconditions', get_site_page_content('termsandconditions'));
412
        }
413
414
415
        $smarty->assign('INLINEJAVASCRIPT', $js);
        $smarty->display('register.tpl');
        die;
416
417
    }
}