the browser-facing portion of osu!
at master 4.0 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace App\Console\Commands; 7 8use DB; 9use Illuminate\Console\Command; 10use Laravel\Passport\Token; 11 12/** 13 * Removes expired tokens and auth codes from the OAuth tables. 14 * It uses chunkById which is much slower than a straight batch delete, but doesn't lock the entire table while deleting. 15 */ 16class OAuthDeleteExpiredTokens extends Command 17{ 18 protected $signature = 'oauth:delete-expired-tokens'; 19 20 protected $description = 'Deletes expired OAuth tokens'; 21 22 /** @var \Carbon\Carbon */ 23 private $expiredBefore; 24 25 public function handle() 26 { 27 $this->expiredBefore = now()->subDays($GLOBALS['cfg']['osu']['oauth']['retain_expired_tokens_days']); 28 $this->line("Deleting before {$this->expiredBefore}"); 29 30 $this->deleteAuthCodes(); 31 $this->deleteAccessTokens(); 32 $this->deleteClientGrantTokens(); 33 } 34 35 /** 36 * Removes refresh tokens and associated access tokens. 37 * 38 * @return void 39 */ 40 private function deleteAccessTokens() 41 { 42 $refreshTokensQuery = DB::table('oauth_refresh_tokens') 43 ->where('expires_at', '<', $this->expiredBefore) 44 ->select('id', 'access_token_id', 'expires_at'); 45 $refreshTokensTotal = (clone $refreshTokensQuery)->count(); 46 47 $progress = $this->output->createProgressBar($refreshTokensTotal); 48 $progress->setFormat('very_verbose'); 49 50 $accessTokensDeleted = 0; 51 $refreshTokensDeleted = 0; 52 $refreshTokensQuery->chunkById(1000, function ($chunk) use (&$accessTokensDeleted, &$refreshTokensDeleted, $progress) { 53 // This assumes the refresh token always has a longer valid lifetime than the access token. 54 $accessTokensDeleted += Token::whereIn('id', $chunk->pluck('access_token_id'))->delete(); 55 $refreshTokensDeleted += DB::table('oauth_refresh_tokens')->whereIn('id', $chunk->pluck('id'))->delete(); 56 $progress->advance($chunk->count()); 57 }, 'expires_at'); 58 59 $progress->finish(); 60 $this->line(''); 61 $this->line("Deleted {$accessTokensDeleted} expired access tokens."); 62 $this->line("Deleted {$refreshTokensDeleted} expired refresh tokens."); 63 } 64 65 /** 66 * Removes auth codes. 67 * 68 * @return void 69 */ 70 private function deleteAuthCodes() 71 { 72 $query = DB::table('oauth_auth_codes')->where('expires_at', '<', $this->expiredBefore)->select('id', 'expires_at'); 73 $total = (clone $query)->count(); 74 75 $progress = $this->output->createProgressBar($total); 76 $progress->setFormat('very_verbose'); 77 78 $deleted = 0; 79 $query->chunkById(1000, function ($chunk) use (&$deleted, $progress) { 80 $deleted += DB::table('oauth_auth_codes')->whereIn('id', $chunk->pluck('id'))->delete(); 81 $progress->advance($chunk->count()); 82 }, 'expires_at'); 83 84 $progress->finish(); 85 $this->line(''); 86 $this->line("Deleted {$deleted} expired auth codes."); 87 } 88 89 /** 90 * Removes client credential grant tokens. These are access tokens with no associated user id. 91 * 92 * @return void 93 */ 94 private function deleteClientGrantTokens() 95 { 96 $query = Token::where('user_id', null)->where('expires_at', '<', $this->expiredBefore)->select('id', 'expires_at'); 97 $total = (clone $query)->count(); 98 99 $progress = $this->output->createProgressBar($total); 100 $progress->setFormat('very_verbose'); 101 102 $deleted = 0; 103 $query->chunkById(1000, function ($chunk) use (&$deleted, $progress) { 104 $deleted += Token::whereIn('id', $chunk->pluck('id'))->delete(); 105 $progress->advance($chunk->count()); 106 }, 'expires_at'); 107 108 $progress->finish(); 109 $this->line(''); 110 $this->line("Deleted {$deleted} expired client grant tokens."); 111 } 112}