Issues (141)

src/Service/ShibError.php (1 issue)

Severity
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
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
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 &quot;forced
197
                reauthentication&quot;. 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