66use Illuminate \Support \Str ;
77use Pterodactyl \Models \User ;
88use PragmaRX \Google2FA \Google2FA ;
9+ use Illuminate \Database \ConnectionInterface ;
910use Illuminate \Contracts \Encryption \Encrypter ;
1011use Pterodactyl \Contracts \Repository \UserRepositoryInterface ;
1112use Pterodactyl \Repositories \Eloquent \RecoveryTokenRepository ;
@@ -33,15 +34,22 @@ class ToggleTwoFactorService
3334 */
3435 private $ recoveryTokenRepository ;
3536
37+ /**
38+ * @var \Illuminate\Database\ConnectionInterface
39+ */
40+ private $ connection ;
41+
3642 /**
3743 * ToggleTwoFactorService constructor.
3844 *
45+ * @param \Illuminate\Database\ConnectionInterface $connection
3946 * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
4047 * @param \PragmaRX\Google2FA\Google2FA $google2FA
4148 * @param \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository $recoveryTokenRepository
4249 * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
4350 */
4451 public function __construct (
52+ ConnectionInterface $ connection ,
4553 Encrypter $ encrypter ,
4654 Google2FA $ google2FA ,
4755 RecoveryTokenRepository $ recoveryTokenRepository ,
@@ -51,6 +59,7 @@ public function __construct(
5159 $ this ->google2FA = $ google2FA ;
5260 $ this ->repository = $ repository ;
5361 $ this ->recoveryTokenRepository = $ recoveryTokenRepository ;
62+ $ this ->connection = $ connection ;
5463 }
5564
5665 /**
@@ -61,11 +70,10 @@ public function __construct(
6170 * @param bool|null $toggleState
6271 * @return string[]
6372 *
73+ * @throws \Throwable
6474 * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
6575 * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
6676 * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
67- * @throws \Pterodactyl\Exceptions\Model\DataValidationException
68- * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
6977 * @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid
7078 */
7179 public function handle (User $ user , string $ token , bool $ toggleState = null ): array
@@ -78,40 +86,42 @@ public function handle(User $user, string $token, bool $toggleState = null): arr
7886 throw new TwoFactorAuthenticationTokenInvalid ('The token provided is not valid. ' );
7987 }
8088
81- // Now that we're enabling 2FA on the account, generate 10 recovery tokens for the account
82- // and store them hashed in the database. We'll return them to the caller so that the user
83- // can see and save them.
84- //
85- // If a user is unable to login with a 2FA token they can provide one of these backup codes
86- // which will then be marked as deleted from the database and will also bypass 2FA protections
87- // on their account.
88- $ tokens = [];
89- if ((! $ toggleState && ! $ user ->use_totp ) || $ toggleState ) {
90- $ inserts = [];
91- for ($ i = 0 ; $ i < 10 ; $ i ++) {
92- $ token = Str::random (10 );
93-
94- $ inserts [] = [
95- 'user_id ' => $ user ->id ,
96- 'token ' => password_hash ($ token , PASSWORD_DEFAULT ),
97- ];
98-
99- $ tokens [] = $ token ;
89+ return $ this ->connection ->transaction (function () use ($ user , $ toggleState ) {
90+ // Now that we're enabling 2FA on the account, generate 10 recovery tokens for the account
91+ // and store them hashed in the database. We'll return them to the caller so that the user
92+ // can see and save them.
93+ //
94+ // If a user is unable to login with a 2FA token they can provide one of these backup codes
95+ // which will then be marked as deleted from the database and will also bypass 2FA protections
96+ // on their account.
97+ $ tokens = [];
98+ if ((! $ toggleState && ! $ user ->use_totp ) || $ toggleState ) {
99+ $ inserts = [];
100+ for ($ i = 0 ; $ i < 10 ; $ i ++) {
101+ $ token = Str::random (10 );
102+
103+ $ inserts [] = [
104+ 'user_id ' => $ user ->id ,
105+ 'token ' => password_hash ($ token , PASSWORD_DEFAULT ),
106+ ];
107+
108+ $ tokens [] = $ token ;
109+ }
110+
111+ // Before inserting any new records make sure all of the old ones are deleted to avoid
112+ // any issues or storing an unnecessary number of tokens in the database.
113+ $ this ->recoveryTokenRepository ->deleteWhere (['user_id ' => $ user ->id ]);
114+
115+ // Bulk insert the hashed tokens.
116+ $ this ->recoveryTokenRepository ->insert ($ inserts );
100117 }
101118
102- // Bulk insert the hashed tokens.
103- $ this ->recoveryTokenRepository ->insert ($ inserts );
104- } elseif ($ toggleState === false || $ user ->use_totp ) {
105- // If we are disabling 2FA on this account we will delete all of the recovery codes
106- // that exist in the database for this account.
107- $ this ->recoveryTokenRepository ->deleteWhere (['user_id ' => $ user ->id ]);
108- }
109-
110- $ this ->repository ->withoutFreshModel ()->update ($ user ->id , [
111- 'totp_authenticated_at ' => Carbon::now (),
112- 'use_totp ' => (is_null ($ toggleState ) ? ! $ user ->use_totp : $ toggleState ),
113- ]);
119+ $ this ->repository ->withoutFreshModel ()->update ($ user ->id , [
120+ 'totp_authenticated_at ' => Carbon::now (),
121+ 'use_totp ' => (is_null ($ toggleState ) ? ! $ user ->use_totp : $ toggleState ),
122+ ]);
114123
115- return $ tokens ;
124+ return $ tokens ;
125+ });
116126 }
117127}
0 commit comments