]> git.mxchange.org Git - friendica.git/blob - src/Model/TwoFactor/RecoveryCode.php
Raw content is now stored with announce messages as well
[friendica.git] / src / Model / TwoFactor / RecoveryCode.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Model\TwoFactor;
23
24 use Friendica\Database\DBA;
25 use Friendica\Util\DateTimeFormat;
26 use PragmaRX\Random\Random;
27 use PragmaRX\Recovery\Recovery;
28
29 /**
30  * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
31  */
32 class RecoveryCode
33 {
34     /**
35      * Returns the number of code the provided users can still use to replace a TOTP code
36      *
37      * @param int $uid User ID
38      * @return int
39      * @throws \Exception
40      */
41     public static function countValidForUser($uid)
42         {
43                 return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
44         }
45
46     /**
47      * Checks the provided code is available to use for login by the provided user
48      *
49      * @param  int $uid User ID
50      * @param string $code
51      * @return bool
52      * @throws \Exception
53      */
54         public static function existsForUser($uid, $code)
55         {
56                 return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
57         }
58
59     /**
60      * Returns a complete list of all recovery codes for the provided user, including the used status
61      *
62      * @param  int $uid User ID
63      * @return array
64      * @throws \Exception
65      */
66         public static function getListForUser($uid)
67         {
68                 $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
69
70                 return DBA::toArray($codesStmt);
71         }
72
73     /**
74      * Marks the provided code as used for the provided user.
75      * Returns false if the code doesn't exist for the user or it has been used already.
76      *
77      * @param  int $uid User ID
78      * @param string $code
79      * @return bool
80      * @throws \Exception
81      */
82         public static function markUsedForUser($uid, $code)
83         {
84                 DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
85
86                 return DBA::affectedRows() > 0;
87         }
88
89     /**
90      * Generates a fresh set of recovery codes for the provided user.
91      * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
92      *
93      * @param  int $uid User ID
94      * @throws \Exception
95      */
96         public static function generateForUser($uid)
97         {
98                 $Random = (new Random())->pattern('[a-z0-9]');
99
100                 $RecoveryGenerator = new Recovery($Random);
101
102                 $codes = $RecoveryGenerator
103                         ->setCount(12)
104                         ->setBlocks(2)
105                         ->setChars(6)
106                         ->lowercase(true)
107                         ->toArray();
108
109                 $generated = DateTimeFormat::utcNow();
110                 foreach ($codes as $code) {
111                         DBA::insert('2fa_recovery_codes', [
112                                 'uid' => $uid,
113                                 'code' => $code,
114                                 'generated' => $generated
115                         ]);
116                 }
117         }
118
119     /**
120      * Deletes all the recovery codes for the provided user.
121      *
122      * @param  int $uid User ID
123      * @throws \Exception
124      */
125         public static function deleteForUser($uid)
126         {
127                 DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
128         }
129
130     /**
131      * Replaces the existing recovery codes for the provided user by a freshly generated set.
132      *
133      * @param  int $uid User ID
134      * @throws \Exception
135      */
136         public static function regenerateForUser($uid)
137         {
138                 self::deleteForUser($uid);
139                 self::generateForUser($uid);
140         }
141 }