Projet

Général

Profil

Paste
Télécharger (11,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / cas / cas_server.module @ 4f315dab

1
<?php
2

    
3
/**
4
 * @file Provides a protocol compliant version of CAS server 2.x
5
 */
6
define('CAS_LOGIN_COOKIE', 'cas_server_login');
7

    
8
/**
9
 * Implementation of hook_menu
10
 */
11
function cas_server_menu() {
12
  $items = array();
13
  $items['cas/login'] = array(
14
    'page callback' => 'cas_server_login',
15
    'title' => 'CAS Login',
16
    'access callback' => TRUE,
17
    'type' => MENU_CALLBACK,
18
  );
19

    
20
  $items['cas/validate'] = array(
21
    'page callback' => 'cas_server_validate',
22
    'title' => 'CAS Validate',
23
    'access callback' => TRUE,
24
    'type' => MENU_CALLBACK,
25
  );
26

    
27
  $items['cas/serviceValidate'] = array(
28
    'page callback' => 'cas_server_service_validate',
29
    'title' => 'CAS Service Validate',
30
    'access callback' => TRUE,
31
    'type' => MENU_CALLBACK,
32
  );
33

    
34
  $items['cas/proxyValidate'] = array(
35
    'page callback' => 'cas_server_service_validate',
36
    'title' => 'CAS Proxy Ticket Validate',
37
    'access callback' => TRUE,
38
    'type' => MENU_CALLBACK,
39
  );
40

    
41
  $items['cas/logout'] = array(
42
    'page callback' => 'cas_server_logout',
43
    'title' => 'CAS Logout',
44
    'access callback' => TRUE,
45
    'type' => MENU_CALLBACK,
46
  );
47

    
48
  $items['admin/config/people/cas_server'] = array(
49
    'title' => 'CAS Server',
50
    'description' => 'Configure central authentication services server',
51
    'page callback' => 'drupal_get_form',
52
    'page arguments' => array('cas_server_admin_settings'),
53
    'access arguments' => array('administer cas server'),
54
    'type' => MENU_NORMAL_ITEM,
55
    'file' => 'cas_server.admin.inc',
56
  );
57

    
58
  $items['admin/config/people/cas_server/settings'] = array(
59
    'title' => 'Settings',
60
    'type' => MENU_DEFAULT_LOCAL_TASK,
61
    'weight' => -10,
62
  );
63

    
64
  return $items;
65
}
66

    
67
/**
68
 * Implements hook_permission().
69
 */
70
function cas_server_permission() {
71
  return array(
72
    'administer cas server' => array(
73
      'title' => t('Administer CAS Server'),
74
      'description' => t('Configure CAS server settings.'),
75
      'restrict access' => TRUE,
76
    )
77
  );
78
}
79

    
80
/**
81
 * Implements hook_theme().
82
 */
83
function cas_server_theme() {
84
  return array(
85
    'cas_service_validate_success' => array(
86
      'variables' => array('name' => NULL, 'attributes' => NULL),
87
      'file' => 'cas_server.response.inc',
88
    ),
89
    'cas_service_validate_attributes' => array(
90
      'variables' => array('attributes' => NULL, 'style' => 'jasig'),
91
      'file' => 'cas_server.response.inc',
92
    ),
93
    'cas_service_validate_failure' => array(
94
      'variables' => array('ticket' => NULL, 'error_code' => NULL),
95
      'file' => 'cas_server.response.inc',
96
    ),
97
    'cas_service_logout_request' => array(
98
      'variables' => array('ticket' => NULL, 'date' => NULL, 'id' => NULL),
99
      'file' => 'cas_server.response.inc',
100
    ),
101
    'cas_service_validate_whitelist_failure' => array(
102
      'variables' => array('service' => NULL, 'error_code' => NULL),
103
      'file' => 'cas_server.response.inc',
104
    ),
105
  );
106
}
107

    
108
/**
109
 * Implements hook_cas_server_user_attributes().
110
 *
111
 * Returns the user's roles.
112
 */
113
function cas_server_cas_server_user_attributes($account, $service) {
114
  return array(
115
    'uid'           => $account->uid,
116
    'mail'          => $account->mail,
117
    'created'       => $account->created,
118
    'timezone'      => $account->timezone,
119
    'language'      => $account->language,
120
    'drupal_roles'  => $account->roles,
121
  );
122
}
123

    
124
function cas_server_service_return() {
125
  global $user;
126
  $service = isset($_COOKIE[CAS_LOGIN_COOKIE]) ? $_COOKIE[CAS_LOGIN_COOKIE] : '';
127
  if ($service && $user->uid) {
128
    $ticket = _cas_server_save_ticket($user->uid, $service);
129
    setcookie(CAS_LOGIN_COOKIE, "", -3600);
130
    drupal_goto($service, array('query' => array('ticket' => $ticket)));
131
  }
132
}
133

    
134
/**
135
 * Handle login
136
 *
137
 */
138
function cas_server_login() {
139
  // Set login cookie so that we know we're in the process of logging in
140
  global $user;
141
  $output='';
142
  $whitelist_error_msg = variable_get('cas_server_whitelist_failure', t('You do not have permission to login to CAS from this service.'));
143
  $service = isset($_REQUEST['service']) ? $_REQUEST['service'] : '';
144
  $gateway = isset($_REQUEST['gateway']);
145
  if ($user->uid) {
146
    if ($service) {
147
      // Check service against whitelist
148
      if (!_cas_server_check_service_whitelist($service)) {
149
        return $whitelist_error_msg;
150
      }
151
      else {
152
        $_COOKIE[CAS_LOGIN_COOKIE] = $service;
153
      }
154
    }
155
    $output=t('You have successfully logged into CAS');
156
    cas_server_service_return();
157
  }
158
  else {
159
    if ($gateway && $service) {
160
      drupal_goto($service);
161
    }
162
    else {
163
      // Redirect to user login
164
      if ($service) {
165
        // Check service against whitelist
166
        if (!_cas_server_check_service_whitelist($service)) {
167
          return $whitelist_error_msg;
168
        }
169
        else {
170
          setcookie(CAS_LOGIN_COOKIE, $service);
171
        }
172
      }
173
      $output .= l(t('Login'), 'user', array('query' => array('destination' => 'cas/login')));
174
      drupal_goto('user/login', array('query' => array('destination' => 'cas/login')));
175
    }
176
  }
177
  return $output;
178
}
179

    
180
/**
181
 * Validate the ticket using a CAS 1.x methodology
182
 * This provides the simple non-xml based
183
 */
184
function cas_server_validate() {
185
  // Prevent this page from being cached.
186
  drupal_page_is_cacheable(FALSE);
187

    
188
  // Set content type.
189
  drupal_add_http_header('Content-Type', 'text/plain; charset=utf-8');
190

    
191
  //Obtain the ticket from the url and validate it.
192
  $ticket = isset($_REQUEST['ticket']) ? $_REQUEST['ticket'] : '';
193
  $service = isset($_REQUEST['service']) ? $_REQUEST['service'] : '';
194

    
195
  // Check service against whitelist
196
  if (!_cas_server_check_service_whitelist($service)) {
197
    print "no\n";
198
    print "\n";
199
    return;
200
  }
201

    
202
  $user_name = _cas_server_validate($service, $ticket);
203
  if ($user_name) {
204
    print "yes\n";
205
    print "$user_name\n";
206
  }
207
  else {
208
    print "no\n";
209
    print "\n";
210
  }
211
}
212
/**
213
 * serviceValidate method using cas 2.0
214
 * Returns data in xml
215
 */
216
function cas_server_service_validate() {
217
  // Prevent this page from being cached.
218
  drupal_page_is_cacheable(FALSE);
219

    
220
  // Set content type.
221
  drupal_add_http_header('Content-Type', 'text/xml; charset=utf-8');
222

    
223
  $ticket = isset($_REQUEST['ticket']) ? $_REQUEST['ticket'] : '';
224
  $service = isset($_REQUEST['service']) ? $_REQUEST['service'] : '';
225

    
226
  // Check service against whitelist
227
  if (!_cas_server_check_service_whitelist($service)) {
228
    $cas_error='INVALID_REQUEST';
229
    print theme('cas_server_validate_whitelist_failure', array('service' => $service, 'error_code' => $cas_error));
230
    watchdog('cas', 'Service %service validation failed!', array('%service' => $service));
231
    return;
232
  }
233

    
234
  $user_name = _cas_server_validate($service, $ticket);
235
  if (!$user_name) $cas_error='INVALID_TICKET';
236
  if (!$ticket || !$service) $cas_error='INVALID_REQUEST';
237

    
238
  if ($user_name) {
239
    //@TODO Generate proxy granting ticket
240
    $account = user_load_by_name($user_name);
241

    
242
    // Generate a list of attributes to return.
243
    $attributes = module_invoke_all('cas_server_user_attributes', $account, $service, $ticket);
244

    
245
    // Let other modules alter the list of attributes.
246
    $context = array(
247
      'service' => $service,
248
      'ticket' => $ticket,
249
    );
250
    drupal_alter('cas_server_user_attributes', $attributes, $account, $context);
251

    
252
    print theme('cas_service_validate_success', array('name' => $user_name, 'attributes' => $attributes));
253
    watchdog('cas', 'User %name CAS successfully authenticated.', array('%name' => $user_name));
254
  }
255
  else {
256
    print theme('cas_service_validate_failure', array('ticket' => $ticket, 'error_code' => $cas_error));
257
    watchdog('cas', 'Ticket %ticket for service %service not recognized.', array('%ticket' => $ticket, '%service' => $service));
258
  }
259
}
260

    
261
function _cas_server_check_service_whitelist($service) {
262
  $mapping_raw = variable_get('cas_server_service_whitelist', '');
263
  if (trim($mapping_raw) != '') {
264
    if (!drupal_match_path($service, $mapping_raw)) {
265
      return FALSE;
266
    }
267
  }
268
  return TRUE;
269
}
270

    
271
/**
272
 * Test to see if a one time use ticket is valid
273
 *
274
 * @param unknown_type $ticket
275
 * @return unknown
276
 */
277
function _cas_server_validate($service, $ticket) {
278
  // Look up the ticket
279
  $user_name='';
280
  $ticket_info=array(':service' => $service, ':ticket' => $ticket);
281
  $result = db_query_range("SELECT u.name FROM {cas_server_tickets} t JOIN {users} u ON t.uid=u.uid  WHERE t.service = :service and t.ticket = :ticket AND valid=1", 0, 1, $ticket_info);
282
  if ($result !== FALSE) {
283
    foreach ($result as $ticket_data) {
284
      $user_name = $ticket_data->name;
285
    }
286
  }
287
  db_update('cas_server_tickets')
288
    ->fields(array('valid' => 0))
289
    ->condition('ticket', $ticket)
290
    ->execute();
291

    
292
  return $user_name;
293
}
294

    
295
/**
296
 * Generate a one time use login ticket for the user in question.
297
 *
298
 * @param int $uid
299
 */
300
function _cas_server_save_ticket($uid, $service) {
301
  // Generate the ticket
302
  $time = REQUEST_TIME;
303
  $ticket = 'ST-' . user_password();
304
  $ticket_data = array('uid' => $uid, 'service' => $service, 'ticket' => $ticket, 'timestamp' => $time, 'valid' => 1);
305
  // Save the ticket to the db
306
  if ($uid && $service) {
307
    db_insert('cas_server_tickets')->fields($ticket_data)->execute();
308
  }
309
  return $ticket;
310
}
311

    
312
/**
313
 * Menu callback; triggers a CAS logout.
314
 */
315
function cas_server_logout() {
316
  // Check service against whitelist
317
  if (!_cas_server_check_service_whitelist($_GET['service'])) {
318
    return variable_get('cas_server_whitelist_failure', t('You do not have permission to login to CAS from this service.'));
319
  }
320

    
321
  global $user;
322

    
323
  // Due to the order of sessions being destroyed on a client site vs CAS server,
324
  // there is a workflow that would allow the user's session to not exist at this point.
325
  // Skip triggering user logout related processes if there is not a valid user in session.
326
  if(user_is_logged_in()) {
327
    // Log the successful logout process.
328
    watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
329
    // Tell modules about the logout.
330
    module_invoke_all('user_logout', $user);
331
  }
332

    
333
  // Destroy the current session, and reset $user to the anonymous user.
334
  session_destroy();
335

    
336
  $output = '<p>' . t('You have been logged out successfully.') . '</p>';
337
  if (isset($_REQUEST['url'])) {
338
    $output .= '<p>' . l(t('Continue'), $_REQUEST['url']) . '</p>';
339
  }
340
  return $output;
341
}
342

    
343
/**
344
 * Send CAS a logout requests for each of the user's CAS tickets.
345
 *
346
 * @param $account
347
 *   The user for whom to send CAS logout requests.
348
 */
349
function cas_server_logout_clients($account) {
350
   $result = db_query("SELECT service, ticket, valid FROM {cas_server_tickets} WHERE uid= :uid", array(':uid' => $account->uid));
351
    if ($result !== FALSE) {
352
      $expired_tickets = array();
353
      foreach ($result as $client) {
354
        $expired_tickets[] = $client->ticket;
355
        if (!$client->valid)  {
356
          $id = 'LR-' . user_password();
357
          $date = date('c');
358
          $logout_request = theme('cas_service_logout_request', array('id' => $id, 'date' => $date, 'ticket' => $client->ticket));
359
          // Send POST request
360
          $response = drupal_http_request(
361
            $client->service,
362
            array(
363
              'headers' => array('Content-Type' => 'application/x-www-form-urlencoded'),
364
              'method' =>  'POST',
365
              'data' => 'logoutRequest=' . urlencode($logout_request),
366
            )
367
          );
368
          if (@$response->error) {
369
            watchdog('error', 'Error in CAS logout Request - %code : %message', array('%code' => $response->code, '%error' => $response->error));
370
          }
371
        }
372
        // Remove ticket
373
      }
374
      if ($expired_tickets) {
375
        db_delete('cas_server_tickets')
376
          ->condition('ticket', $expired_tickets, 'IN')
377
          ->execute();
378
      }
379
    }
380
}
381

    
382
/**
383
 * Implements hook_user_logout().
384
 */
385
function cas_server_user_logout($account) {
386
  cas_server_logout_clients($account);
387
}