-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsimplesamlphp_auth.module
More file actions
983 lines (840 loc) · 33.6 KB
/
Copy pathsimplesamlphp_auth.module
File metadata and controls
983 lines (840 loc) · 33.6 KB
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
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
<?php
/**
* @file
* simpleSAMLphp authentication module for Backdrop.
*
* This authentication module is based on the shibboleth authentication module,
* with changes to adopt to use simpleSAMLphp.
*
* ISSUES and TODOs:
* ISSUE: User is always dropped on user page after login, instead of where
* they were when they clicked "Federated Login". Because of this, deep
* linking to access controlled content does not work. Usability would
* be considerably increased if this were resolved.
* FYI: Backdrop now requires knowledge of the local user password in order to
* change e-mail address, etc. This could be an issue for users of
* accounts that are autoprovisioned by this module, though Backdrop does
* give users the ability to reset their password to something they know
* @todo Rework the default login limitation logic to use a backdrop permission
* rather than a list of UIDs.
* @todo When denying access because the administrator has chosen not to allow
* the module to register/create accounts, the user is told to contact
* the administrator; the message should provide the contact information.
* ISSUE: Until Backdrop issue #754560 is resolved users will not see logout
* notices.
*/
/**
* Implements hook_menu().
*/
function simplesamlphp_auth_menu() {
$items = array();
$items['admin/config/people/simplesamlphp_auth'] = array(
'title' => 'SimpleSAMLphp Auth Settings',
'description' => 'Control the various settings of the simpleSAMLphp authentication module',
'page callback' => 'backdrop_get_form',
'page arguments' => array('simplesamlphp_auth_settings'),
'access arguments' => array('administer simpleSAMLphp authentication'),
'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM,
'file' => 'simplesamlphp_auth.admin.inc',
);
$link_text = filter_xss_admin(config_get_translated('simplesamlphp_auth.settings', 'login_link_display_name'));
$items['saml_login'] = array(
'title' => $link_text,
'description' => 'Provides a site login page',
'page callback' => 'simplesamlphp_auth_loginpage',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
if (_simplesamlphp_auth_isEnabled()) {
// Add a new tab to the user login/register/password pages.
$items['user/saml_login'] = array(
'title' => $link_text,
'page callback' => 'backdrop_goto',
'page arguments' => array('saml_login'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
);
}
return $items;
}
/**
* Implements hook_permission().
*/
function simplesamlphp_auth_permission() {
return array(
'administer simpleSAMLphp authentication' => array(
'title' => t('Administer simpleSAMLphp authentication'),
'description' => t('Warning: Give to trusted roles only; this permission has security implications.'),
),
);
}
/**
* The /saml_login which triggers user authentication to SimpleSAMLphp SP.
*/
function simplesamlphp_auth_loginpage() {
global $user;
global $base_url;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$fail = NULL;
$output = NULL;
// Make sure this page is not getting cached.
backdrop_page_is_cacheable(FALSE);
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without initializing.
backdrop_set_message(t("We're sorry this feature is not yet enabled."));
return '';
}
// Do some sanity checking before attempting anything.
$config = \SimpleSAML\Configuration::getInstance();
$config_store_type = $config->getValue('store.type');
// Make sure phpsession is NOT being used.
if ($config_store_type == 'phpsession') {
watchdog('simplesamlphp_auth', 'A user attempted to login using simplesamlphp but the store.type is phpsession, use memcache or sql for simplesamlphp session storage. See: simplesamlphp/config/config.php.', NULL, WATCHDOG_WARNING);
$fail = TRUE;
}
// Make sure there is an instance of \SimpleSAML\Auth\Simple.
if (!$_simplesamlphp_auth_as) {
watchdog('simplesamlphp_auth', 'A user attempted to login using this module but there was a problem.', NULL, WATCHDOG_WARNING);
$fail = TRUE;
}
// There was a problem, we can't go on, but we don't want to tell the user
// any specifics either.
if ($fail) {
backdrop_set_message(t("We're sorry. There was a problem. The issue has been logged for the administrator."));
backdrop_goto(base_path());
}
$returnto = NULL;
// Support for deep linking.
// See if a URL has been explicitly provided in ReturnTo.
if ((isset($_REQUEST['ReturnTo']) && $_REQUEST['ReturnTo']) &&
(valid_url($_REQUEST['ReturnTo']) && stristr($_REQUEST['ReturnTo'], $base_url))
) {
$returnto = $_REQUEST['ReturnTo'];
// Check if REFERER URL is available and use it if it points to the site.
}
elseif ((isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']) &&
(valid_url($_SERVER['HTTP_REFERER']) && stristr($_SERVER['HTTP_REFERER'], $base_url))
) {
$returnto = $_SERVER['HTTP_REFERER'];
}
// If the user is anonymous, set the cookie and require authentication.
if ($user->uid == 0) {
if ($returnto) {
// Set the cookie so we can deliver the user to the place they started.
setrawcookie('simplesamlphp_auth_returnto', $returnto, time() + 60 * 60);
}
// Require the user to be authenticated.
$_simplesamlphp_auth_as->requireAuth();
// If the user is authenticated, send them along.
}
else {
$gotourl = NULL;
// Check to see if we've set a cookie. If there is one, give it priority.
if (isset($_COOKIE['simplesamlphp_auth_returnto']) && $_COOKIE['simplesamlphp_auth_returnto']) {
// Use the cookie for the ReturnTo.
$gotourl = $_COOKIE['simplesamlphp_auth_returnto'];
// Unset the cookie.
setrawcookie('simplesamlphp_auth_returnto', '');
}
elseif ($returnto) {
$gotourl = $returnto;
}
// If a ReturnTo has been set.
if ($gotourl) {
$parsed_gotourl = backdrop_parse_url($gotourl);
backdrop_goto($parsed_gotourl['path'], $parsed_gotourl);
}
else {
backdrop_goto('user/' . $user->uid);
}
}
return $output;
}
/**
* Implements hook_config_info().
*/
function simplesamlphp_auth_config_info() {
$prefixes['simplesamlphp_auth.settings'] = array(
'label' => t('simpleSAMLphp authentication settings'),
'group' => t('Configuration'),
);
return $prefixes;
}
/**
* Implements hook_init().
*/
function simplesamlphp_auth_init() {
$config = config('simplesamlphp_auth.settings');
global $user;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
global $_simplesamlphp_auth_saml_config;
global $_simplesamlphp_auth_saml_version;
if (backdrop_is_cli() || !_simplesamlphp_auth_isEnabled(TRUE)) {
// Exit without initializing.
return;
}
if (!class_exists('\SimpleSAML\Configuration')) {
// Load the SimpleSAMLphp library if it isn't already loaded.
$basedir = $config->get('installdir');
if (file_exists($basedir . '/lib/_autoload.php')) {
require_once $basedir . '/lib/_autoload.php';
}
if (!class_exists('\SimpleSAML\Configuration')) {
return;
}
}
// Get the SimpleSAMLphp session.
$_simplesamlphp_auth_saml_config = \SimpleSAML\Configuration::getInstance();
$_simplesamlphp_auth_saml_version = $_simplesamlphp_auth_saml_config->getVersion();
// Load simpleSAMLphp, configuration and metadata.
$_simplesamlphp_auth_as = new \SimpleSAML\Auth\Simple($config->get('authsource'));
$_simplesamlphp_auth_saml_attributes = $_simplesamlphp_auth_as->getAttributes();
if ($user->uid == 0) {
// User is not logged in to Backdrop.
if ($_simplesamlphp_auth_as->isAuthenticated()) {
// User is logged in - SimpleSAMLphp (but not Backdrop).
// Get unique identifier from saml attributes.
$authname = _simplesamlphp_auth_get_authname();
_simplesaml_auth_debug(t('Authname is [%authname] userid is [%uid]', array(
'%authname' => $authname,
'%uid' => $user->uid,
)));
if (!empty($authname)) {
// User is logged in with SAML authentication and we got the unique
// identifier and try to log into Backdrop.
_simplesaml_auth_debug(t('Load user [%authname]', array('%authname' => $authname)));
// Retrieve user mapping and attempt to log the user in.
$ext_user = authmap_external_load($authname);
$skip_user_finalization = FALSE;
// First we check the admin settings for SimpleSAMLphp and find out
// if we are allowed to register users.
$register_new_users = $config->get('registerusers');
$username_collision_method = $config->get('username_collision');
if (!$ext_user && $register_new_users) {
$username = NULL;
$mail_address = NULL;
try {
$username = _simplesamlphp_auth_get_default_name(0);
// Get mail from default attribute.
$mail_address = _simplesamlphp_auth_get_mail();
}
catch (Exception $e) {
// Not configured appropriately.
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
}
// Allow third party modules to handle which username/email to use for
// the registration check. e.g. if they're moving email domains.
backdrop_alter('simplesamlphp_auth_name_mail', $username, $mail_address);
$user_local = $username ? user_load_by_name($username) : NULL;
$user_email_local = $mail_address ? user_load_by_mail($mail_address) : NULL;
if ($user_local || $user_email_local) {
// Local username or mail account exists. Attempt to merge it or bail early.
if ($user_local && $user_email_local) {
if ($user_local->uid != $user_email_local->uid) {
// We've found multiple accounts, one with their username and another with their email.
// We can't link this user. Abort.
backdrop_set_message(
t(
"Attempted to login using username @username and email @email, but found two local accounts with those values. Please contact the site administrator so they may deactivate one of the accounts.",
['@username' => $username, '@email' => $mail_address,]
),
'warning'
);
$_simplesamlphp_auth_as->logout(base_path());
return;
}
}
elseif ($user_email_local) {
$user_local = $user_email_local;
}
if ($username_collision_method === 'merge') {
// If we are going to silently merge the external and local accounts
// we need to adjust the authmap table before proceeding with the
// login.
_simplesaml_auth_debug(t('Username/email collision detected. Username @username is authenticating via simpleSAMLphp but already exists as a local user. Merging external and local accounts and proceeding with login.',
array('@username' => $username)));
authmap_set_authmaps($user_local, array('authname_simplesamlphp_auth' => $authname));
}
else {
// If we abort the login simply report and return.
$warning_message = t('Username collision detected. Username @username is authenticating via simpleSAMLphp but already exists as a local user. Aborting login.',
['@username' => $username]);
_simplesaml_auth_debug($warning_message);
backdrop_set_message($warning_message, 'warning');
$_simplesamlphp_auth_as->logout(base_path());
return;
}
// Force a login.
authmap_external_login_register($authname, 'simplesamlphp_auth');
$skip_user_finalization = TRUE;
if ($user->uid) {
// Specify the external user.
$ext_user = $user;
}
}
}
if (!$ext_user) {
// First we check the admin settings for simpleSAMLphp and find out
// if we are allowed to register users.
if ($config->get('registerusers')) {
// We are allowed to register new users.
_simplesaml_auth_debug(t('Register [%authname]', array('%authname' => $authname)));
authmap_external_login_register($authname, 'simplesamlphp_auth');
$skip_user_finalization = TRUE;
if (!empty($user->uid)) {
// Populate roles based on configuration setting.
$rolepopulation = $config->get('rolepopulation');
if (!empty($rolepopulation)) {
$user->roles = _rolepopulation($rolepopulation);
}
$user->save();
}
else {
// We were unable to register this new user on the site.
// We let the user know about this, log an error, and redirect to the home page.
$msg = t("We are sorry. While you have successfully authenticated, we were unable to create an account for you on this site. Please ask the site administrator to provision access for you.");
backdrop_set_message(check_plain($msg));
watchdog('simplesamlphp_auth', 'Unable to register %authname using simplesamlphp_auth', array('%authname' => $authname), WATCHDOG_ERROR);
$_simplesamlphp_auth_as->logout(base_path());
}
}
else {
// We are not allowed to register new users on the site through
// simpleSAML. We let the user know about this and redirect to the
// user/login page.
$msg = t("We are sorry. While you have successfully authenticated, you are not yet entitled to access this site. Please ask the site administrator to provision access for you.");
backdrop_set_message(check_plain($msg));
$_simplesamlphp_auth_as->logout(base_path());
}
}
else {
// If successfully logged into Backdrop.
// See if we're supposed to re-evaluate role assignments.
if ($config->get('roleevaleverytime')) {
// If the user is already registered...
// Update the roles.
// Populate roles based on configuration setting.
_simplesaml_auth_debug(t('User already registered [%authname] updating roles.', array('%authname' => $authname)));
$ext_user->roles = _rolepopulation($config->get('rolepopulation'));
// Save the updated roles and populate the user object.
$ext_user->save();
}
// Populate the user object.
$user = $ext_user;
if (module_exists('rules')) {
rules_invoke_all('simplesamlphp_auth_rules_event_login', $user);
}
}
// Finalizing the login, calls hook_user op login.
// simplesamlphp_auth_external_login_register() calls this for us.
if (!$skip_user_finalization) {
$edit = array();
user_login_finalize($edit);
}
}
}
}
else {
// The user is already logged into Backdrop.
// If we forbid users from logging in using local accounts.
if (FALSE == $config->get('allowdefaultlogin')) {
// If the user has NOT been authenticated via simpleSAML...
if (!$_simplesamlphp_auth_as->isAuthenticated()) {
// :FYI: Until Backdrop issue #754560 is corrected this message will
// never be seen by the user.
backdrop_set_message(t("We are sorry, users are not permitted to log in using local accounts."));
// Destroy the user's session (log them out).
_simplesamlphp_auth_destroy_backdrop_session();
}
}
else {
// If we are allowing users to log in with local accounts.
// If the user has NOT been authenticated via simpleSAML.
if (!$_simplesamlphp_auth_as->isAuthenticated()) {
// See if we limit this privilege to specified users.
$string_allow_def_log_users = $config->get('users');
$array_allow_def_log_users = array();
// See if we limit this privilege to specified roles.
$array_allow_def_log_roles = $config->get('roles');
// If user IDs or roles are specified, we let them in. Everyone else
// gets logged out.
if (backdrop_strlen($string_allow_def_log_users) || $array_allow_def_log_roles) {
// Convert the string into an array.
// @todo Perform a test to make sure that only numbers, spaces, or
// commas are in the string.
$array_allow_def_log_users = explode(',', $string_allow_def_log_users);
// If we still have something to work with.
if (0 < count($array_allow_def_log_users) || 0 < count($array_allow_def_log_roles)) {
// Log the user out of Backdrop if:
// 1) the current user's uid is NOT in the list of allowed uids
// 2) or their role does not match and allowed mixed mode role.
$match_roles = array_intersect($user->roles, $array_allow_def_log_roles);
if (!in_array($user->uid, $array_allow_def_log_users) && count($match_roles) == 0) {
// User is logged into Backdrop, but may not be logged into
// simpleSAML. If this is the case we're supposed to log the
// user out of Backdrop.
// :FYI: Until Backdrop issue #754560 is corrected this message
// will never be seen by the user.
backdrop_set_message(t('We are sorry, you are not permitted to log in using a local account.'));
// Write to the watchdog so someone will know what is happening.
watchdog('simplesamlphp_auth', 'User %name not authorized to log in using local account.', array('%name' => $user->name));
_simplesamlphp_auth_destroy_backdrop_session();
}
}
}
}
}
}
}
/**
* Implements hook_user_insert().
*/
function simplesamlphp_auth_user_insert($account) {
global $_simplesamlphp_auth_as;
global $user;
if (!_simplesamlphp_auth_isEnabled() || empty($_simplesamlphp_auth_as) || !$_simplesamlphp_auth_as->isAuthenticated()) {
// Exit without initializing.
return;
}
// There are cases where the logged in user is an admin and is creating
// accounts. In such cases we don't want to use their name and email;
// we want to keep the supplied values.
$mail_address = '';
if ($user->uid == 0 || $user->uid === $account->uid) {
// Get name from default attributes.
try {
_simplesaml_auth_debug(t('Registering user [%acctname]', array('%acctname' => $account->name)));
$account->name = _simplesamlphp_auth_get_default_name($account->uid);
db_update('users')
->fields(array('name' => $account->name))
->condition('uid', $account->uid)
->execute();
_simplesaml_auth_debug(t('Updating username [%acctname]', array('%acctname' => $account->name)));
}
catch (Exception $e) {
backdrop_set_message(t('Your user name was not provided by your identity provider (IDP).'), "error");
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
}
// Get mail from default attribute.
try {
$mail_address = _simplesamlphp_auth_get_mail();
}
catch (Exception $e) {
backdrop_set_message(t('Your e-mail address was not provided by your identity provider (IDP).'), "error");
watchdog('simplesamlphp_auth', $e->getMessage(), NULL, WATCHDOG_CRITICAL);
}
}
else {
if (!empty($account->mail)) {
$mail_address = $account->mail;
}
}
if (!empty($mail_address)) {
db_update('users')
->fields(array('mail' => $mail_address))
->condition('uid', $account->uid)
->execute();
}
if (module_exists('rules')) {
rules_invoke_event('simplesamlphp_auth_rules_event_register', $account);
}
_simplesaml_auth_debug(t('Updating mail [%mailaddr]', array('%mailaddr' => $mail_address)));
}
/**
* Implements hook_user_logout().
*/
function simplesamlphp_auth_user_logout($account) {
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
$config = config('simplesamlphp_auth.settings');
global $user;
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
global $base_url;
if (!empty($_simplesamlphp_auth_saml_attributes)) {
// The Drupal module called session_destroy. This seems to work
// well though, so leaving as is.
backdrop_session_destroy_uid($account->uid);
$gotourl = base_path();
if ($config->get('logoutgotourl')) {
$gotourl = $config->get('logoutgotourl');
}
// Allow modules to alter $gotourl.
backdrop_alter('simplesamlphp_auth_logout_gotourl', $gotourl, $account);
$_simplesamlphp_auth_as->logout($gotourl);
}
}
/**
* Implements hook_user_delete().
*/
function simplesamlphp_auth_user_delete($account) {
db_delete('authmap')
->condition('uid', $account->uid)
->condition('authname', $account->name)
->execute();
}
/**
* Implements hook_form_alter().
*/
function simplesamlphp_auth_form_alter(&$form, $form_state, $form_id) {
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
// If the user has a simplesamlphp_auth authmap record, then don't require
// them to know their Backdrop password. This will allow them to change their
// e-mail address, and set a Backdrop password if they want to and are allowed.
if ((isset($form['#user']->init) && $form['#user']->init) && (_simplesaml_auth_user_has_authmap($form['#user']->init) && $form_id == 'user_profile_form')) {
unset($form['account']['current_pass']);
unset($form['account']['current_pass_required_values']);
$form['#validate'] = array_diff($form['#validate'], array('user_validate_current_pass'));
// If the user is a simplesamlphp_auth user and is NOT allowed to set their
// Backdrop password, remove the fields from the form.
if (!config_get('simplesamlphp_auth.settings', 'allowsetbackdroppwd')) {
unset($form['account']['pass']);
}
}
}
/**
* Implements hook_block_view().
*/
function simplesamlphp_auth_block_view($delta = '') {
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
switch ($delta) {
case 0:
$block = array(
'subject' => t('simpleSAMLphp login'),
'content' => _simplesamlphp_auth_generate_block_text(),
);
break;
}
return $block;
}
/**
* Implements hook_block_info().
*/
function simplesamlphp_auth_block_info() {
$block = array(
array(
'info' => t('simpleSAMLphp authentication'),
'cache' => BACKDROP_NO_CACHE,
),
);
return $block;
}
/****************************************************************************
* Private functions ********************************************************
****************************************************************************/
/**
* Checks to see if authentication via SimpleSAMLphp should be activated.
*
* @param bool $show_inactive_msg
* Whether to display the "module not activated" message.
*
* @return bool
* TRUE/FALSE
*/
function _simplesamlphp_auth_isEnabled($show_inactive_msg = FALSE) {
$config = config('simplesamlphp_auth.settings');
global $user;
$failure = NULL;
$is_activated = $config->get('activate');
$basedir = $config->get('installdir');
if ($is_activated) {
// Make sure we know where SimpleSAMLphp is.
if (!class_exists('\SimpleSAML\Configuration') && !file_exists($basedir)) {
$failure = t('This module requires the SimpleSAMLphp library. See the !simplesamlphp module project page for further information.', array('!simplesamlphp' => l(t('SimpleSAMLphp'), 'https://backdropcms.org/project/simplesamlphp_auth'))); watchdog('simplesamlphp_auth', $failure, NULL, WATCHDOG_WARNING);
}
// If there were no failures, then it should be activated.
if (!$failure) {
return TRUE;
}
}
// If we made it this far, it's not activated.
// Communicate but don't be too annoying.
if ($failure && $show_inactive_msg && (1 == $user->uid || user_access('access administration pages')) && (preg_match('/admin\/people/', request_uri()) || preg_match('/admin\/modules/', request_uri()) || preg_match('/admin\/config/', request_uri()))) {
backdrop_set_message($failure);
}
return FALSE;
}
/**
* Gets the authname attribute from the SAML assertion.
*
* @return string
* The authname attribute.
*/
function _simplesamlphp_auth_get_authname() {
$config = config('simplesamlphp_auth.settings');
global $_simplesamlphp_auth_saml_attributes;
$authname = '';
// Check if valid local session exists..
if (isset($_simplesamlphp_auth_saml_attributes)) {
_simplesaml_auth_debug(t('_simplesamlphp_auth_get_authname: Valid local session exist'));
if (isset($_simplesamlphp_auth_saml_attributes[$config->get('unique_id')])) {
$authname = $_simplesamlphp_auth_saml_attributes[$config->get('unique_id')][0];
}
else {
throw new Exception(t('error in simplesamlphp_auth.module: no valid unique id attribute set'));
}
}
return $authname;
}
/**
* Gets the default name attribute from the SAML assertion.
*
* @return string
* The name attribute.
*/
function _simplesamlphp_auth_get_default_name($account) {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$default_name = '';
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$auth_user_name_attr = config_get('simplesamlphp_auth.settings', 'user_name');
if ((!isset($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr])) ||
(!isset($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0])) ||
($_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0] == '')
) {
throw new Exception(t('There was no set attribute named "%auth_user_name_attr" returned for user %uid.',
array(
'%auth_user_name_attr' => $auth_user_name_attr,
'%uid' => $account,
)));
}
$default_name = $_simplesamlphp_auth_saml_attributes[$auth_user_name_attr][0];
}
return $default_name;
}
/**
* Gets the mail attribute.
*
* @return string
* The mail attribute.
*/
function _simplesamlphp_auth_get_mail() {
$config = config('simplesamlphp_auth.settings');
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$mail_address = '';
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
if (isset($_simplesamlphp_auth_saml_attributes[$config->get('mailattr')])) {
$mail_address = $_simplesamlphp_auth_saml_attributes[$config->get('mailattr')][0];
}
else {
throw new Exception(t('Error in simplesamlphp_auth.module: No valid mail attribute set.'));
}
}
return $mail_address;
}
/**
* Forces HTTPS connections.
*/
function _forcehttps_rewrite($url) {
if (config_get('simplesamlphp_auth.settings', 'forcehttps')) {
$url = str_replace('http://', 'https://', $url);
_simplesaml_auth_debug('forcehttps rewrite: ' . $url);
}
return $url;
}
/**
* Generates the text for the log in block.
*/
function _simplesamlphp_auth_generate_block_text() {
global $_simplesamlphp_auth_as;
$block_content = '';
global $user;
if (!_simplesamlphp_auth_isEnabled()) {
// Exit without executing.
return;
}
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$block_content .= '<p>' . t('Logged in as: @username', array('@username' => $user->name))
. '<br />' . l(t('Log out'), 'user/logout') . '</p>';
}
else {
$block_content .= '<p>' . l(filter_xss_admin(config_get_translated('simplesamlphp_auth.settings', 'login_link_display_name')), 'saml_login') . '</p>';
}
return $block_content;
}
/**
* Evaluates a role rule.
* The rules work as follows:
* = does an exact match on an attribute and will iterate over array values if
* the array is multivalued.
* @= matches the domain portion of an email address. It assumes the attribute
* is a string, and will not iterate over an array (but take the first value).
* ~= does a partial string match on the attribute, and does iterate over multiple
* values, returning true if any of the values match.
*
*
* @param array $roleruleevaluation
* An array containing the role rule to evaluate.
* @param array $attributes
* An array containing the identity attributes.
*
* @return array
* An array containing role value and the attribute, or FALSE.
*/
function _simplesamlphp_auth_evaulaterolerule($roleruleevaluation, $attributes) {
_simplesaml_auth_debug(t('Evaluate rule (key=%key,operator=%op,value=%val)', array(
'%key' => $roleruleevaluation[0],
'%op' => $roleruleevaluation[1],
'%val' => $roleruleevaluation[2],
)));
if (!array_key_exists($roleruleevaluation[0], $attributes)) {
return FALSE;
}
$attribute = $attributes[$roleruleevaluation[0]];
switch ($roleruleevaluation[1]) {
case '=':
return in_array($roleruleevaluation[2], $attribute);
case '@=':
$dc = explode('@', $attribute[0]);
if (count($dc) != 2) {
return FALSE;
}
return ($dc[1] == $roleruleevaluation[2]);
case '~=':
foreach ($attribute as $subattr) {
$pos = strpos($subattr, $roleruleevaluation[2]);
if ($pos !== FALSE) {
return TRUE;
}
}
return FALSE;
}
return FALSE;
}
/**
* Performs role population.
*
* @param array $rolemap
* A string containing the role map.
*
* @return array
* An array containing user's roles.
*/
function _rolepopulation($rolemap) {
global $_simplesamlphp_auth_as;
global $_simplesamlphp_auth_saml_attributes;
$roles = array();
_simplesaml_auth_debug(t('Rolemap: %rolemap', array('%rolemap' => $rolemap)));
// Check if valid local session exists..
if ($_simplesamlphp_auth_as->isAuthenticated()) {
$attributes = $_simplesamlphp_auth_saml_attributes;
if (empty($rolemap)) {
return $roles;
}
_simplesaml_auth_debug(t('Evaluate rolemap: %rolemap', array('%rolemap' => $rolemap)));
$rolerules = explode('|', $rolemap);
foreach ($rolerules as $rolerule) {
_simplesaml_auth_debug(t('Evaluate role rule: %rolerule', array('%rolerule' => $rolerule)));
$roleruledecompose = explode(':', $rolerule, 2);
$roleid = $roleruledecompose[0];
$roleruleevaluations = explode(';', $roleruledecompose[1]);
$addnew = TRUE;
foreach ($roleruleevaluations as $roleruleevaluation) {
_simplesaml_auth_debug(t('Evaluate role evaulation: %roleruleeval', array('%roleruleeval' => $roleruleevaluation)));
$roleruleevaluationdc = explode(',', $roleruleevaluation);
if (!_simplesamlphp_auth_evaulaterolerule($roleruleevaluationdc, $attributes)) {
$addnew = FALSE;
}
}
if ($addnew) {
$roles[$roleid] = $roleid;
_simplesaml_auth_debug(t('Add new role: %roleid', array('%roleid' => $roleid)));
}
}
}
return $roles;
}
/**
* See if the user has an authmap record for simplesamlphp_auth.
*/
function _simplesaml_auth_user_has_authmap($authname) {
$authmaps = authmap_get_authmaps($authname);
$return = 0;
if (is_array($authmaps)) {
$return = in_array('simplesamlphp_auth', array_keys($authmaps));
}
return $return;
}
/**
* Debug the form API workflow.
*/
function _simplesaml_auth_debug($message) {
watchdog('simplesamlphp', $message, NULL, WATCHDOG_DEBUG);
}
/**
* Logged out user that has an active session in Backdrop but not with simpleSAML.
*/
function _simplesamlphp_auth_destroy_backdrop_session() {
module_load_include('pages.inc', 'user');
user_logout();
}
/****************************************************************************
* Public functions *********************************************************
****************************************************************************/
/**
* Determine if the current user is authenticated through SAML.
*
* @return bool
* TRUE if the current user is authenticated through SAML. FALSE otherwise.
*/
function simplesamlphp_auth_is_authenticated() {
global $_simplesamlphp_auth_as;
// Assume that the user isn't authenticated until proven otherwise.
$authenticated = FALSE;
// If the global variable exists, and the auth flag is set, note it.
if (isset($_simplesamlphp_auth_as) && $_simplesamlphp_auth_as->isAuthenticated()) {
$authenticated = TRUE;
}
// Return the result.
return $authenticated;
}
/**
* Return any attributes provided by the SAML IDP.
*
* @param string $attribute
* The attribute whose value to return. Can be skipped if all attribute
* values are requested.
*
* @return array
* If an attribute was provided, the value of the attribute is returned.
* Otherwise, an array of all attribute values is returned, keyed by
* attribute.
*/
function simplesamlphp_auth_get_attributes($attribute = NULL) {
global $_simplesamlphp_auth_saml_attributes;
if (isset($attribute)) {
// Initially, assume that there's nothing to return.
$result = NULL;
// If the specified attribute is set, grab it.
if (isset($_simplesamlphp_auth_saml_attributes)) {
if (isset($_simplesamlphp_auth_saml_attributes[$attribute])) {
$result = $_simplesamlphp_auth_saml_attributes[$attribute];
}
}
}
// No specific attribute was requested; return all of them.
else {
// Initially, assume that there's nothing to return.
$result = array();
// If the global array exists, return it.
if (isset($_simplesamlphp_auth_saml_attributes)) {
$result = $_simplesamlphp_auth_saml_attributes;
}
}
// Return whatever we've got.
return $result;
}