1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CILogon\Service; |
4
|
|
|
|
5
|
|
|
use CILogon\Service\Util; |
6
|
|
|
use CILogon\Service\Content; |
7
|
|
|
use CILogon\Service\Loggit; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* ShibError |
11
|
|
|
* This class handles errors passed by the Shibboleth SP |
12
|
|
|
* software when /etc/shibboleth/shibboleth2.xml is |
13
|
|
|
* configured as follows: |
14
|
|
|
* <Errors redirectErrors="https://cilogon.org"/> |
15
|
|
|
* Note that with v.2.4.x of the Shibboleth SP software, |
16
|
|
|
* the 'redirectErrors' attribute must be an absolute URL, |
17
|
|
|
* meaning that the shibboleth2.xml file will be different |
18
|
|
|
* for https://test.cilogon.org/. V.2.5 of the SP |
19
|
|
|
* software should allow relative URLS. |
20
|
|
|
* |
21
|
|
|
* To handle errors from the Shibboleth SP software, |
22
|
|
|
* create a new ShibError instance at the root index.php |
23
|
|
|
* file. The constructor looks for various 'GET' |
24
|
|
|
* parameters that get passed by the SP software. If there |
25
|
|
|
* is an error, show an error page with the info provided |
26
|
|
|
* by the SP software. |
27
|
|
|
* |
28
|
|
|
* Example usage: |
29
|
|
|
* require_once 'ShibError.php'; |
30
|
|
|
* // The class constructor does ALL of the work |
31
|
|
|
* $shiberror = new ShibError(); |
32
|
|
|
*/ |
33
|
|
|
class ShibError |
34
|
|
|
{ |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var array $errorarray Holds the values of the various shibboleth |
38
|
|
|
* error parameters |
39
|
|
|
*/ |
40
|
|
|
private $errorarray; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array $errorparams Shibboleth error parameters passed to the |
44
|
|
|
* redirectErrors handler: |
45
|
|
|
* https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPErrors |
46
|
|
|
*/ |
47
|
|
|
private static $errorparams = array( |
48
|
|
|
'requestURL', // The URL associated with the request |
49
|
|
|
'errorType', // The general type of error |
50
|
|
|
'errorText', // The actual error message |
51
|
|
|
'entityID', // Name of identity provider, if known |
52
|
|
|
'now', // Current date and time |
53
|
|
|
'statusCode', // SAML status code causing error, sent by IdP |
54
|
|
|
'statusCode2', // SAML sub-status code causing error, sent by IdP |
55
|
|
|
'statusMessage', // SAML status message, sent by IdP |
56
|
|
|
'contactName', // A support contact name for the IdP |
57
|
|
|
// provided by that site's metadata. |
58
|
|
|
'contactEmail', // A contact email address for the IdP contact |
59
|
|
|
// provided by that site's metadata. |
60
|
|
|
'errorURL', // The URL of an error handling page for the IdP |
61
|
|
|
// provided by that site's metadata. |
62
|
|
|
); |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* __construct |
66
|
|
|
* |
67
|
|
|
* Default constructor. This method attempts to read in the |
68
|
|
|
* various Shibboleth SP error parameters that would have been |
69
|
|
|
* passed as parameters to the 'redirectErrors' handler URL, i.e. |
70
|
|
|
* in the $_GET global variable. |
71
|
|
|
* |
72
|
|
|
* @return ShibError A new ShibError object. |
73
|
|
|
*/ |
74
|
|
|
public function __construct() |
75
|
|
|
{ |
76
|
|
|
$this->errorarray = array(); |
77
|
|
|
foreach (self::$errorparams as $param) { |
78
|
|
|
if (isset($_GET[$param])) { |
79
|
|
|
$this->errorarray[$param] = rtrim($_GET[$param]); |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
if ($this->isError()) { |
84
|
|
|
// CIL-410 Temporary fix for /secure/testidp Shibboleth error. |
85
|
|
|
// Check for error and redirect to /testidp . |
86
|
|
|
if ( |
87
|
|
|
(isset($this->errorarray['errorType'])) && |
88
|
|
|
($this->errorarray['errorType'] == 'shibsp::ConfigurationException') && |
89
|
|
|
(isset($this->errorarray['errorText'])) && |
90
|
|
|
($this->errorarray['errorText'] == |
91
|
|
|
'None of the configured SessionInitiators handled the request.') && |
92
|
|
|
(preg_match('%/secure/testidp%', $this->errorarray['requestURL'])) |
93
|
|
|
) { |
94
|
|
|
header('Location: https://' . Util::getDN() . '/testidp/'); |
95
|
|
|
// CIL-480 Check for user IdP login failure and OAuth transaction |
96
|
|
|
// and redirect appropriately |
97
|
|
|
} elseif ( |
98
|
|
|
(isset($this->errorarray['errorType'])) && |
99
|
|
|
($this->errorarray['errorType'] == 'opensaml::FatalProfileException') && |
100
|
|
|
(isset($this->errorarray['errorText'])) && |
101
|
|
|
($this->errorarray['errorText'] == 'SAML response reported an IdP error.') && |
102
|
|
|
(isset($this->errorarray['statusCode2'])) && |
103
|
|
|
($this->errorarray['statusCode2'] == 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed') |
104
|
|
|
) { |
105
|
|
|
$clientparams = json_decode(Util::getSessionVar('clientparams'), true); // OAuth 2.0 |
106
|
|
|
$failureuri = Util::getSessionVar('failureuri'); // OAuth 1.0a |
107
|
|
|
if (array_key_exists('redirect_uri', $clientparams)) { |
108
|
|
|
Util::unsetAllUserSessionVars(); |
109
|
|
|
header('Location: ' . $clientparams['redirect_uri'] . |
110
|
|
|
(preg_match('/\?/', $clientparams['redirect_uri']) ? '&' : '?') . |
111
|
|
|
'error=access_denied&error_description=' . |
112
|
|
|
'User%20denied%20authorization%20request' . |
113
|
|
|
((array_key_exists('state', $clientparams)) ? |
114
|
|
|
'&state=' . $clientparams['state'] : '')); |
115
|
|
|
} elseif (strlen($failureuri) > 0) { |
116
|
|
|
Util::unsetAllUserSessionVars(); |
117
|
|
|
header('Location: ' . $failureuri . '?reason=cancel'); |
118
|
|
|
} else { |
119
|
|
|
$this->printError(); |
120
|
|
|
} |
121
|
|
|
} else { |
122
|
|
|
$this->printError(); |
123
|
|
|
} |
124
|
|
|
exit; // No further processing!!! |
|
|
|
|
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* isError |
130
|
|
|
* |
131
|
|
|
* This method returns true if several Shibboleth error parameters |
132
|
|
|
* were passed in via the $_GET array. These parameters are sent |
133
|
|
|
* by the Shibboleth SP software when there is an error. |
134
|
|
|
* |
135
|
|
|
* @return bool True if several Shibboleth error parameters were |
136
|
|
|
* passed to the handler URL. False otherwise. |
137
|
|
|
*/ |
138
|
|
|
public function isError() |
139
|
|
|
{ |
140
|
|
|
return ((isset($this->errorarray['requestURL'])) && |
141
|
|
|
(isset($this->errorarray['errorType'])) && |
142
|
|
|
(isset($this->errorarray['errorText'])) && |
143
|
|
|
(isset($this->errorarray['now'])) && |
144
|
|
|
(strlen($this->errorarray['requestURL']) > 0) && |
145
|
|
|
(strlen($this->errorarray['errorType']) > 0) && |
146
|
|
|
(strlen($this->errorarray['errorText']) > 0) && |
147
|
|
|
(strlen($this->errorarray['now']) > 0)); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* printError |
152
|
|
|
* |
153
|
|
|
* This method prints out text for an error message page. It also |
154
|
|
|
* logs the error and sends an alert email with the shibboleth |
155
|
|
|
* error parameters. You should probably 'exit()' after you call |
156
|
|
|
* this so more HTML doesn't get output. |
157
|
|
|
*/ |
158
|
|
|
public function printError() |
159
|
|
|
{ |
160
|
|
|
$errorstr1 = ''; // For logging - one line |
161
|
|
|
$errorstr2 = ''; // For HTML and email - multi-line |
162
|
|
|
foreach ($this->errorarray as $key => $value) { |
163
|
|
|
$errorstr1 .= Util::htmlent($key . '="' . $value . '" '); |
164
|
|
|
$errorstr2 .= Util::htmlent(sprintf("%-14s= %s\n", $key, $value)); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$log = new Loggit(); |
168
|
|
|
$log->error('Shibboleth error: ' . $errorstr1); |
169
|
|
|
|
170
|
|
|
Content::printHeader('Shiboleth Error'); |
171
|
|
|
Content::printCollapseBegin('shiberror', 'Shibboleth Error', false); |
172
|
|
|
|
173
|
|
|
echo ' |
174
|
|
|
<div class="card-body px-5"> |
175
|
|
|
<div class="card-text my-2"> |
176
|
|
|
The CILogon Service has encountered a Shibboleth error. |
177
|
|
|
</div> <!-- end card-text --> |
178
|
|
|
'; |
179
|
|
|
|
180
|
|
|
Content::printErrorBox('<pre>' . $errorstr2 . '</pre>'); |
181
|
|
|
|
182
|
|
|
echo ' |
183
|
|
|
<div class="card-text my-2"> |
184
|
|
|
System administrators have been notified. |
185
|
|
|
This may be a temporary error. Please try again later, or |
186
|
|
|
contact us at the email address at the bottom of the page. |
187
|
|
|
</div> <!-- end card-text --> |
188
|
|
|
'; |
189
|
|
|
|
190
|
|
|
$skin = Util::getSkin(); |
191
|
|
|
$forceauthn = $skin->getConfigOption('forceauthn'); |
192
|
|
|
if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) { |
193
|
|
|
echo ' |
194
|
|
|
<div class="card-text my-2"> |
195
|
|
|
Note that this error may be due to your selected Identity |
196
|
|
|
Provider (IdP) not fully supporting "forced |
197
|
|
|
reauthentication". This setting forces users to log |
198
|
|
|
in at the IdP every time, thus bypassing Single Sign-On |
199
|
|
|
(SSO). |
200
|
|
|
</div> <!-- end card-text --> |
201
|
|
|
'; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
Content::printFormHead(); |
205
|
|
|
|
206
|
|
|
echo ' |
207
|
|
|
<div class="card-text my-2"> |
208
|
|
|
<div class="form-group"> |
209
|
|
|
<div class="form-row align-items-center |
210
|
|
|
justify-content-center"> |
211
|
|
|
<div class="col-auto"> |
212
|
|
|
<input type="submit" name="submit" |
213
|
|
|
class="btn btn-primary submit form-control" |
214
|
|
|
value="Proceed" /> |
215
|
|
|
</div> |
216
|
|
|
</div> <!-- end form-row align-items-center --> |
217
|
|
|
</div> <!-- end form-group --> |
218
|
|
|
</div> <!-- end card-text --> |
219
|
|
|
</form> |
220
|
|
|
</div> <!-- end card-body -->'; |
221
|
|
|
|
222
|
|
|
Content::printCollapseEnd(); |
223
|
|
|
Content::printFooter(); |
224
|
|
|
|
225
|
|
|
Util::sendErrorAlert('Shibboleth Error', $errorstr2); |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.