the browser-facing portion of osu!
at master 276 lines 8.7 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 Tests\Models\OAuth; 7 8use App\Models\OAuth\Client; 9use App\Models\OAuth\Token; 10use App\Models\User; 11use Laravel\Passport\AuthCode; 12use Laravel\Passport\RefreshToken; 13use Tests\TestCase; 14 15class ClientTest extends TestCase 16{ 17 /** @var Client */ 18 protected $client; 19 20 /** @var User */ 21 protected $owner; 22 23 public function testScopesFromTokensAreAggregated() 24 { 25 $user = User::factory()->create(); 26 $this->client->tokens()->create([ 27 'id' => '1', 28 'revoked' => false, 29 'scopes' => ['identify'], 30 'user_id' => $user->getKey(), 31 ]); 32 33 $this->client->tokens()->create([ 34 'id' => '2', 35 'revoked' => false, 36 'scopes' => ['friends.read'], 37 'user_id' => $user->getKey(), 38 ]); 39 40 $clients = Client::forUser($user); 41 $this->assertCount(1, $clients); 42 $this->assertSame(['friends.read', 'identify'], $clients[0]->scopes); 43 } 44 45 public function testScopesFromDifferentClientsAreNotAggregated() 46 { 47 $user = User::factory()->create(); 48 $this->client->tokens()->create([ 49 'id' => '1', 50 'revoked' => false, 51 'scopes' => ['identify'], 52 'user_id' => $user->getKey(), 53 ]); 54 55 $otherClient = Client::factory()->create(['user_id' => $this->owner]); 56 $otherClient->tokens()->create([ 57 'id' => '2', 58 'revoked' => false, 59 'scopes' => ['friends.read'], 60 'user_id' => $user->getKey(), 61 ]); 62 63 $clients = Client::forUser($user); 64 $this->assertSame(['identify'], $clients->find($this->client->getKey())->scopes); 65 $this->assertSame(['friends.read'], $clients->find($otherClient->getKey())->scopes); 66 } 67 68 public function testScopesFromRevokedTokensAreNotAggregated() 69 { 70 $user = User::factory()->create(); 71 $this->client->tokens()->create([ 72 'id' => '1', 73 'revoked' => false, 74 'scopes' => ['identify'], 75 'user_id' => $user->getKey(), 76 ]); 77 78 $this->client->tokens()->create([ 79 'id' => '2', 80 'revoked' => true, 81 'scopes' => ['friends.read'], 82 'user_id' => $user->getKey(), 83 ]); 84 85 $clients = Client::forUser($user); 86 $this->assertCount(1, $clients); 87 $this->assertSame(['identify'], $clients[0]->scopes); 88 } 89 90 public function testClientWithOnlyRevokedTokensDoesNotShow() 91 { 92 $user = User::factory()->create(); 93 $this->client->tokens()->create([ 94 'id' => '1', 95 'revoked' => true, 96 'scopes' => ['identify'], 97 'user_id' => $user->getKey(), 98 ]); 99 100 $clients = Client::forUser($user); 101 $this->assertEmpty($clients); 102 } 103 104 public function testRevokingClientRemovesItFromTheList() 105 { 106 $user = User::factory()->create(); 107 $this->client->tokens()->create([ 108 'id' => '1', 109 'revoked' => false, 110 'scopes' => ['identify'], 111 'user_id' => $user->getKey(), 112 ]); 113 114 $this->assertCount(1, Client::forUser($user)); 115 $this->client->revokeForUser($user); 116 $this->assertCount(0, Client::forUser($user)); 117 } 118 119 public function testDoesNotAggregateScopesFromOtherUsers() 120 { 121 $user1 = User::factory()->create(); 122 $user2 = User::factory()->create(); 123 $this->client->tokens()->create([ 124 'id' => '1', 125 'revoked' => false, 126 'scopes' => ['identify'], 127 'user_id' => $user1->getKey(), 128 ]); 129 130 $this->client->tokens()->create([ 131 'id' => '2', 132 'revoked' => false, 133 'scopes' => ['friends.read'], 134 'user_id' => $user2->getKey(), 135 ]); 136 137 $clients = Client::forUser($user1); 138 $this->assertCount(1, $clients); 139 $this->assertSame(['identify'], $clients[0]->scopes); 140 } 141 142 public function testDoesNotRevokeOtherUserTokens() 143 { 144 $user1 = User::factory()->create(); 145 $user2 = User::factory()->create(); 146 $this->client->tokens()->create([ 147 'id' => '1', 148 'revoked' => false, 149 'scopes' => ['identify'], 150 'user_id' => $user1->getKey(), 151 ]); 152 153 $this->client->tokens()->create([ 154 'id' => '2', 155 'revoked' => false, 156 'scopes' => ['identify'], 157 'user_id' => $user2->getKey(), 158 ]); 159 160 $this->assertCount(1, Client::forUser($user1)); 161 $this->assertCount(1, Client::forUser($user2)); 162 $this->client->revokeForUser($user1); 163 $this->assertCount(0, Client::forUser($user1)); 164 $this->assertCount(1, Client::forUser($user2)); 165 } 166 167 public function testNumberOfClientsIsLimited() 168 { 169 config_set('osu.oauth.max_user_clients', 1); 170 171 $client = Client::factory() 172 ->allowUnsaved() 173 ->create(['user_id' => $this->owner]); 174 $this->assertFalse($client->exists); 175 $this->assertArrayHasKey('user.oauthClients.count', $client->validationErrors()->all()); 176 } 177 178 public function testNumberOfClientsLimitDoesNotIncludeRevokedClients() 179 { 180 config_set('osu.oauth.max_user_clients', 1); 181 $this->client->update(['revoked' => true]); 182 183 $client = Client::factory()->create(['user_id' => $this->owner]); 184 $this->assertTrue($client->exists); 185 $this->assertEmpty($client->validationErrors()->all()); 186 } 187 188 public function testRevokingClientSkipsValidation() 189 { 190 $client = Client::factory()->make(['user_id' => $this->owner]); 191 $client->save(['skipValidations' => true]); 192 $this->assertTrue($client->exists); 193 $client->revoke(); 194 $this->assertTrue($client->fresh()->revoked); 195 } 196 197 public function testResetSecretChangesClientSecret() 198 { 199 $oldSecret = $this->client->secret; 200 201 $this->client->resetSecret(); 202 203 $this->assertNotSame($oldSecret, $this->client->secret); 204 } 205 206 public function testResetSecretInvalidatesExistingTokens() 207 { 208 $user = User::factory()->create(); 209 $token = $this->client->tokens()->create([ 210 'id' => '1', 211 'revoked' => false, 212 'scopes' => ['identify'], 213 'user_id' => $user->getKey(), 214 ]); 215 216 $token->refreshToken()->create([ 217 'id' => '1', 218 'revoked' => false, 219 ]); 220 221 $this->client->authCodes()->create([ 222 'id' => '1', 223 'revoked' => false, 224 'scopes' => json_encode(['identify']), 225 'user_id' => $user->getKey(), 226 ]); 227 228 // assert no revoked tokens; 229 $this->assertSame(1, Token::where('revoked', false)->count()); 230 $this->assertSame(1, RefreshToken::where('revoked', false)->count()); 231 $this->assertSame(1, AuthCode::where('revoked', false)->count()); 232 233 $this->client->resetSecret(); 234 235 // assert no unrevoked tokens; 236 $this->assertSame(0, Token::where('revoked', false)->count()); 237 $this->assertSame(0, RefreshToken::where('revoked', false)->count()); 238 $this->assertSame(0, AuthCode::where('revoked', false)->count()); 239 } 240 241 public function testResetSecretPreventsAccessWithExistingToken() 242 { 243 $user = User::factory()->create(); 244 $token = $this->client->tokens()->create([ 245 'id' => '1', 246 'revoked' => false, 247 'scopes' => ['identify'], 248 'user_id' => $user->getKey(), 249 ]); 250 251 $this->client->resetSecret(); 252 $token->refresh(); 253 $this->actAsUserWithToken($token); 254 255 $this->get(route('api.me'))->assertUnauthorized(); 256 } 257 258 public function testPassportCreateClientCommand() 259 { 260 $countBefore = Client::count(); 261 262 $this->artisan('passport:client', ['--password' => true]) 263 ->expectsQuestion('What should we name the password grant client?', 'potato') 264 ->expectsQuestion('Which user provider should this client use to retrieve users?', 'user'); 265 266 $this->assertSame($countBefore + 1, Client::count(), 'client was not created.'); 267 } 268 269 protected function setUp(): void 270 { 271 parent::setUp(); 272 273 $this->owner = User::factory()->create(); 274 $this->client = Client::factory()->create(['user_id' => $this->owner]); 275 } 276}