Passed
Pull Request — master (#7)
by
unknown
02:36 queued 41s
created

OtpService::sendNewOtpToUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
dl 0
loc 11
ccs 2
cts 2
cp 1
rs 10
c 1
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * @copyright 2018 Hilmi Erdem KEREN
5
 * @license MIT
6
 */
7
8
namespace Erdemkeren\Otp;
9
10
use Illuminate\Contracts\Auth\Authenticatable;
11
12
/**
13
 * Class OtpService.
14
 */
15
class OtpService
16
{
17
    /**
18
     * The password generator manager.
19
     *
20
     * @var PasswordGeneratorManagerInterface
21
     */
22
    private $manager;
23
24
    /**
25
     * The encryptor implementation.
26
     *
27
     * @var EncryptorInterface
28
     */
29
    private $encryptor;
30
31
    /**
32
     * The password length.
33
     *
34
     * @var int
35
     */
36
    private $passwordLength;
37
38
    /**
39
     * The default otp password generator.
40
     *
41
     * @var string
42
     */
43
    private $defaultGenerator;
44
45
    /**
46
     * The password generator.
47
     *
48
     * @var callable
49
     */
50
    private $passwordGenerator;
51
52
    /**
53
     * The name of the token class being used
54
     * by the otp service.
55
     *
56
     * @var string
57
     */
58
    private $tokenClass;
59
60
    /**
61
     * OtpService constructor.
62
     *
63
     * @param PasswordGeneratorManagerInterface $manager
64
     * @param EncryptorInterface                $encryptor
65
     * @param string                            $defaultGenerator
66
     * @param int                               $passwordLength
67
     * @param string                            $tokenClass
68
     */
69 8
    public function __construct(
70
        PasswordGeneratorManagerInterface $manager,
71
        EncryptorInterface $encryptor,
72
        string $defaultGenerator,
73
        int $passwordLength,
74
        string $tokenClass
75
    ) {
76 8
        $this->manager = $manager;
77 8
        $this->encryptor = $encryptor;
78 8
        $this->passwordLength = $passwordLength;
79 8
        $this->defaultGenerator = $defaultGenerator;
80
81 8
        if (! class_exists($tokenClass)) {
82 1
            throw new \RuntimeException(
83 1
                "The token implementation [{$tokenClass}] could not be found."
84
            );
85
        }
86
87 8
        $generatorReflection = new \ReflectionClass($tokenClass);
88 8
        if (! $generatorReflection->isInstantiable()) {
89 1
            throw new \RuntimeException(
90 1
                "The token implementation [{$tokenClass}] is not instantiable."
91
            );
92
        }
93
94 8
        if (! is_subclass_of($tokenClass, TokenInterface::class)) {
95 1
            throw new \TypeError(
96 1
                'The token class should be an instance of '.TokenInterface::class
97
            );
98
        }
99
100 8
        $this->tokenClass = $tokenClass;
101 8
    }
102
103
    /**
104
     * Check the otp of the authenticable
105
     * with the given cipher text.
106
     *
107
     * @param mixed  $authenticableId
108
     * @param string $token
109
     *
110
     * @return bool
111
     */
112 1
    public function check($authenticableId, string $token): bool
113
    {
114 1
        $token = $this->retrieveByCipherText($authenticableId, $token);
115
116 1
        return (bool) $token && ! $token->expired();
117
    }
118
119
    /**
120
     * Set the active password generator of the otp service.
121
     *
122
     * @param string $name
123
     */
124 1
    public function setPasswordGenerator(string $name): void
125
    {
126 1
        $this->passwordGenerator = $this->manager->get($name);
127 1
    }
128
129
    /**
130
     * Create a new otp token.
131
     *
132
     * @param Authenticatable|mixed $authenticatableId
133
     * @param int                   $length
134
     *
135
     * @return Token
136
     */
137 2
    public function create($authenticatableId, ?int $length = null): TokenInterface
138
    {
139 2
        $plainText = $this->getPasswordGenerator()($length ?: $this->passwordLength);
140 2
        $cipherText = $this->encryptor->encrypt($plainText);
141
142 2
        if ($authenticatableId instanceof Authenticatable) {
143 2
            $authenticatableId = $authenticatableId->getAuthIdentifier();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $authenticatableId is correct as $authenticatableId->getAuthIdentifier() targeting Erdemkeren\Otp\Http\Midd...le::getAuthIdentifier() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
144
        }
145
146 2
        return $this->tokenClass::create($authenticatableId, $cipherText, $plainText);
147
    }
148
149
    /**
150
     * Create a new otp and notify the user.
151
     *
152
     * @param Authenticatable $user
153
     */
154
    public function sendNewOtpToUser(Authenticatable $user): void
155
    {
156
        $token = OtpFacade::create($user, 6);
157
158 1
        if (! method_exists($user, 'notify')) {
159
            throw new \UnexpectedValueException(
160 1
                'The otp owner should be an instance of notifiable or implement the notify method.'
161
            );
162
        }
163
164
        $user->notify($token->toNotification());
0 ignored issues
show
Unused Code introduced by
The call to Erdemkeren\Otp\Http\Midd...Authenticable::notify() has too many arguments starting with $token->toNotification(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        $user->/** @scrutinizer ignore-call */ 
165
               notify($token->toNotification());

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
165
    }
166
167
    /**
168
     * Retrieve the token of the authenticable
169
     * by the given plain text.
170
     *
171
     * @param mixed  $authenticableId
172 3
     * @param string $plainText
173
     *
174 3
     * @return null|TokenInterface
175 3
     */
176
    public function retrieveByPlainText($authenticableId, string $plainText): ?TokenInterface
177
    {
178 3
        return $this->retrieveByCipherText($authenticableId, $this->encryptor->encrypt($plainText));
179 3
    }
180 3
181
    /**
182
     * Retrieve the token of the authenticable
183
     * by the given cipher text.
184
     *
185
     * @param mixed  $authenticableId
186
     * @param string $cipherText
187
     *
188
     * @return null|TokenInterface
189
     */
190 1
    public function retrieveByCipherText($authenticableId, string $cipherText): ?TokenInterface
191
    {
192 1
        if ($authenticableId instanceof Authenticatable) {
193 1
            $authenticableId = $authenticableId->getAuthIdentifier();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $authenticableId is correct as $authenticableId->getAuthIdentifier() targeting Erdemkeren\Otp\Http\Midd...le::getAuthIdentifier() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
194
        }
195
196
        return $this->tokenClass::retrieveByAttributes([
197
            'authenticable_id' => $authenticableId,
198
            'cipher_text'      => $cipherText,
199
        ]);
200 2
    }
201
202 2
    /**
203
     * Add a new password generator implementation.
204
     *
205
     * @param string                                     $name
206
     * @param callable|PasswordGeneratorInterface|string $generator
207
     */
208
    public function addPasswordGenerator(string $name, $generator): void
209
    {
210
        $this->manager->register($name, $generator);
211
    }
212
213
    /**
214
     * Get the active password generator.
215
     *
216
     * @return callable
217
     */
218
    private function getPasswordGenerator(): callable
219
    {
220
        return $this->passwordGenerator ?: $this->passwordGenerator = $this->manager->get($this->defaultGenerator);
221
    }
222
}
223