Issues (102)

src/Service/BackupCodeGenerator.php (2 issues)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SilverStripe\MFA\Service;
6
7
use SilverStripe\Core\Config\Configurable;
8
use SilverStripe\Core\Extensible;
9
use SilverStripe\Core\Injector\Injectable;
10
use SilverStripe\MFA\State\BackupCode;
11
use SilverStripe\Security\Security;
12
13
class BackupCodeGenerator implements BackupCodeGeneratorInterface
14
{
15
    use Configurable;
16
    use Extensible;
17
    use Injectable;
18
19
    /**
20
     * The number of back-up codes that should be generated for a user. Note that changing this value will not
21
     * regenerate or generate new codes to meet the new number. The user will have to manually regenerate codes to
22
     * receive the new number of codes.
23
     *
24
     * @config
25
     * @var int
26
     */
27
    private static $backup_code_count = 15;
0 ignored issues
show
The private property $backup_code_count is not used, and could be removed.
Loading history...
28
29
    /**
30
     * The length of each individual backup code.
31
     *
32
     * @config
33
     * @var int
34
     */
35
    private static $backup_code_length = 12;
0 ignored issues
show
The private property $backup_code_length is not used, and could be removed.
Loading history...
36
37
    /**
38
     * Generates a list of backup codes
39
     *
40
     * {@inheritDoc}
41
     */
42
    public function generate(): array
43
    {
44
        $codeCount = (int) $this->config()->get('backup_code_count');
45
        $codeLength = (int) $this->config()->get('backup_code_length');
46
        $charset = $this->getCharacterSet();
47
48
        $codes = [];
49
        while (count($codes) < $codeCount) {
50
            $code = $this->generateCode($charset, $codeLength);
51
52
            if (!in_array($code, $codes)) {
53
                $hashData = Security::encrypt_password($code);
54
                $codes[] = BackupCode::create($code, $hashData['password'], $hashData['algorithm'], $hashData['salt']);
55
            }
56
        }
57
58
        return $codes;
59
    }
60
61
    public function getCharacterSet(): array
62
    {
63
        $characterSet = range('a', 'z');
64
65
        $this->extend('updateCharacterSet', $characterSet);
66
67
        return $characterSet;
68
    }
69
70
    /**
71
     * Generates a backup code at the specified string length, using a mixture of digits and mixed case letters
72
     *
73
     * @param array $charset
74
     * @param int $codeLength
75
     * @return string
76
     */
77
    protected function generateCode(array $charset, int $codeLength): string
78
    {
79
        $characters = [];
80
        $numberOfOptions = count($charset);
81
        while (count($characters) < $codeLength) {
82
            $key = random_int(0, $numberOfOptions - 1); // zero based array
83
            $characters[] = $charset[$key];
84
        }
85
        return implode($characters);
86
    }
87
}
88