Completed
Push — master ( 951c60...d1ac03 )
by Terrence
12:39
created

ShibError::__construct()   D

Complexity

Conditions 9
Paths 15

Size

Total Lines 39
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 39
ccs 0
cts 28
cp 0
rs 4.909
cc 9
eloc 25
nc 15
nop 0
crap 90
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.
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
76
     */
77
    public function __construct()
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
78
    {
79
        $this->errorarray = array();
80
        foreach (static::$errorparams as $param) {
0 ignored issues
show
Bug introduced by
Since $errorparams is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $errorparams to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
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
            // CIL-410 Temporary fix for /secure/testidp Shibboleth error.
102
            // Check for error and redirect to /testidp .
103
            } elseif (($this->errorarray['errorType'] == 'shibsp::ConfigurationException') &&
104
                ($this->errorarray['errorText'] ==
105
                    'None of the configured SessionInitiators handled the request.') &&
106
                (preg_match('%/secure/testidp%', $this->errorarray['requestURL']))
107
                ) {
108
                header('Location: https://' . Util::getDN() . '/testidp/');
109
            } else {
110
                $this->printError();
111
            }
112
            Util::unsetSessionVar('requestsilver');
113
            exit; // No further processing!!!
0 ignored issues
show
Coding Style Compatibility introduced by
The method __construct() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
114
        }
115
    }
116
117
    /**
118
     * isError
119
     *
120
     * This method returns true if several Shibboleth error parameters
121
     * were passed in via the $_GET array. These parameters are sent
122
     * by the Shibboleth SP software when there is an error.
123
     *
124
     * @return bool True if several Shibboleth error parameters were
125
     *         passed to the handler URL. False otherwise.
126
     */
127
    public function isError()
128
    {
129
        return ((isset($this->errorarray['requestURL'])) &&
130
                (isset($this->errorarray['errorType'])) &&
131
                (isset($this->errorarray['errorText'])) &&
132
                (isset($this->errorarray['now'])) &&
133
                (strlen($this->errorarray['requestURL']) > 0) &&
134
                (strlen($this->errorarray['errorType']) > 0) &&
135
                (strlen($this->errorarray['errorText']) > 0) &&
136
                (strlen($this->errorarray['now']) > 0));
137
    }
138
139
    /**
140
     * printError
141
     *
142
     * This method prints out text for an error message page. It also
143
     * logs the error and sends an alert email with the shibboleth
144
     * error parameters. You should probably 'exit()' after you call
145
     * this so more HTML doesn't get output.
146
     */
147
    public function printError()
148
    {
149
        $errorstr1 = '';  // For logging - one line
150
        $errorstr2 = '';  // For HTML and email - multi-line
151
        foreach ($this->errorarray as $key => $value) {
152
            $errorstr1 .= Util::htmlent($key.'="' . $value . '" ');
153
            $errorstr2 .= Util::htmlent(sprintf("%-14s= %s\n", $key, $value));
154
        }
155
156
        $log = new Loggit();
157
        $log->error('Shibboleth error: ' . $errorstr1);
158
159
        Content::printHeader('Shiboleth Error');
160
161
        echo '
162
        <div class="boxed">
163
        ';
164
165
        $erroroutput = '
166
        <p>
167
        The CILogon Service has encountered a Shibboleth error.
168
        </p>
169
        <blockquote><pre>' . $errorstr2 . '</pre>
170
        </blockquote>
171
        <p>
172
        System administrators have been
173
        notified. This may be a temporary error. Please try again later, or
174
        contact us at the email address at the bottom of the page.
175
        </p>
176
        ';
177
178
        $skin = Util::getSkin();
179
        $forceauthn = $skin->getConfigOption('forceauthn');
180
        if ((!is_null($forceauthn)) && ((int)$forceauthn == 1)) {
181
            $erroroutput .= '
182
            <p>
183
            Note that this error may be due to your selected Identity
184
            Provider (IdP) not fully supporting &quot;forced
185
            reauthentication&quot;. This setting forces users to log in at
186
            the IdP every time, thus bypassing Single Sign-On (SSO).
187
            </p>
188
            ';
189
        }
190
191
        Content::printErrorBox($erroroutput);
192
193
        echo '
194
        <div>
195
        ';
196
        Content::printFormHead();
197
        echo '
198
        <input type="submit" name="submit" class="submit" value="Proceed" />
199
        </form>
200
        </div>
201
        ';
202
203
204
        echo '
205
        </div>
206
        ';
207
        Content::printFooter();
208
209
        Util::sendErrorAlert('Shibboleth Error', $errorstr2);
210
    }
211
}
212