Completed
Push — master ( dd0bc2...b5fd08 )
by Oscar
03:27
created

FormTimestamp::__construct()   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
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Middleware;
6
use Psr7Middlewares\Utils;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use RuntimeException;
10
use Exception;
11
12
/**
13
 * Middleware to span protection using the timestamp value in forms.
14
 */
15
class FormTimestamp
16
{
17
    use Utils\FormTrait;
18
    use Utils\CryptTrait;
19
20
    /**
21
     * @var string The honeypot input name
22
     */
23
    protected $inputName = 'hpt_time';
24
25
    /**
26
     * @var int Minimum seconds to determine whether the request is a bot
27
     */
28
    protected $min = 3;
29
30
    /**
31
     * @var int Max seconds to expire the form. Zero to do not expire
32
     */
33
    protected $max = 0;
34
35
    /**
36
     * Set the field name.
37
     * 
38
     * @param string $inputName
39
     * 
40
     * @return self
41
     */
42
    public function inputName($inputName)
43
    {
44
        $this->inputName = $inputName;
45
46
        return $this;
47
    }
48
49
    /**
50
     * Minimum time required.
51
     * 
52
     * @param int $seconds
53
     * 
54
     * @return self
55
     */
56
    public function min($seconds)
57
    {
58
        $this->min = $seconds;
59
60
        return $this;
61
    }
62
63
    /**
64
     * Max time before expire the form.
65
     * 
66
     * @param int $seconds
67
     * 
68
     * @return self
69
     */
70
    public function max($seconds)
71
    {
72
        $this->max = $seconds;
73
74
        return $this;
75
    }
76
77
    /**
78
     * Execute the middleware.
79
     *
80
     * @param ServerRequestInterface $request
81
     * @param ResponseInterface      $response
82
     * @param callable               $next
83
     *
84
     * @return ResponseInterface
85
     */
86 View Code Duplication
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
    {
88
        if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
89
            throw new RuntimeException('FormTimestamp middleware needs FormatNegotiator executed before');
90
        }
91
92
        if (FormatNegotiator::getFormat($request) !== 'html') {
93
            return $next($request, $response);
94
        }
95
96
        if ($this->isPost($request) && !$this->isValid($request)) {
97
            return $response->withStatus(403);
98
        }
99
100
        $response = $next($request, $response);
101
102
        return $this->insertIntoPostForms($response, '<input type="hidden" name="'.$this->inputName.'" value="'.$this->encrypt(time()).'">');
103
    }
104
105
    /**
106
     * Check whether the request is valid.
107
     * 
108
     * @param ServerRequestInterface $request
109
     * 
110
     * @return bool
111
     */
112
    protected function isValid(ServerRequestInterface $request)
113
    {
114
        $data = $request->getParsedBody();
115
116
        //value does not exists
117
        if (empty($data[$this->inputName])) {
118
            return false;
119
        }
120
121
        try {
122
            $time = $this->decrypt($data[$this->inputName]);
123
        } catch (Exception $e) {
124
            return false;
125
        }
126
127
        //value is not valid
128
        if (!is_numeric($time)) {
129
            return false;
130
        }
131
132
        $now = time();
133
134
        //sent from future
135
        if ($now < $time) {
136
            return false;
137
        }
138
139
        $diff = $now - $time;
140
141
        //check min
142
        if ($diff < $this->min) {
143
            return false;
144
        }
145
146
        //check max
147
        if ($this->max && $diff > $this->max) {
148
            return false;
149
        }
150
151
        return true;
152
    }
153
}
154