Completed
Push — develop ( 8af9ec...c7538c )
by jake
02:21
created

CsrfHandler::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * Session Handler
4
 *
5
 * PHP version 5
6
 *
7
 * Copyright (C) 2016 Jake Johns
8
 *
9
 * This software may be modified and distributed under the terms
10
 * of the MIT license.  See the LICENSE file for details.
11
 *
12
 * @category  Middleware
13
 * @package   Vperyod\SessionHandler
14
 * @author    Jake Johns <[email protected]>
15
 * @copyright 2016 Jake Johns
16
 * @license   http://jnj.mit-license.org/2016 MIT License
17
 * @link      https://github.com/vperyod/vperyod.session-handler
18
 */
19
20
namespace Vperyod\SessionHandler;
21
22
use Psr\Http\Message\ServerRequestInterface as Request;
23
use Psr\Http\Message\ResponseInterface as Response;
24
25
/**
26
 * CsrfHandler
27
 *
28
 * @category Middleware
29
 * @package  Vperyod\SessionHandler
30
 * @author   Jake Johns <[email protected]>
31
 * @license  http://jnj.mit-license.org/2016 MIT License
32
 * @link     https://github.com/vperyod/vperyod.sesion-handler
33
 */
34
class CsrfHandler
35
{
36
    use SessionRequestAwareTrait;
37
38
    /**
39
     * FailResponder
40
     *
41
     * @var callable
42
     *
43
     * @access protected
44
     */
45
    protected $failResponder;
46
47
    /**
48
     * Create a CSRF Handler
49
     *
50
     * @param callabel $failResponder respond to failed CSRF check
51
     *
52
     * @access public
53
     */
54 9
    public function __construct(callable $failResponder = null)
55
    {
56 9
        $this->failResponder = $failResponder ?: [$this, 'fail'];
57 9
    }
58
59
    /**
60
     * Check non-idempotent and non-ignored requests and respond or continue
61
     *
62
     * @param Request  $request  PSR7 Request
63
     * @param Response $response PSR7 Response
64
     * @param callable $next     Next callable middleware
65
     *
66
     * @return Response
67
     *
68
     * @access public
69
     */
70 9
    public function __invoke(Request $request, Response $response, callable $next)
71
    {
72 9
        if ('GET' === $request->getMethod()) {
73 1
            return $next($request, $response);
74
        }
75
76 8
        $request = $this->withCsrfHeader($request);
77
78 8
        if ($this->ignore($request) || $this->isValid($request)) {
79 3
            return $next($request, $response);
80
        }
81
82 5
        $responder = $this->failResponder;
83 5
        return $responder($request, $response, $next);
84
    }
85
86
    /**
87
     * Check body for posted value, and move to request header
88
     *
89
     * @param Request $request PSR7 Request
90
     *
91
     * @return Request
92
     *
93
     * @access protected
94
     */
95 8
    protected function withCsrfHeader(Request $request)
96
    {
97 8
        $key  = $this->getCsrfName();
98 8
        $body = $request->getParsedBody();
99
100 8
        if (isset($body[$key])) {
101 2
            $value = $body[$key];
102 2
            unset($body[$key]);
103
104
            $request = $request
105 2
                ->withParsedBody($body)
106 2
                ->withHeader($this->getCsrfHeader(), $value);
107
        }
108
109 8
        return $request;
110
    }
111
112
    /**
113
     * Ignore this request?
114
     *
115
     * @param Request $request PSR7 Request
116
     *
117
     * @return bool
118
     *
119
     * @access protected
120
     */
121 8
    protected function ignore(Request $request)
122
    {
123 8
        return $request->getAttribute('ignore-csrf', false);
124
    }
125
126
    /**
127
     * Is CSRF Header Valid?
128
     *
129
     * @param Request $request PSR7 Request
130
     *
131
     * @return bool
132
     *
133
     * @access protected
134
     */
135 7
    protected function isValid(Request $request)
136
    {
137 7
        $key = $this->getCsrfHeader();
138 7
        $value = $request->getHeaderLine($key);
139 7
        return $value && $this->getSession($request)
140 3
            ->getCsrfToken()
141 7
            ->isValid($value);
142
    }
143
144
    /**
145
     * Respond to failed CSRF Check
146
     *
147
     * @param Request  $request  PSR7 Request
148
     * @param Response $response PSR7 Response
149
     *
150
     * @return Response
151
     *
152
     * @access protected
153
     */
154 5
    protected function fail(Request $request, Response $response)
155
    {
156
        $request;
157
        $response = $response
158 5
            ->withStatus(400)
159 5
            ->withHeader('Content-type', 'text/plain');
160
161 5
        $response->getBody()->write('CSRF Detected');
162 5
        return $response;
163
    }
164
}
165