Completed
Push — master ( b1d15f...81f221 )
by Michael
03:08
created

Middleware::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 1
eloc 2
nc 1
nop 3
1
<?php
2
3
namespace Schnittstabil\Psr7\Csrf;
4
5
use Schnittstabil\Csrf\TokenService;
6
use Schnittstabil\Csrf\TokenServiceInterface;
7
use Schnittstabil\Psr7\Csrf\Middlewares\AcceptHeaderToken;
8
use Schnittstabil\Psr7\Csrf\Middlewares\AcceptMethods;
9
use Schnittstabil\Psr7\Csrf\Middlewares\AcceptParsedBodyToken;
10
use Schnittstabil\Psr7\Csrf\Middlewares\Guard;
11
use Schnittstabil\Psr7\Csrf\Middlewares\GuardInterface;
12
use Schnittstabil\Psr7\Csrf\Middlewares\RespondWithCookieToken;
13
use Schnittstabil\Psr7\Csrf\Middlewares\RespondWithHeaderToken;
14
use Schnittstabil\Psr7\Middleware\Stack as MiddlewareStack;
15
16
/**
17
 * CSRF protection middleware.
18
 */
19
class Middleware extends MiddlewareStack
20
{
21
    protected $isGuarded;
22
    protected $tokenService;
23
24
    /**
25
     * Create a new Middleware.
26
     *
27
     * @param TokenServiceInterface $tokenService A token service.
28
     */
29
    public function __construct(TokenServiceInterface $tokenService)
30
    {
31
        parent::__construct();
32
        $this->isGuarded = false;
33
        $this->tokenService = $tokenService;
34
    }
35
36
    /**
37
     * Create a new Middleware.
38
     *
39
     * @param string $key  Shared secret key used for generating token signatures.
40
     * @param int    $ttl  Default Time to Live in seconds used for calculating the expiration time of the tokens (1440sec === 24min === default of session.gc_maxlifetime).
41
     * @param string $algo Name of hashing algorithm. See hash_algos() for a list of supported algorithms.
42
     *
43
     * @return static
44
     */
45
    public static function create($key, $ttl = 1440, $algo = 'SHA512')
46
    {
47
        return new self(new TokenService($key, $ttl, $algo));
48
    }
49
50
    /**
51
     * Get the token service.
52
     *
53
     * @return TokenServiceInterface
54
     */
55
    public function getTokenService()
56
    {
57
        return $this->tokenService;
58
    }
59
60
    /**
61
     * Push a middleware onto the top of a new Stack instance.
62
     *
63
     * @param callable $newTopMiddleware the middleware to be pushed onto the top.
64
     *
65
     * @return static the new instance
66
     */
67
    public function add(callable $newTopMiddleware)
68
    {
69
        if ($this->isGuarded && $newTopMiddleware instanceof GuardInterface) {
70
            throw new \RuntimeException('Invalid state: already guarded');
71
        }
72
73
        if (!$this->isGuarded && !($newTopMiddleware instanceof GuardInterface)) {
74
            throw new \RuntimeException('Invalid state: not guarded');
75
        }
76
77
        $clone = parent::add($newTopMiddleware);
78
        $clone->isGuarded = true;
79
80
        return $clone;
81
    }
82
83
    /**
84
     * Add new Guard middleware.
85
     *
86
     * @param callable $rejectMiddleware Defaults to `new Reject()`.
87
     *
88
     * @return static
89
     */
90
    public function withGuard(callable $rejectMiddleware = null)
91
    {
92
        return $this->add(new Guard($rejectMiddleware));
93
    }
94
95
    /**
96
     * Add new AcceptHeaderToken middleware.
97
     *
98
     * @param string $headerName Header field name.
99
     *
100
     * @return static
101
     */
102
    public function withAcceptHeaderToken($headerName = 'X-XSRF-TOKEN')
103
    {
104
        return $this->add(new AcceptHeaderToken([$this->tokenService, 'getConstraintViolations'], $headerName));
105
    }
106
107
    /**
108
     * Add new AcceptMethods middleware.
109
     *
110
     * @param string[] $methods HTTP methods allowed to bypass CSRF protection.
111
     *
112
     * @return static
113
     */
114
    public function withAcceptMethods(array $methods = array('GET', 'OPTIONS'))
115
    {
116
        return $this->add(new AcceptMethods($methods));
117
    }
118
119
    /**
120
     * Add new AcceptParsedBodyToken middleware.
121
     *
122
     * @param string|int|mixed[] $path <a href="https://github.com/schnittstabil/get" target="_blank">See `Get::value` for details</a>
123
     *
124
     * @return static
125
     */
126
    public function withAcceptParsedBodyToken($path = 'X-XSRF-TOKEN')
127
    {
128
        return $this->add(new AcceptParsedBodyToken([$this->tokenService, 'getConstraintViolations'], $path));
129
    }
130
131
    /**
132
     * Add new RespondWithCookieToken middleware.
133
     *
134
     * @param string   $cookieName Cookie name.
135
     * @param callable $modify     Allows to modify the cookie; same signature as `$this->modifyCookie`.
136
     *
137
     * @return static
138
     */
139
    public function withRespondWithCookieToken($cookieName = 'XSRF-TOKEN', callable $modify = null)
140
    {
141
        return $this->add(new RespondWithCookieToken([$this->tokenService, 'generate'], $cookieName, $modify));
142
    }
143
144
    /**
145
     * Add new RespondWithHeaderToken middleware.
146
     *
147
     * @param string $headerName Header field name.
148
     *
149
     * @return static
150
     */
151
    public function withRespondWithHeaderToken($headerName = 'XSRF-TOKEN')
152
    {
153
        return $this->add(new RespondWithHeaderToken([$this->tokenService, 'generate'], $headerName));
154
    }
155
}
156