Passed
Push — 4.1 ( 6d2665...02ad0f )
by
unknown
32:36 queued 24:09
created

AbstractConfirmationToken::checkToken()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Startup;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Core\Convert;
10
use SilverStripe\Security\RandomGenerator;
11
12
/**
13
 * Shared functionality for token-based authentication of potentially dangerous URLs or query
14
 * string parameters
15
 *
16
 * @internal This class is designed specifically for use pre-startup and may change without warning
17
 */
18
abstract class AbstractConfirmationToken
19
{
20
    /**
21
     * @var HTTPRequest
22
     */
23
    protected $request = null;
24
25
    /**
26
     * The validated and checked token for this parameter
27
     *
28
     * @var string|null A string value, or null if either not provided or invalid
29
     */
30
    protected $token = null;
31
32
    /**
33
     * Given a list of token names, suppress all tokens that have not been validated, and
34
     * return the non-validated token with the highest priority
35
     *
36
     * @param array $keys List of token keys in ascending priority (low to high)
37
     * @param HTTPRequest $request
38
     * @return static The token container for the unvalidated $key given with the highest priority
39
     */
40
    public static function prepare_tokens($keys, HTTPRequest $request)
41
    {
42
        $target = null;
43
        foreach ($keys as $key) {
44
            $token = new static($key, $request);
0 ignored issues
show
Unused Code introduced by
The call to SilverStripe\Core\Startu...ionToken::__construct() has too many arguments starting with $key. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

44
            $token = /** @scrutinizer ignore-call */ new static($key, $request);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
45
            // Validate this token
46
            if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
47
                $token->suppress();
48
                $target = $token;
49
            }
50
        }
51
        return $target;
52
    }
53
54
    /**
55
     * Generate a local filesystem path to store a token
56
     *
57
     * @param $token
58
     * @return string
59
     */
60
    protected function pathForToken($token)
61
    {
62
        return TEMP_PATH . DIRECTORY_SEPARATOR . 'token_' . preg_replace('/[^a-z0-9]+/', '', $token);
63
    }
64
65
    /**
66
     * Generate a new random token and store it
67
     *
68
     * @return string Token name
69
     */
70
    protected function genToken()
71
    {
72
        // Generate a new random token (as random as possible)
73
        $rg = new RandomGenerator();
74
        $token = $rg->randomToken('md5');
75
76
        // Store a file in the session save path (safer than /tmp, as open_basedir might limit that)
77
        file_put_contents($this->pathForToken($token), $token);
78
79
        return $token;
80
    }
81
82
    /**
83
     * Is the necessary token provided for this parameter?
84
     * A value must be provided for the token
85
     *
86
     * @return bool
87
     */
88
    public function tokenProvided()
89
    {
90
        return !empty($this->token);
91
    }
92
93
    /**
94
     * Validate a token
95
     *
96
     * @param string $token
97
     * @return boolean True if the token is valid
98
     */
99
    protected function checkToken($token)
100
    {
101
        if (!$token) {
102
            return false;
103
        }
104
105
        $file = $this->pathForToken($token);
106
        $content = null;
107
108
        if (file_exists($file)) {
109
            $content = file_get_contents($file);
110
            unlink($file);
111
        }
112
113
        return $content === $token;
114
    }
115
116
    /**
117
     * Get redirect url, excluding querystring
118
     *
119
     * @return string
120
     */
121
    public function currentURL()
122
    {
123
        return Controller::join_links(Director::baseURL(), $this->request->getURL(false));
124
    }
125
126
    /**
127
     * Forces a reload of the request with the token included
128
     *
129
     * @return HTTPResponse
130
     */
131
    public function reloadWithToken()
132
    {
133
        $location = $this->redirectURL();
134
        $locationJS = Convert::raw2js($location);
135
        $locationATT = Convert::raw2att($location);
136
        $body = <<<HTML
137
<script>location.href='$locationJS';</script>
138
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
139
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue</a>
140
HTML;
141
142
        // Build response
143
        $result = new HTTPResponse($body);
144
        $result->redirect($location);
145
        return $result;
146
    }
147
148
    /**
149
     * Is this parameter requested without a valid token?
150
     *
151
     * @return bool True if the parameter is given without a valid token
152
     */
153
    abstract public function reloadRequired();
154
155
    /**
156
     * Check if this token is provided either in the backurl, or directly,
157
     * but without a token
158
     *
159
     * @return bool
160
     */
161
    abstract public function reloadRequiredIfError();
162
163
    /**
164
     * Suppress the current parameter for the duration of this request
165
     */
166
    abstract public function suppress();
167
168
    /**
169
     * Determine the querystring parameters to include
170
     *
171
     * @param bool $includeToken Include the token value?
172
     * @return array List of querystring parameters, possibly including token parameter
173
     */
174
    abstract public function params($includeToken = true);
175
176
    /**
177
     * @return string
178
     */
179
    abstract public function getRedirectUrlBase();
180
181
    /**
182
     * @return array
183
     */
184
    abstract public function getRedirectUrlParams();
185
186
    /**
187
     * Get redirection URL
188
     *
189
     * @return string
190
     */
191
    abstract protected function redirectURL();
192
}
193