forked from SU-SWS/stanford_ssp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stanford_ssp.module
668 lines (567 loc) · 18.9 KB
/
stanford_ssp.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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
236
237
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
<?php
/**
* @file
* Code for the Stanford SimpleSAML PHP module.
*/
/**
* Implements hook_menu().
*/
function stanford_ssp_menu() {
$items = array();
// Authpoint
// ---------
$items['sso/login'] = array(
'title' => "Stanford SSO Authentication Endpoint",
'description' => "Authentication endpoint.",
'page callback' => 'stanford_ssp_sso_auth',
'access callback' => "stanford_ssp_access_login_page",
);
$items['sso/denied-test'] = array(
'title' => "Always denied",
'description' => "Authentication endpoint test.",
'page callback' => 'stanford_ssp_sso_auth',
'access callback' => FALSE,
);
// Configuration Forms
// -------------------
$items['admin/config/stanford/stanford_ssp'] = array(
'title' => "Stanford SSO",
'description' => "Stanford Single Sign On configuration settings.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_configuration_form"),
'access arguments' => array('administer stanford_ssp'),
'file' => 'stanford_ssp.admin.inc',
);
$items['admin/config/stanford/stanford_ssp/settings'] = array(
'title' => "Stanford SSO",
'description' => "Stanford Single Sign On configuration settings.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_configuration_form"),
'access arguments' => array('administer stanford_ssp'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'stanford_ssp.admin.inc',
);
$items['admin/config/stanford/stanford_ssp/authorizations'] = array(
'title' => "Authorizations",
'description' => "Configure authorization dynamics.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_authorizations_form"),
'access arguments' => array('administer stanford_ssp'),
'type' => MENU_LOCAL_TASK,
'file' => 'stanford_ssp.admin.inc',
);
$items['admin/config/stanford/stanford_ssp/role-mappings'] = array(
'title' => "Role Mappings",
'description' => "Configure role assignments for authenticated users.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_role_mappings_form"),
'access arguments' => array('administer stanford_ssp'),
'type' => MENU_LOCAL_TASK,
'file' => 'stanford_ssp.admin.inc',
);
$items['admin/config/stanford/stanford_ssp/login-block-forms'] = array(
'title' => "Login Block & Forms",
'description' => "Configure role assignments for authenticated users.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_login_block_forms_form"),
'access arguments' => array('administer stanford_ssp'),
'type' => MENU_LOCAL_TASK,
'file' => 'stanford_ssp.admin.inc',
);
$items['admin/config/stanford/stanford_ssp/add-sso-user'] = array(
'title' => "Add SSO User",
'description' => "Create a new user through the SSO.",
'page callback' => 'drupal_get_form',
'page arguments' => array("stanford_ssp_add_sso_user"),
'access arguments' => array('administer stanford_ssp'),
'type' => MENU_LOCAL_TASK,
'file' => 'stanford_ssp.admin.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function stanford_ssp_permission() {
return array(
'administer stanford_ssp' => array(
'title' => t('Administer Stanford SSP'),
'description' => t('Administrate the configuration for the Stanford SSP module.'),
),
);
}
/**
* Custom access callback to validate user's access to login page sso/login
*/
function stanford_ssp_access_login_page() {
// If user is anonymous then go through the log-in process.
if (user_is_anonymous()) {
return TRUE;
}
// If the user is already logged in and a goto is set then do the redirect.
if (isset($_GET['goto'])) {
$goto = check_plain($_GET['goto']);
if (drupal_valid_path($goto)) {
drupal_goto($goto);
}
}
}
/**
* Implements hook_init().
*
* Tried to use hook boot but too few api functions were available.
*/
function stanford_ssp_init() {
// If the user is anonymous then end the journey.
// We don't worry about those suckers.
$anon = user_is_anonymous();
if ($anon) {
return;
}
// Check to force https for authenticated users.
stanford_ssp_force_https();
// Check for cache setting:
if (variable_get("stanford_ssp_prevent_cache", FALSE)) {
header("Cache-Control: no-store, no-cache, must-revalidate");
}
}
/**
* Implements hook_user_login().
*/
function stanford_ssp_user_login() {
// Redirect the user if setting available.
$redir = variable_get("stanford_ssp_redirect_on_login", FALSE);
// If the force redirect option has not been set check for the `goto` url
// parameter.
if (!$redir && isset($_GET['goto'])) {
$goto = check_plain($_GET['goto']);
if (drupal_valid_path($goto)) {
$redir = $goto;
}
}
if (!empty($redir)) {
drupal_goto($redir);
}
}
/**
* Authentication Endpoint.
*
* Use this endpoint instead of the default `saml_login` endpoint included in
* the simplesaml module as it is easier to make adjustments prior to firing
* off that sequence through this function. For example: Check if the force
* https is on.
*
* @return array
* Render array.
*/
function stanford_ssp_sso_auth() {
$output = array();
// Check to force https for authenticated users.
stanford_ssp_force_https();
// A way in to simplesaml.
module_load_include("inc", "stanford_simplesamlphp_auth", "stanford_simplesamlphp_auth.pages");
stanford_simplesamlphp_auth_loginpage();
return $output;
}
/**
* Enforces the use https configuration setting.
*
* Checks to ensure user is on https and will force redirect if option is on.
* Also enforces secure cookies on https.
*
*/
function stanford_ssp_force_https() {
global $is_https;
// If is https force secure cookies. Mmmmm cookies.
if ($is_https) {
// Mandate secure cookies for sessions.
ini_set('session.cookie_secure', 1);
// Session cookies should be deleted when the browser session ends.
ini_set('session.cookie_lifetime', 0);
return;
}
// Check if setting is on. If not then end.
$force = variable_get("stanford_ssp_force_https", FALSE);
if (!$force) {
return;
}
// Force a redirect to https.
$redirect_url = "https://" . check_plain($_SERVER['HTTP_HOST']) . check_plain($_SERVER['REQUEST_URI']);
header("Location: " . $redirect_url);
drupal_exit();
}
/**
* Handles 403 page errors and provided the automagic redirection to SAML login.
*
* @param array &$page
* The page render array.
*/
function stanford_ssp_page_build(&$page) {
// We don't want to mess with the 403 page setting so we should check the
// header response on the page build.
$headers = drupal_get_http_header();
if (user_is_anonymous() && isset($headers["status"]) && $headers["status"] == "403 Forbidden") {
if (variable_get("stanford_ssp_automagic_login", FALSE)) {
$dest = drupal_get_destination();
// If we don't unset the destination we get a redirect loop.
unset($_GET['destination']);
// Need to go to here to log in.
drupal_goto("sso/login", array('query' => array('goto' => $dest['destination'])));
}
}
}
/**
* Provide a different lookup mechanism other than by user name.
* Needs a patch from https://www.drupal.org/node/2635152 in order for this
* hook to exist. Without this hook, only username works for lookup.
*
* @param object $ext_user
* A loaded user object.
* @param array $_simplesamlphp_auth_saml_attributes
* User's Attributes from the simplesaml response.
* @param string $auth_field
* A field name.
*
*/
function stanford_ssp_saml_ext_user_alter(&$ext_user, $attributes, $auth_field) {
// If a user has already been found we don't need to provide any extra work.
if ($ext_user) {
return;
}
// If still no user try sunet id.
if (!$ext_user) {
$sunet = $attributes["uid"];
if (is_array($sunet)) {
$sunet = array_pop($sunet);
}
$ext_user = stanford_ssp_user_load_by_sunetid($sunet);
}
}
/**
* Loads a user by sunet id.
*
* @return mixed
* A loaded user object or false if could not be loaded.
*/
function stanford_ssp_user_load_by_sunetid($sunetid) {
$uid = db_select("stanford_ssp_sunetid", "sss")
->fields("sss", array("uid"))
->condition("sunet", check_plain($sunetid))
->execute()
->fetchField();
if (is_numeric($uid)) {
return user_load($uid);
}
return FALSE;
}
/**
* Returns the sunet id of a user by their Drupal uid.
*
* @param int $uid
* Drupal user account id
*
* @return mixed
* string if valid account found.
* False if none.
*/
function stanford_ssp_get_sunetid_by_uid($uid) {
$uid = (int) $uid;
$r = db_select("stanford_ssp_sunetid", "sss")
->fields("sss", array("sunet"))
->condition("uid", $uid)
->execute()
->fetchObject();
return (isset($r->sunet)) ? $r->sunet : FALSE;
}
/**
* Implements hook_user_insert().
*/
function stanford_ssp_user_insert(&$edit, $account, $category) {
// Get the users attributes.
try {
$attributes = stanford_simplesamlphp_auth_get_saml_attributes();
} catch(Exception $e) {
watchdog('stanford_ssp_user_insert', 'Could not load SAML information', array(), WATCHDOG_DEBUG);
}
// If the new account and the current user don't have matching emails then
// the account must have been created by a form instead of through login.
if (isset($attributes['email'][0]) && ($attributes['email'][0] !== $account->init)) {
return;
}
// Act on the current user.
if (!empty($attributes)) {
$record = array(
'sunet' => array_pop($attributes['uid']),
'uid' => $account->uid,
);
drupal_write_record("stanford_ssp_sunetid", $record);
}
}
/**
* Implements hook_user_delete().
*/
function stanford_ssp_user_delete($account) {
// Remove tracking from the schema.
db_delete('stanford_ssp_sunetid')
->condition('uid', $account->uid)
->execute();
}
/**
* Implements hook_stanford_simplesamlphp_auth_allow_login.
*
* @param array $attributes
* SAML Attributes.
*
* @return bool
* True if ok. False is not allowed to log in.
*/
function stanford_ssp_stanford_simplesamlphp_auth_allow_login($attributes) {
// Is the user allowed to log in?
$isok = TRUE;
// If the checks are off then just let it pass.
if (variable_get("stanford_ssp_auth_restrictions", "allow") !== "restrict") {
return $isok;
}
$sunets = variable_get("stanford_ssp_auth_restriction_sunet", FALSE);
$groups = variable_get("stanford_ssp_auth_restriction_group", FALSE);
// First check list of sunets.
if (!empty($sunets)) {
$sunets = explode(",", $sunets);
$sunets = array_map("trim", $sunets);
$isok = stanford_ssp_simplesamlphp_auth_allow_login_validate_sunets($sunets, $attributes);
}
// If the sunets are ok we should check the groups.
if (!empty($groups) && $isok) {
$groups = explode(",", $groups);
$groups = array_map("trim", $groups);
$isok = stanford_ssp_simplesamlphp_auth_allow_login_validate_groups($groups, $attributes);
}
// Return true or false.
return $isok;
}
/**
* Validates a list of sunets against the user that is trying to log in.
* @param [type] $sunets [description]
* @param [type] $attributes [description]
* @return [type] [description]
*/
function stanford_ssp_simplesamlphp_auth_allow_login_validate_sunets($sunets, $attributes) {
$sunetid = array_pop($attributes['uid']);
return in_array($sunetid, $sunets);
}
/**
* [stanford_ssp_simplesamlphp_auth_allow_login_validate_groups description]
* @param [type] $groups [description]
* @param [type] $attributes [description]
* @return [type] [description]
*/
function stanford_ssp_simplesamlphp_auth_allow_login_validate_groups($groups, $attributes) {
$samlgroups = $attributes['eduPersonEntitlement'];
return (count(array_intersect($groups, $samlgroups)) > 0);
}
/**
* Implements simplesamlphp_auth_user_roles_alter()
* @param [type] $roles [description]
* @return [type] [description]
*/
function stanford_ssp_stanford_simplesamlphp_auth_user_roles_alter($user) {
$saml = stanford_simplesamlphp_auth_get_saml_info();
$source = $saml['source'];
// No source object. Must be a local user.
if (!is_object($source)) {
return;
}
// No authenticated user to work on. Go to bed.
if (!$source->isAuthenticated()) {
return;
}
// Action to perform when evaluating roles.
$operation = variable_get("stanford_ssp_auth_role_map", "none");
// If the action to do is none then we can end this function.
if ($operation == "none") {
return;
}
// What roles are matchy pants.
$roles = stanford_simplesamlphp_auth_rolepopulation(variable_get('stanford_simplesamlphp_auth_rolepopulation', ''));
$user_roles = user_roles();
$userinfo = FALSE;
$values = array_intersect_key($user_roles, $roles);
// Do the op.
switch ($operation) {
// Much like simplesamlphp_auths re-assign roles option this will replace
// all of the users roles with the ones that were matched in the mapping.
case 'reassign':
$userinfo = array('roles' => $values);
break;
// Unline implesamlphp_auths re-assign roles option this will only add/grant
// additional roles to the user.
case 'grant':
$new_roles= $user->roles + $values;
$userinfo = array('roles' => $new_roles);
break;
}
// Always add the SSO User role if it exists.
$sso_user_role = user_role_load_by_name("SSO User");
if ($sso_user_role) {
$userinfo['roles'][$sso_user_role->rid] = $sso_user_role->name;
}
// Save the new roles.
$user = user_save($user, $userinfo);
// We want to map eduPersonAffiliation to roles.
stanford_ssp_auth_role_map_person_affiliation($user);
}
/**
* Maps the affiliations to the user roles.
* @param object $user
* The user to check the attributes on. Probably should be the currently
* logged in user as the attributes come from that user anyhow.
*/
function stanford_ssp_auth_role_map_person_affiliation(&$user) {
$roles = user_roles();
$attributes = stanford_simplesamlphp_auth_get_attributes();
$affiliations = $attributes['eduPersonAffiliation'];
// Role mapping array goes as such:
// regex => role name.
$maps = array(
'/faculty/i' => 'Stanford Faculty',
'/staff/i' => 'Stanford Staff',
'/postdoc/i' => 'Stanford Student',
'/student/i' => 'Stanford Student',
// '/member/' => 'SSO User',
// '/affiliate/' => 'SSO User',
);
// Loop through the expressions looking for matches in the maps array.
$matches = array();
foreach ($maps as $expression => $role_name) {
$has_matches = preg_grep($expression, $affiliations);
// If any of the affiliations match the pattern add it to the matches array
// keying by id so that we don't get duplicates.
if (count($has_matches)) {
// Check to see if the role exist. It may have been deleted.
$role = user_role_load_by_name($role_name);
if ($role) {
$matches[$role->rid] = $role->name;
}
}
}
// If anything was matched save the user.
if (count($matches)) {
$final_roles = $user->roles + $matches;
$user = user_save($user, array('roles' => $final_roles));
}
}
/**
* *****************************************
* FORM ALTERS
* *****************************************
*/
/**
* User account form.
* @param [type] &$form [description]
* @param [type] &$form_state [description]
* @return [type] [description]
*/
function stanford_ssp_form_user_profile_form_alter(&$form, &$form_state) {
// Check for editing a user. If we don't have a user we can end.
$account = isset($form["#user"]) ? $form["#user"] : FALSE;
if (!$account) {
return;
}
// Check to see if user is a SAML user.
$sunet = stanford_ssp_get_sunetid_by_uid($account->uid);
if (!$sunet) {
return;
}
// If a saml user check to see if they can use the password fields.
if (!variable_get("stanford_simplesamlphp_auth_allowsetdrupalpwd", FALSE)) {
$form["account"]['pass']['#access'] = FALSE;
}
}
/**
* Implements hook_form_FORMID_alter().
*
* @param [type] &$form [description]
* @param [type] &$form_state [description]
* @return [type] [description]
*/
function stanford_ssp_form_user_login_alter(&$form, &$form_state) {
// Check for https forcing:
stanford_ssp_force_https();
// Remove the Federated login link because it is ugly.
if (isset($form["links"])) {
unset($form["links"]);
}
$show_sso = variable_get("stanford_ssp_show_sso_login", TRUE);
$show_local = variable_get("stanford_ssp_show_local_login", TRUE);
$sso_on = variable_get("stanford_simplesamlphp_auth_activate", FALSE);
$local_on = variable_get("stanford_simplesamlphp_auth_allowdefaultlogin", TRUE);
// Display (or not) the login link for SSO (SAML).
if ($show_sso && $sso_on) {
$form['saml_auth'] = array(
'#type' => 'fieldset',
'#title' => "SUNet Login",
'#weight' => -10,
);
$link_text = variable_get("stanford_ssp_sso_link_text", t("Log in with your SUNet ID »"));
$form['saml_auth']['saml_link'] = array(
'#prefix' => "<p>",
'#markup' => l($link_text, "sso/login"),
'#suffix' => "</p>",
);
}
// If local login form is enabled add a fieldset to title it.
if ($show_local && $local_on) {
// Add the fieldset for the Drupal login form.
$form['drupal_auth'] = array(
'#type' => 'fieldset',
'#title' => "Drupal Login",
'#weight' => 0,
);
}
// If not showing local we need to unset the login form items.
if (!$show_local || !$local_on) {
unset($form['name']);
unset($form['pass']);
unset($form['actions']);
}
// If nothing available print out a message.
if (!$show_local && !$show_sso) {
$form["nothingtoseehere"]["#markup"] = "<h3>No login options available.</h3>";
}
}
/**
* Implements hook form alter.
* @param [type] $form [description]
* @param [type] $form_state [description]
* @return [type] [description]
*/
function stanford_ssp_form_user_pass_alter(&$form, &$form_state) {
$form["#validate"][] = "stanford_ssp_form_user_pass_alter_validate";
}
/**
* Prevent password reset from working if local accounts turned off.
* @param [type] $form [description]
* @param [type] $form_state [description]
* @return [type] [description]
*/
function stanford_ssp_form_user_pass_alter_validate($form, $form_state) {
if (!variable_get("stanford_simplesamlphp_auth_allowdefaultlogin", TRUE)) {
form_set_error("", t("We're sorry but local account login has been disabled. Password reset is not available at this time."));
}
}
/**
* Formats workgroup entitlment.
*
* Eg: anchorage:humanbiology-admins to anchorage_humanbiology-admins.
*
* @param string $entitlement
* Eg: anchorage:humanbiology-admins.
*
* @return string
* eg: anchorage_humanbiology-admins
*/
function stanford_ssp_format_entitlement($entitlement) {
$entitlement = strtolower($entitlement);
$entitlement = str_replace(":", "_", $entitlement);
return $entitlement;
}