Passed
Push — master ( ddbf8b...086098 )
by Robbie
11:17
created

ConfirmationMiddleware::confirmedEffect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Middleware;
4
5
use SilverStripe\Core\Injector\Injector;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Control\Session;
11
use SilverStripe\Security\Confirmation;
12
use SilverStripe\Security\Security;
13
14
/**
15
 * Checks whether user manual confirmation is required for HTTPRequest
16
 * depending on the rules given.
17
 *
18
 * How it works:
19
 *  - Gives the request to every single rule
20
 *  - If no confirmation items are found by the rules, then move on to the next middleware
21
 *  - initialize the Confirmation\Storage with all the confirmation items found
22
 *  - Check whether the storage has them confirmed already and if yes, move on to the next middleware
23
 *  - Otherwise redirect to the confirmation URL
24
 */
25
class ConfirmationMiddleware implements HTTPMiddleware
26
{
27
    /**
28
     * The confirmation storage identifier
29
     *
30
     * @var string
31
     */
32
    protected $confirmationId = 'middleware';
33
34
    /**
35
     * Confirmation form URL
36
     *
37
     * @var string
38
     */
39
    protected $confirmationFormUrl = '/dev/confirm';
40
41
    /**
42
     * The list of rules to check requests against
43
     *
44
     * @var ConfirmationMiddleware\Rule[]
45
     */
46
    protected $rules;
47
48
    /**
49
     * The list of bypasses
50
     *
51
     * @var ConfirmationMiddleware\Bypass[]
52
     */
53
    protected $bypasses = [];
54
55
    /**
56
     * Where user should be redirected when refusing
57
     * the action on the confirmation form
58
     *
59
     * @var string
60
     */
61
    private $declineUrl;
62
63
    /**
64
     * Init the middleware with the rules
65
     *
66
     * @param ConfirmationMiddleware\Rule[] $rules Rules to check requests against
67
     */
68
    public function __construct(...$rules)
69
    {
70
        $this->rules = $rules;
71
        $this->declineUrl = Director::baseURL();
72
    }
73
74
    /**
75
     * The URL of the confirmation form ("Security/confirm/middleware" by default)
76
     *
77
     * @param HTTPRequest $request Active request
78
     * @param string $confirmationStorageId ID of the confirmation storage to be used
79
     *
80
     * @return string URL of the confirmation form
81
     */
82
    protected function getConfirmationUrl(HTTPRequest $request, $confirmationStorageId)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

82
    protected function getConfirmationUrl(/** @scrutinizer ignore-unused */ HTTPRequest $request, $confirmationStorageId)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
83
    {
84
        return Controller::join_links(
85
            $this->confirmationFormUrl,
86
            urlencode($confirmationStorageId)
87
        );
88
    }
89
90
    /**
91
     * Returns the URL where the user to be redirected
92
     * when declining the action (on the confirmation form)
93
     *
94
     * @param HTTPRequest $request Active request
95
     *
96
     * @return string URL
97
     */
98
    protected function generateDeclineUrlForRequest(HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

98
    protected function generateDeclineUrlForRequest(/** @scrutinizer ignore-unused */ HTTPRequest $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
99
    {
100
        return $this->declineUrl;
101
    }
102
103
    /**
104
     * Override the default decline url
105
     *
106
     * @param string $url
107
     *
108
     * @return $this
109
     */
110
    public function setDeclineUrl($url)
111
    {
112
        $this->declineUrl = $url;
113
        return $this;
114
    }
115
116
    /**
117
     * Check whether the rules can be bypassed
118
     * without user confirmation
119
     *
120
     * @param HTTPRequest $request
121
     *
122
     * @return bool
123
     */
124
    public function canBypass(HTTPRequest $request)
125
    {
126
        foreach ($this->bypasses as $bypass) {
127
            if ($bypass->checkRequestForBypass($request)) {
128
                return true;
129
            }
130
        }
131
132
        return false;
133
    }
134
135
    /**
136
     * Extract the confirmation items from the request and return
137
     *
138
     * @param HTTPRequest $request
139
     *
140
     * @return Confirmation\Item[] list of confirmation items
141
     */
142
    public function getConfirmationItems(HTTPRequest $request)
143
    {
144
        $confirmationItems = [];
145
146
        foreach ($this->rules as $rule) {
147
            if ($item = $rule->getRequestConfirmationItem($request)) {
148
                $confirmationItems[] = $item;
149
            }
150
        }
151
152
        return $confirmationItems;
153
    }
154
155
    /**
156
     * Initialize the confirmation session storage
157
     * with the confirmation items and return an HTTPResponse
158
     * redirecting to the according confirmation form.
159
     *
160
     * @param HTTPRequest $request
161
     * @param Confirmation\Storage $storage
162
     * @param Confirmation\Item[] $confirmationItems
163
     *
164
     * @return HTTPResponse
165
     */
166
    protected function buildConfirmationRedirect(HTTPRequest $request, Confirmation\Storage $storage, array $confirmationItems)
167
    {
168
        $storage->cleanup();
169
170
        foreach ($confirmationItems as $item) {
171
            $storage->putItem($item);
172
        }
173
174
        $storage->setSuccessRequest($request);
175
        $storage->setFailureUrl($this->generateDeclineUrlForRequest($request));
176
177
        $result = new HTTPResponse();
178
        $result->redirect($this->getConfirmationUrl($request, $this->confirmationId));
179
180
        return $result;
181
    }
182
183
    /**
184
     * Process the confirmation items and either perform the confirmedEffect
185
     * and pass the request to the next middleware, or return a redirect to
186
     * the confirmation form
187
     *
188
     * @param HTTPRequest $request
189
     * @param callable $delegate
190
     * @param Confirmation\Item[] $items
191
     *
192
     * @return HTTPResponse
193
     */
194
    protected function processItems(HTTPRequest $request, callable $delegate, $items)
195
    {
196
        $storage = Injector::inst()->createWithArgs(Confirmation\Storage::class, [$request->getSession(), $this->confirmationId, false]);
197
198
        if (!count($storage->getItems())) {
199
            return $this->buildConfirmationRedirect($request, $storage, $items);
200
        }
201
202
        $confirmed = false;
203
        if ($storage->getHttpMethod() === 'POST') {
204
            $postVars = $request->postVars();
205
            $csrfToken = $storage->getCsrfToken();
206
207
            $confirmed = $storage->confirm($postVars) && isset($postVars[$csrfToken]);
208
        } else {
209
            $confirmed = $storage->check($items);
210
        }
211
212
        if (!$confirmed) {
213
            return $this->buildConfirmationRedirect($request, $storage, $items);
214
        }
215
216
        if ($response = $this->confirmedEffect($request)) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response is correct as $this->confirmedEffect($request) targeting SilverStripe\Control\Mid...ware::confirmedEffect() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
217
            return $response;
218
        }
219
220
        $storage->cleanup();
221
        return $delegate($request);
222
    }
223
224
    /**
225
     * The middleware own effects that should be performed on confirmation
226
     *
227
     * This method is getting called before the confirmation storage cleanup
228
     * so that any responses returned here don't trigger a new confirmtation
229
     * for the same request traits
230
     *
231
     * @param HTTPRequest $request
232
     *
233
     * @return null|HTTPResponse
234
     */
235
    protected function confirmedEffect(HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

235
    protected function confirmedEffect(/** @scrutinizer ignore-unused */ HTTPRequest $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
236
    {
237
        return null;
238
    }
239
240
    public function process(HTTPRequest $request, callable $delegate)
241
    {
242
        if ($this->canBypass($request)) {
243
            if ($response = $this->confirmedEffect($request)) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response is correct as $this->confirmedEffect($request) targeting SilverStripe\Control\Mid...ware::confirmedEffect() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
244
                return $response;
245
            } else {
246
                return $delegate($request);
247
            }
248
        }
249
250
        if (!$items = $this->getConfirmationItems($request)) {
251
            return $delegate($request);
252
        }
253
254
        return $this->processItems($request, $delegate, $items);
255
    }
256
257
    /**
258
     * Override the confirmation storage ID
259
     *
260
     * @param string $id
261
     *
262
     * @return $this
263
     */
264
    public function setConfirmationStorageId($id)
265
    {
266
        $this->confirmationId = $id;
267
        return $this;
268
    }
269
270
    /**
271
     * Override the confirmation form url
272
     *
273
     * @param string $url
274
     *
275
     * @return $this
276
     */
277
    public function setConfirmationFormUrl($url)
278
    {
279
        $this->confirmationFormUrl = $url;
280
        return $this;
281
    }
282
283
    /**
284
     * Set the list of bypasses for the confirmation
285
     *
286
     * @param ConfirmationMiddleware\Bypass[] $bypasses
287
     *
288
     * @return $this
289
     */
290
    public function setBypasses($bypasses)
291
    {
292
        $this->bypasses = $bypasses;
293
        return $this;
294
    }
295
}
296