CreatePersistentLogin::__invoke()   B
last analyzed

Complexity

Conditions 3
Paths 13

Size

Total Lines 82

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 13
nop 1
dl 0
loc 82
ccs 35
cts 35
cp 1
crap 3
rs 8.3927
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Germania\PermanentAuth;
3
4
use Germania\PermanentAuth\Exceptions\StorageExceptionInterface;
5
use Psr\Log\LoggerInterface;
6
use Psr\Log\NullLogger;
7
use RandomLib\Generator;
8
9
10
/**
11
 * This Callable creates a permanent login for a User ID on both Client and Server side.
12
 *
13
 * - Creates a selector and token pair.
14
 * - Stores hashed token with Expiration date in the server-side database, using $store_on_server Callable
15
 * - Stores selector and token with Expiration date in a client-side cookie, using $store_on_client Callable
16
 *
17
 * In case one of the Callables throws a `StorageExceptionInterface` it will give another try,
18
 * up to 5 attempts until it returns false.
19
 */
20
class CreatePersistentLogin
21
{
22
    /**
23
     * @var \RandomLib\Generator
24
     */
25
    public $generator;
26
27
    /**
28
     * @var Callable
29
     */
30
    public $store_on_client;
31
32
    /**
33
     * @var Callable
34
     */
35
    public $hash_callable;
36
37
    /**
38
     * @var Callable
39
     */
40
    public $store_on_server;
41
42
    /**
43
     * @var \DateTime
44
     */
45
    public $valid_until;
46
47
    /**
48
     * @var LoggerInterface
49
     */
50
    public $logger;
51
52
    /**
53
     * Set length of cookie selector
54
     * @var integer
55
     */
56
    public $selector_length = 32;
57
58
    /**
59
     * Set length of token
60
     * @var integer
61
     */
62
    public $token_length = 256;
63
64
65
66
    /**
67
     * Counts the attemtps it took for creating a unique selector
68
     * @var integer
69
     */
70
    public $attempts = 0;
71
72
    /**
73
     * Limit selector creation to max. attempts.
74
     * @var integer
75
     */
76
    public $max_attempts = 5;
77
78
79
80
    /**
81
     * @param Generator         $generator        ircmaxells' RandomLib\Generator instance
82
     * @param Callable          $store_on_client  Callable for setting Authentication cookie
83
     * @param Callable          $hash_callable    Callable for securely hashing the token
84
     * @param Callable          $store_on_server  Callable for storing the Permanent Login
85
     * @param DateTime          $valid_until      How long permanent login shall be valid
86
     * @param LoggerInterface   $logger           Optional: PSR-3 Logger
87
     */
88 15
    public function __construct( Generator $generator, Callable $store_on_client, Callable $hash_callable, Callable $store_on_server, \DateTime $valid_until, LoggerInterface $logger = null)
89
    {
90 15
        $this->generator        = $generator;
91 15
        $this->store_on_client  = $store_on_client;
92 15
        $this->hash_callable    = $hash_callable;
93 15
        $this->store_on_server  = $store_on_server;
94 15
        $this->valid_until      = $valid_until;
95 15
        $this->logger           = $logger ?: new NullLogger;
96 15
    }
97
98
99
    /**
100
     * @param  $user_id The user ID to create a persistent login for
101
     *
102
     * @return bool FALSE when maximum attenpts exceeded
103
     *
104
     * @throws   description
105
     */
106 15
    public function __invoke( $user_id )
107
    {
108 15
        $this->attempts++;
109
        try {
110
111
            // ------------------------------------------
112
            // 1. Create cookie selector and auth token:
113
            // ------------------------------------------
114
115 15
            $selector = $this->generator->generateString( $this->selector_length );
116 15
            $token    = $this->generator->generate( $this->token_length );
117
118
119
120
            // ------------------------------------------
121
            // 2. Store in database.
122
            //    May throw StorageExceptionInterface exception
123
            // ------------------------------------------
124
125 15
            $hash_callable = $this->hash_callable;
126 15
            $token_hash     = $hash_callable( $token );
127
128 15
            $store_on_server = $this->store_on_server;
129 15
            $store_on_server( $user_id, $selector, $token_hash, $this->valid_until );
130
131
132
133
            // ------------------------------------------
134
            // 3. Store on Client
135
            //    May throw StorageExceptionInterface exception
136
            // ------------------------------------------
137
138 10
            $store_on_client = $this->store_on_client;
139 10
            $store_on_client(
140 10
                $selector,
141 8
                $token,
142 10
                $this->valid_until->getTimestamp()
143 2
            );
144
145
146
            // ------------------------------------------
147
            // Well done.
148
            // ------------------------------------------
149
150 5
            $this->logger->info("Created persistent login", [
151 5
                'attempts'    => $this->attempts,
152 5
                'user_id'     => $user_id,
153 5
                'selector'    => $selector,
154 5
                'valid_until' => $this->valid_until->format('Y-m-d, H:i:s')
155 1
            ]);
156
157
            // Reset counter
158 5
            $this->attempts = 0;
159 5
            return true;
160
        }
161
162 10
        catch( StorageExceptionInterface $e) {
163 10
            $this->logger->warning("Could not store permanent login.", [
164 10
                'exception'   => $e->getMessage(),
165 10
                'user_id'     => $user_id,
166 8
                'selector'    => $selector
167 2
            ]);
168
        }
169
170
        //
171
        // Try again
172
        //
173 10
        if ($this->attempts < $this->max_attempts):
174 10
            return $this->__invoke( $user_id );
175
        endif;
176
177
        //
178
        // Error on too much attempts
179
        //
180 10
        $this->logger->error("Could not create permanent login; stop after {$this->attempts} times.", [
181 10
            'user_id'     => $user_id,
182 8
            'selector'    => $selector
183 2
        ]);
184
185 10
        return false;
186
187
    }
188
189
}
190