Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/auth/sso/sso.Drupal.class.php (1 issue)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 * This file contains the necessary elements to implement a Single Sign On
8
 * mechanism with an external Drupal application (on which the Chamilo module
9
 * 7.x-1.0-alpha3 or above must be implemented).
10
 *
11
 * To use this class, set variable "sso_authentication_subclass" to "Drupal"
12
 * in Chamilo settings.  If not yet available in the "Security" tab, execute the
13
 * following on the Chamilo database:
14
 *  INSERT INTO `settings_current` (`variable`, `type`, `category`, `selected_value`, `title`, `comment`, `access_url`)
15
 *  VALUES ('sso_authentication_subclass', 'textfield', 'Security', 'Drupal', 'SSOSubclass', 'SSOSubclassComment', 1);
16
 *
17
 * @package chamilo.auth.sso
18
 */
19
20
/**
21
 * The SSO class allows for management of remote Single Sign On resources.
22
 */
23
class ssoDrupal
24
{
25
    public $protocol; // 'http://',
26
    public $domain; // 'localhost/project/drupal',
27
    public $auth_uri; // '/?q=user',
28
    public $deauth_uri; // '/?q=logout',
29
    public $referer; // http://my.chamilo.com/main/auth/profile.php
30
31
    /**
32
     * Instanciates the object, initializing all relevant URL strings.
33
     */
34
    public function __construct()
35
    {
36
        $this->protocol = api_get_setting('sso_authentication_protocol');
37
        // There can be multiple domains, so make sure to take only the first
38
        // This might be later extended with a decision process
39
        $domains = preg_split('/,/', api_get_setting('sso_authentication_domain'));
40
        $this->domain = trim($domains[0]);
41
        $this->auth_uri = api_get_setting('sso_authentication_auth_uri');
42
        $this->deauth_uri = api_get_setting('sso_authentication_unauth_uri');
43
        //cut the string to avoid recursive URL construction in case of failure
44
        $this->referer = $this->protocol.$_SERVER['HTTP_HOST'].substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], 'sso'));
45
        $this->deauth_url = $this->protocol.$this->domain.$this->deauth_uri;
46
        $this->master_url = $this->protocol.$this->domain.$this->auth_uri;
47
        $this->referrer_uri = base64_encode($_SERVER['REQUEST_URI']);
48
        $this->target = api_get_path(WEB_PATH);
49
    }
50
51
    /**
52
     * Unlogs the user from the remote server.
53
     */
54
    public function logout()
55
    {
56
        // no_redirect means Drupal sent the signal to logout. When redirecting to Drupal, the $_GET['stop'] param is
57
        // set to 1, to allow Drupal to know that this is it, the logout is already done in Chamilo and there's no
58
        // need to do it again
59
        if (empty($_GET['no_redirect'])) {
60
            header('Location: '.$this->deauth_url.'&stop=1');
61
        } else {
62
            header('Location: '.$this->protocol.$this->domain);
63
        }
64
        exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
65
    }
66
67
    /**
68
     * Sends the user to the master URL for a check of active connection.
69
     */
70
    public function ask_master()
71
    {
72
        // Generate a single usage token that must be encoded by the master
73
        $_SESSION['sso_challenge'] = api_generate_password(48);
74
        // Redirect browser to the master URL
75
        $params = '';
76
        if (empty($_GET['no_redirect'])) {
77
            $params = 'sso_referer='.urlencode($this->referer).
78
                '&sso_target='.urlencode($this->target).
79
                '&sso_challenge='.urlencode($_SESSION['sso_challenge']).
80
                '&sso_ruri='.urlencode($this->referrer_uri);
81
            if (strpos($this->master_url, "?") === false) {
82
                $params = "?{$params}";
83
            } else {
84
                $params = "&{$params}";
85
            }
86
        }
87
        header('Location: '.$this->master_url.$params);
88
        exit;
89
    }
90
91
    /**
92
     * Validates the received active connection data with the database.
93
     *
94
     * @return false|null Return the loginFailed variable value to local.inc.php
95
     */
96
    public function check_user()
97
    {
98
        global $_user;
99
        $loginFailed = false;
100
101
        //change the way we recover the cookie depending on how it is formed
102
        $sso = $this->decode_cookie($_GET['sso_cookie']);
103
104
        //get token that should have been used and delete it
105
        //from session since it can only be used once
106
        $sso_challenge = '';
107
        if (isset($_SESSION['sso_challenge'])) {
108
            $sso_challenge = $_SESSION['sso_challenge'];
109
            unset($_SESSION['sso_challenge']);
110
        }
111
112
        //lookup the user in the main database
113
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
114
        $sql = "SELECT id, username, password, auth_source, active, expiration_date, status
115
                FROM $user_table
116
                WHERE username = '".trim(Database::escape_string($sso['username']))."'";
117
        $result = Database::query($sql);
118
        if (Database::num_rows($result) > 0) {
119
            $uData = Database::fetch_array($result);
120
            //Check the user's password
121
            if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE) {
122
                if ($sso['secret'] === sha1($uData['username'].$sso_challenge.api_get_security_key())
123
                    && ($sso['username'] == $uData['username'])) {
124
                    //Check if the account is active (not locked)
125
                    if ($uData['active'] == '1') {
126
                        // check if the expiration date has not been reached
127
                        if (empty($uData['expiration_date']) or $uData['expiration_date'] > date('Y-m-d H:i:s') or $uData['expiration_date'] == '0000-00-00 00:00:00') {
128
                            //If Multiple URL is enabled
129
                            if (api_get_multiple_access_url()) {
130
                                //Check the access_url configuration setting if the user is registered in the access_url_rel_user table
131
                                //Getting the current access_url_id of the platform
132
                                $current_access_url_id = api_get_current_access_url_id();
133
                                // my user is subscribed in these
134
                                //sites: $my_url_list
135
                                $my_url_list = api_get_access_url_from_user($uData['id']);
136
                            } else {
137
                                $current_access_url_id = 1;
138
                                $my_url_list = [1];
139
                            }
140
141
                            $my_user_is_admin = UserManager::is_admin($uData['id']);
142
143
                            if ($my_user_is_admin === false) {
144
                                if (is_array($my_url_list) && count($my_url_list) > 0) {
145
                                    if (in_array($current_access_url_id, $my_url_list)) {
146
                                        // the user has permission to enter at this site
147
                                        $_user['user_id'] = $uData['id'];
148
                                        $_user = api_get_user_info($_user['user_id']);
149
                                        $_user['uidReset'] = true;
150
                                        Session::write('_user', $_user);
151
                                        Event::eventLogin($_user['user_id']);
152
                                        // Redirect to homepage
153
                                        $sso_target = '';
154
                                        if (!empty($sso['ruri'])) {
155
                                            //The referrer URI is *only* used if
156
                                            // the user credentials are OK, which
157
                                            // should be protection enough
158
                                            // against evil URL spoofing...
159
                                            $sso_target = api_get_path(WEB_PATH).base64_decode($sso['ruri']);
160
                                        } else {
161
                                            $sso_target = isset($sso['target']) ? $sso['target'] : api_get_path(WEB_PATH).'index.php';
162
                                        }
163
                                        header('Location: '.$sso_target);
164
                                        exit;
165
                                    } else {
166
                                        // user does not have permission for this site
167
                                        $loginFailed = true;
168
                                        Session::erase('_uid');
169
                                        header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
170
                                        exit;
171
                                    }
172
                                } else {
173
                                    // there is no URL in the multiple
174
                                    // urls list for this user
175
                                    $loginFailed = true;
176
                                    Session::erase('_uid');
177
                                    header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
178
                                    exit;
179
                                }
180
                            } else {
181
                                //Only admins of the "main" (first) Chamilo
182
                                // portal can login wherever they want
183
                                if (in_array(1, $my_url_list)) {
184
                                    //Check if this admin is admin on the
185
                                    // principal portal
186
                                    $_user['user_id'] = $uData['id'];
187
                                    $_user = api_get_user_info($_user['user_id']);
188
                                    $is_platformAdmin = $uData['status'] == COURSEMANAGER;
189
                                    Session::write('is_platformAdmin', $is_platformAdmin);
190
                                    Session::write('_user', $_user);
191
                                    Event::eventLogin($_user['user_id']);
192
                                } else {
193
                                    //Secondary URL admin wants to login
194
                                    // so we check as a normal user
195
                                    if (in_array($current_access_url_id, $my_url_list)) {
196
                                        $_user['user_id'] = $uData['user_id'];
197
                                        $_user = api_get_user_info($_user['user_id']);
198
                                        Session::write('_user', $_user);
199
                                        Event::eventLogin($_user['user_id']);
200
                                    } else {
201
                                        $loginFailed = true;
202
                                        Session::erase('_uid');
203
                                        header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
204
                                        exit;
205
                                    }
206
                                }
207
                            }
208
                        } else {
209
                            // user account expired
210
                            $loginFailed = true;
211
                            Session::erase('_uid');
212
                            header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_expired');
213
                            exit;
214
                        }
215
                    } else {
216
                        //User not active
217
                        $loginFailed = true;
218
                        Session::erase('_uid');
219
                        header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_inactive');
220
                        exit;
221
                    }
222
                } else {
223
                    //SHA1 of password is wrong
224
                    $loginFailed = true;
225
                    Session::erase('_uid');
226
                    header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_password');
227
                    exit;
228
                }
229
            } else {
230
                //Auth_source is wrong
231
                $loginFailed = true;
232
                Session::erase('_uid');
233
                header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_authentication_source');
234
                exit;
235
            }
236
        } else {
237
            //No user by that login
238
            $loginFailed = true;
239
            Session::erase('_uid');
240
            header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_not_found');
241
            exit;
242
        }
243
244
        return $loginFailed;
245
    }
246
247
    /**
248
     * Generate the URL for profile editing for a any user or the current user.
249
     *
250
     * @param int  $userId  Optional. The user id
251
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
252
     *
253
     * @return string If the URL is obtained return the drupal_user_id. Otherwise return false
254
     */
255
    public function generateProfileEditingURL($userId = 0, $asAdmin = false)
256
    {
257
        $userId = intval($userId);
258
259
        if (empty($userId)) {
260
            $userId = api_get_user_id();
261
        }
262
263
        $userExtraFieldValue = new ExtraFieldValue('user');
264
        $drupalUserIdData = $userExtraFieldValue->get_values_by_handler_and_field_variable(
265
            $userId,
266
            'drupal_user_id'
267
        );
268
269
        // If this is an administrator, allow him to make some changes in
270
        // the Chamilo profile
271
        if ($asAdmin && api_is_platform_admin(true)) {
272
            return api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=$userId";
273
        }
274
        // If the user doesn't match a Drupal user, give the normal profile
275
        // link
276
        if ($drupalUserIdData === false) {
277
            return api_get_path(WEB_CODE_PATH).'auth/profile.php';
278
        }
279
        // In all other cases, generate a link to the Drupal profile edition
280
        $drupalUserId = $drupalUserIdData['value'];
281
        $url = "{$this->protocol}{$this->domain}/user/{$drupalUserId}/edit";
282
283
        return $url;
284
    }
285
286
    /**
287
     * Decode the cookie (this function may vary depending on the
288
     * Single Sign On implementation.
289
     *
290
     * @param	string	Encoded cookie
291
     *
292
     * @return array Parsed and unencoded cookie
293
     */
294
    private function decode_cookie($cookie)
295
    {
296
        return UnserializeApi::unserialize(
297
            'not_allowed_classes',
298
            base64_decode($cookie)
299
        );
300
    }
301
}
302