TokenManager   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 13
eloc 30
c 0
b 0
f 0
dl 0
loc 96
ccs 0
cts 29
cp 0
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getNewToken() 0 6 1
A storeToken() 0 5 1
A generateTokenData() 0 9 2
B validateToken() 0 41 9
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca\Security;
11
12
use Exception;
13
use Waca\WebRequest;
14
15
class TokenManager
16
{
17
    /**
18
     * Validates a CSRF token
19
     *
20
     * @param string      $data    The token data string itself
21
     * @param string|null $context Token context for extra validation
22
     * @param int         $minimumLifetime The minimum lifetime (in seconds) the token must have had to be valid
23
     *
24
     * @return bool
25
     */
26
    public function validateToken($data, $context = null, $minimumLifetime = 0)
27
    {
28
        if (!is_string($data) || strlen($data) === 0) {
0 ignored issues
show
introduced by
The condition is_string($data) is always true.
Loading history...
29
            // Nothing to validate
30
            return false;
31
        }
32
33
        $tokens = WebRequest::getSessionTokenData();
34
35
        // if the token doesn't exist, then it's not valid
36
        if (!array_key_exists($data, $tokens)) {
37
            return false;
38
        }
39
40
        /** @var Token $token */
41
        $token = unserialize($tokens[$data]);
42
43
        if ($token->getTokenData() !== $data) {
44
            return false;
45
        }
46
47
        if ($token->getContext() !== $context) {
48
            return false;
49
        }
50
51
        if ($token->isUsed()) {
52
            return false;
53
        }
54
55
        if ($minimumLifetime > 0) {
56
            $age = time() - $token->getGenerationTimestamp()->getTimestamp();
57
            if ($age < $minimumLifetime) {
58
                return false;
59
            }
60
        }
61
62
        // mark the token as used, and save it back to the session
63
        $token->markAsUsed();
64
        $this->storeToken($token);
65
66
        return true;
67
    }
68
69
    /**
70
     * @param string|null $context An optional context for extra validation
71
     *
72
     * @return Token
73
     */
74
    public function getNewToken($context = null)
75
    {
76
        $token = new Token($this->generateTokenData(), $context);
77
        $this->storeToken($token);
78
79
        return $token;
80
    }
81
82
    /**
83
     * Stores a token in the session data
84
     *
85
     * @param Token $token
86
     */
87
    private function storeToken(Token $token)
88
    {
89
        $tokens = WebRequest::getSessionTokenData();
90
        $tokens[$token->getTokenData()] = serialize($token);
91
        WebRequest::setSessionTokenData($tokens);
92
    }
93
94
    /**
95
     * Generates a security token
96
     *
97
     * @return string
98
     * @throws Exception
99
     *
100
     * @category Security-Critical
101
     */
102
    private function generateTokenData()
103
    {
104
        $genBytes = openssl_random_pseudo_bytes(33);
105
106
        if ($genBytes !== false) {
107
            return base64_encode($genBytes);
108
        }
109
110
        throw new Exception('Unable to generate secure token.');
111
    }
112
}