cilogon /
service-lib
| 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!!! |
||
|
0 ignored issues
–
show
|
|||
| 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.