Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Authentication Plugin: Shibboleth Authentication
19
 * Authentication using Shibboleth.
20
 *
21
 * Distributed under GPL (c)Markus Hagman 2004-2006
22
 *
23
 * @package auth_shibboleth
24
 * @author Martin Dougiamas
25
 * @author Lukas Haemmerle
26
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
27
 */
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
require_once($CFG->libdir.'/authlib.php');
32
 
33
/**
34
 * Shibboleth authentication plugin.
35
 */
36
class auth_plugin_shibboleth extends auth_plugin_base {
37
 
38
    /**
39
     * Constructor.
40
     */
41
    public function __construct() {
42
        $this->authtype = 'shibboleth';
43
        $this->config = get_config('auth_shibboleth');
44
    }
45
 
46
    /**
47
     * Old syntax of class constructor. Deprecated in PHP7.
48
     *
49
     * @deprecated since Moodle 3.1
50
     */
51
    public function auth_plugin_shibboleth() {
52
        debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
53
        self::__construct();
54
    }
55
 
56
    /**
57
     * Returns true if the username and password work and false if they are
58
     * wrong or don't exist.
59
     *
60
     * @param string $username The username (with system magic quotes)
61
     * @param string $password The password (with system magic quotes)
62
     * @return bool Authentication success or failure.
63
     */
64
    function user_login($username, $password) {
65
       global $SESSION;
66
 
67
        // If we are in the shibboleth directory then we trust the server var
68
        if (!empty($_SERVER[$this->config->user_attribute])) {
69
            // Associate Shibboleth session with user for SLO preparation
70
            $sessionkey = '';
71
            if (isset($_SERVER['Shib-Session-ID'])){
72
                // This is only available for Shibboleth 2.x SPs
73
                $sessionkey = $_SERVER['Shib-Session-ID'];
74
            } else {
75
                // Try to find out using the user's cookie
76
                foreach ($_COOKIE as $name => $value){
77
                    if (preg_match('/_shibsession_/i', $name)){
78
                        $sessionkey = $value;
79
                    }
80
                }
81
            }
82
 
83
            // Set shibboleth session ID for logout
84
            $SESSION->shibboleth_session_id  = $sessionkey;
85
 
86
            return (strtolower($_SERVER[$this->config->user_attribute]) == strtolower($username));
87
        } else {
88
            // If we are not, the user has used the manual login and the login name is
89
            // unknown, so we return false.
90
            return false;
91
        }
92
    }
93
 
94
 
95
 
96
    /**
97
     * Returns the user information for 'external' users. In this case the
98
     * attributes provided by Shibboleth
99
     *
100
     * @return array $result Associative array of user data
101
     */
102
    function get_userinfo($username) {
103
    // reads user information from shibboleth attributes and return it in array()
104
        global $CFG;
105
 
106
        // Check whether we have got all the essential attributes
107
        if ( empty($_SERVER[$this->config->user_attribute]) ) {
108
            throw new \moodle_exception( 'shib_not_all_attributes_error', 'auth_shibboleth' , '',
109
                    "'".$this->config->user_attribute."' ('".$_SERVER[$this->config->user_attribute]."'), '".
110
                $this->config->field_map_firstname."' ('".$_SERVER[$this->config->field_map_firstname]."'), '".
111
                $this->config->field_map_lastname."' ('".$_SERVER[$this->config->field_map_lastname]."') and '".
112
                $this->config->field_map_email."' ('".$_SERVER[$this->config->field_map_email]."')");
113
        }
114
 
115
        $attrmap = $this->get_attributes();
116
 
117
        $result = array();
118
        $search_attribs = array();
119
 
120
        foreach ($attrmap as $key=>$value) {
121
            // Check if attribute is present
122
            if (!isset($_SERVER[$value])){
123
                $result[$key] = '';
124
                continue;
125
            }
126
 
127
            // Make usename lowercase
128
            if ($key == 'username'){
129
                $result[$key] = strtolower($this->get_first_string($_SERVER[$value]));
130
            } else {
131
                $result[$key] = $this->get_first_string($_SERVER[$value]);
132
            }
133
        }
134
 
135
         // Provide an API to modify the information to fit the Moodle internal
136
        // data representation
137
        if (
138
              $this->config->convert_data
139
              && $this->config->convert_data != ''
140
              && is_readable($this->config->convert_data)
141
            ) {
142
 
143
            // Include a custom file outside the Moodle dir to
144
            // modify the variable $moodleattributes
145
            include($this->config->convert_data);
146
        }
147
 
148
        return $result;
149
    }
150
 
151
    /**
152
     * Returns array containg attribute mappings between Moodle and Shibboleth.
153
     *
154
     * @return array
155
     */
156
    function get_attributes() {
157
        $configarray = (array) $this->config;
158
 
159
        $moodleattributes = array();
160
        $userfields = array_merge($this->userfields, $this->get_custom_user_profile_fields());
161
        foreach ($userfields as $field) {
162
            if (isset($configarray["field_map_$field"])) {
163
                $moodleattributes[$field] = $configarray["field_map_$field"];
164
            }
165
        }
166
        $moodleattributes['username'] = $configarray["user_attribute"];
167
 
168
        return $moodleattributes;
169
    }
170
 
171
    function prevent_local_passwords() {
172
        return true;
173
    }
174
 
175
    /**
176
     * Returns true if this authentication plugin is 'internal'.
177
     *
178
     * @return bool
179
     */
180
    function is_internal() {
181
        return false;
182
    }
183
 
184
    /**
185
     * Whether shibboleth users can change their password or not.
186
     *
187
     * Shibboleth auth requires password to be changed on shibboleth server directly.
188
     * So it is required to have  password change url set.
189
     *
190
     * @return bool true if there's a password url or false otherwise.
191
     */
192
    function can_change_password() {
193
        if (!empty($this->config->changepasswordurl)) {
194
            return true;
195
        } else {
196
            return false;
197
        }
198
    }
199
 
200
    /**
201
     * Get password change url.
202
     *
203
     * @return moodle_url|null Returns URL to change password or null otherwise.
204
     */
205
    function change_password_url() {
206
        if (!empty($this->config->changepasswordurl)) {
207
            return new moodle_url($this->config->changepasswordurl);
208
        } else {
209
            return null;
210
        }
211
    }
212
 
213
     /**
214
     * Hook for login page
215
     *
216
     */
217
    function loginpage_hook() {
218
        global $SESSION, $CFG;
219
 
220
        // Prevent username from being shown on login page after logout
221
        $CFG->nolastloggedin = true;
222
 
223
        return;
224
    }
225
 
226
     /**
227
     * Hook for logout page
228
     *
229
     */
230
    function logoutpage_hook() {
231
        global $SESSION, $redirect;
232
 
233
        // Only do this if logout handler is defined, and if the user is actually logged in via Shibboleth
234
        $logouthandlervalid = isset($this->config->logout_handler) && !empty($this->config->logout_handler);
235
        if (isset($SESSION->shibboleth_session_id) && $logouthandlervalid ) {
236
            // Check if there is an alternative logout return url defined
237
            if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) {
238
                // Set temp_redirect to alternative return url
239
                $temp_redirect = $this->config->logout_return_url;
240
            } else {
241
                // Backup old redirect url
242
                $temp_redirect = $redirect;
243
            }
244
 
245
            // Overwrite redirect in order to send user to Shibboleth logout page and let him return back
246
            $redirecturl = new moodle_url($this->config->logout_handler, array('return' => $temp_redirect));
247
            $redirect = $redirecturl->out();
248
        }
249
    }
250
 
251
    /**
252
     * Cleans and returns first of potential many values (multi-valued attributes)
253
     *
254
     * @param string $string Possibly multi-valued attribute from Shibboleth
255
     */
256
    function get_first_string($string) {
257
        $list = explode( ';', $string);
258
        $clean_string = rtrim($list[0]);
259
 
260
        return $clean_string;
261
    }
262
 
263
    /**
264
     * Test if settings are correct, print info to output.
265
     */
266
    public function test_settings() {
267
        global $OUTPUT;
268
 
269
        if (!isset($this->config->user_attribute) || empty($this->config->user_attribute)) {
270
            echo $OUTPUT->notification(get_string("shib_not_set_up_error", "auth_shibboleth",
271
                (new moodle_url('/auth/shibboleth/README.txt'))->out()), 'notifyproblem');
272
            return;
273
        }
274
        if ($this->config->convert_data and $this->config->convert_data != '' and !is_readable($this->config->convert_data)) {
275
            echo $OUTPUT->notification(get_string("auth_shib_convert_data_warning", "auth_shibboleth"), 'notifyproblem');
276
            return;
277
        }
278
        if (isset($this->config->organization_selection) && empty($this->config->organization_selection) &&
279
                isset($this->config->alt_login) && $this->config->alt_login == 'on') {
280
 
281
            echo $OUTPUT->notification(get_string("auth_shib_no_organizations_warning", "auth_shibboleth"), 'notifyproblem');
282
            return;
283
        }
284
    }
285
 
286
    /**
287
     * Return a list of identity providers to display on the login page.
288
     *
289
     * @param string $wantsurl The requested URL.
290
     * @return array List of arrays with keys url, iconurl and name.
291
     */
292
    public function loginpage_idp_list($wantsurl) {
293
        $config = get_config('auth_shibboleth');
294
        $result = [];
295
 
296
        // Before displaying the button check that Shibboleth is set-up correctly.
297
        if (empty($config->user_attribute)) {
298
            return $result;
299
        }
300
 
301
        $url = new moodle_url('/auth/shibboleth/index.php');
302
 
303
        if ($config->auth_logo) {
304
            $iconurl = moodle_url::make_pluginfile_url(
305
                context_system::instance()->id,
306
                'auth_shibboleth',
307
                'logo',
308
                null,
309
                null,
310
                $config->auth_logo);
311
        } else {
312
            $iconurl = null;
313
        }
314
 
315
        $result[] = ['url' => $url, 'iconurl' => $iconurl, 'name' => $config->login_name];
316
        return $result;
317
    }
318
}
319
 
320
 
321
    /**
322
     * Sets the standard SAML domain cookie that is also used to preselect
323
     * the right entry on the local way
324
     *
325
     * @param string $selectedIDP IDP identifier
326
     */
327
    function set_saml_cookie($selectedIDP) {
328
        if (isset($_COOKIE['_saml_idp']))
329
        {
330
            $IDPArray = generate_cookie_array($_COOKIE['_saml_idp']);
331
        }
332
        else
333
        {
334
            $IDPArray = array();
335
        }
336
        $IDPArray = appendCookieValue($selectedIDP, $IDPArray);
337
        setcookie ('_saml_idp', generate_cookie_value($IDPArray), time() + (100*24*3600));
338
    }
339
 
340
    /**
341
     * Generate array of IdPs from Moodle Shibboleth settings
342
     *
343
     * @param string Text containing tuble/triple of IdP entityId, name and (optionally) session initiator
344
     * @return array Identifier of IdPs and their name/session initiator
345
     */
346
    function get_idp_list($organization_selection) {
347
        $idp_list = array();
348
 
349
        $idp_raw_list = explode("\n",  $organization_selection);
350
 
351
        foreach ($idp_raw_list as $idp_line){
352
            $idp_data = explode(',', $idp_line);
353
            if (isset($idp_data[2]))
354
            {
355
                $idp_list[trim($idp_data[0])] = array(trim($idp_data[1]),trim($idp_data[2]));
356
            }
357
            elseif(isset($idp_data[1]))
358
            {
359
                $idp_list[trim($idp_data[0])] = array(trim($idp_data[1]));
360
            }
361
        }
362
 
363
        return $idp_list;
364
    }
365
 
366
    /**
367
     * Generates an array of IDPs using the cookie value
368
     *
369
     * @param string Value of SAML domain cookie
370
     * @return array Identifiers of IdPs
371
     */
372
    function generate_cookie_array($value) {
373
 
374
        // Decodes and splits cookie value
375
        $CookieArray = explode(' ', $value);
376
        $CookieArray = array_map('base64_decode', $CookieArray);
377
 
378
        return $CookieArray;
379
    }
380
 
381
    /**
382
     * Generate the value that is stored in the cookie using the list of IDPs
383
     *
384
     * @param array IdP identifiers
385
     * @return string SAML domain cookie value
386
     */
387
    function generate_cookie_value($CookieArray) {
388
 
389
        // Merges cookie content and encodes it
390
        $CookieArray = array_map('base64_encode', $CookieArray);
391
        $value = implode(' ', $CookieArray);
392
        return $value;
393
    }
394
 
395
    /**
396
     * Append a value to the array of IDPs
397
     *
398
     * @param string IdP identifier
399
     * @param array IdP identifiers
400
     * @return array IdP identifiers with appended IdP
401
     */
402
    function appendCookieValue($value, $CookieArray) {
403
 
404
        array_push($CookieArray, $value);
405
        $CookieArray = array_reverse($CookieArray);
406
        $CookieArray = array_unique($CookieArray);
407
        $CookieArray = array_reverse($CookieArray);
408
 
409
        return $CookieArray;
410
    }
411
 
412
 
413