Merge pull request #7207 from nupplaphil/bug/6917-php_warnings
[friendica.git/.git] / src / Model / TwoFactorRecoveryCode.php
1 <?php
2
3 namespace Friendica\Model;
4
5 use Friendica\BaseObject;
6 use Friendica\Database\DBA;
7 use Friendica\Util\DateTimeFormat;
8 use PragmaRX\Random\Random;
9 use PragmaRX\Recovery\Recovery;
10
11 /**
12  * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
13  *
14  * @package Friendica\Model
15  */
16 class TwoFactorRecoveryCode extends BaseObject
17 {
18     /**
19      * Returns the number of code the provided users can still use to replace a TOTP code
20      *
21      * @param int $uid User ID
22      * @return int
23      * @throws \Exception
24      */
25     public static function countValidForUser($uid)
26         {
27                 return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
28         }
29
30     /**
31      * Checks the provided code is available to use for login by the provided user
32      *
33      * @param  int $uid User ID
34      * @param string $code
35      * @return bool
36      * @throws \Exception
37      */
38         public static function existsForUser($uid, $code)
39         {
40                 return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
41         }
42
43     /**
44      * Returns a complete list of all recovery codes for the provided user, including the used status
45      *
46      * @param  int $uid User ID
47      * @return array
48      * @throws \Exception
49      */
50         public static function getListForUser($uid)
51         {
52                 $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
53
54                 return DBA::toArray($codesStmt);
55         }
56
57     /**
58      * Marks the provided code as used for the provided user.
59      * Returns false if the code doesn't exist for the user or it has been used already.
60      *
61      * @param  int $uid User ID
62      * @param string $code
63      * @return bool
64      * @throws \Exception
65      */
66         public static function markUsedForUser($uid, $code)
67         {
68                 DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
69
70                 return DBA::affectedRows() > 0;
71         }
72
73     /**
74      * Generates a fresh set of recovery codes for the provided user.
75      * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
76      *
77      * @param  int $uid User ID
78      * @throws \Exception
79      */
80         public static function generateForUser($uid)
81         {
82                 $Random = (new Random())->pattern('[a-z0-9]');
83
84                 $RecoveryGenerator = new Recovery($Random);
85
86                 $codes = $RecoveryGenerator
87                         ->setCount(12)
88                         ->setBlocks(2)
89                         ->setChars(6)
90                         ->lowercase(true)
91                         ->toArray();
92
93                 $generated = DateTimeFormat::utcNow();
94                 foreach ($codes as $code) {
95                         DBA::insert('2fa_recovery_codes', [
96                                 'uid' => $uid,
97                                 'code' => $code,
98                                 'generated' => $generated
99                         ]);
100                 }
101         }
102
103     /**
104      * Deletes all the recovery codes for the provided user.
105      *
106      * @param  int $uid User ID
107      * @throws \Exception
108      */
109         public static function deleteForUser($uid)
110         {
111                 DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
112         }
113
114     /**
115      * Replaces the existing recovery codes for the provided user by a freshly generated set.
116      *
117      * @param  int $uid User ID
118      * @throws \Exception
119      */
120         public static function regenerateForUser($uid)
121         {
122                 self::deleteForUser($uid);
123                 self::generateForUser($uid);
124         }
125 }