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