[ 'uuid-1' => ['uuid' => 'uuid-1', 'name' => 'Claude', 'app_id' => 'https://claude.ai/x', 'created' => 1790000000, 'last_used' => null], 'uuid-other' => ['uuid' => 'uuid-other', 'name' => 'Backup-Tool', 'app_id' => '', 'created' => 1, 'last_used' => null], 'uuid-foreign' => ['uuid' => 'uuid-foreign', 'name' => 'Fremd', 'app_id' => 'backup-tool', 'created' => 2, 'last_used' => 5], ], 4 => [ 'uuid-4' => ['uuid' => 'uuid-4', 'name' => 'Claude', 'app_id' => 'https://claude.ai/x', 'created' => 3, 'last_used' => 7], ], 5 => [ 'uuid-5' => ['uuid' => 'uuid-5', 'name' => 'Claude', 'app_id' => 'https://claude.ai/x', 'created' => 4, 'last_used' => null], ], ]; Functions\when('is_multisite')->justReturn(false); Functions\when('current_user_can')->justReturn(true); Functions\when('get_userdata')->alias( static fn (int $id): object => (object) ['user_login' => 'user' . $id] ); Functions\when('get_user_meta')->alias( static fn (int $user, string $key): string => in_array( $user . ':' . $key, ['3:mcp_refresh_jti_uuid-1', '3:mcp_refresh_jti_uuid-foreign', '4:mcp_refresh_jti_uuid-4', '5:mcp_refresh_jti_uuid-5'], true ) ? 'jti' : '' ); } protected function tearDown(): void { WP_Application_Passwords::$items = []; parent::tearDown(); } /** @param list $rows user ID, meta key */ private function db(array $rows): \wpdb { $db = Mockery::mock(\wpdb::class)->makePartial(); $db->expects('get_results') ->with(Mockery::on(static fn (string $sql): bool => str_contains($sql, 'FROM wp_usermeta') && str_contains($sql, "LIKE 'mcp\\_refresh\\_jti\\_%'"))) ->andReturn(array_map( static fn (array $row): object => (object) ['user_id' => $row[0], 'meta_key' => $row[1]], $rows )); return $db; } public function testListsOnlyMcpOauthConnections(): void { $db = $this->db([ ['3', 'mcp_refresh_jti_uuid-1'], ['3', 'mcp_refresh_jti_uuid-gone'], ['3', 'mcp_refresh_jti_uuid-foreign'], ]); $connections = (new ConnectionRepository($db))->all(); self::assertCount(1, $connections); self::assertSame(3, $connections[0]['user_id']); self::assertSame('user3', $connections[0]['user_login']); self::assertSame('Claude', $connections[0]['name']); self::assertSame('uuid-1', $connections[0]['uuid']); self::assertSame('https://claude.ai/x', $connections[0]['app_id']); self::assertSame(1790000000, $connections[0]['created']); self::assertNull($connections[0]['last_used']); } public function testMultisiteListsOnlyMembersAndHidesSuperAdminsFromOtherAdmins(): void { Functions\when('is_multisite')->justReturn(true); Functions\when('get_current_blog_id')->justReturn(2); Functions\when('is_user_member_of_blog')->alias(static fn (int $user, int $blog): bool => $blog === 2 && $user !== 4); Functions\when('is_super_admin')->alias(static fn (?int $user = null): bool => $user === 5); $db = $this->db([ ['3', 'mcp_refresh_jti_uuid-1'], ['4', 'mcp_refresh_jti_uuid-4'], ['5', 'mcp_refresh_jti_uuid-5'], ]); $connections = (new ConnectionRepository($db))->all(); self::assertSame(['uuid-1'], array_column($connections, 'uuid')); } public function testMultisiteSuperAdminSeesOtherSuperAdminsOfTheSite(): void { Functions\when('is_multisite')->justReturn(true); Functions\when('get_current_blog_id')->justReturn(2); Functions\when('is_user_member_of_blog')->alias(static fn (int $user, int $blog): bool => $user !== 4); Functions\when('is_super_admin')->alias(static fn (?int $user = null): bool => $user === null || $user === 5); $db = $this->db([ ['3', 'mcp_refresh_jti_uuid-1'], ['4', 'mcp_refresh_jti_uuid-4'], ['5', 'mcp_refresh_jti_uuid-5'], ]); $connections = (new ConnectionRepository($db))->all(); self::assertSame(['uuid-1', 'uuid-5'], array_column($connections, 'uuid')); } public function testRevokesOnlyMcpOauthConnections(): void { $repository = new ConnectionRepository(Mockery::mock(\wpdb::class)->makePartial()); self::assertFalse($repository->revoke(3, 'uuid-other')); self::assertArrayHasKey('uuid-other', WP_Application_Passwords::$items[3]); self::assertFalse($repository->revoke(3, 'uuid-foreign'), 'marker meta alone is not enough'); self::assertArrayHasKey('uuid-foreign', WP_Application_Passwords::$items[3]); self::assertFalse($repository->revoke(3, 'uuid-gone')); self::assertFalse($repository->revoke(0, 'uuid-1')); self::assertFalse($repository->revoke(3, '')); self::assertTrue($repository->revoke(3, 'uuid-1')); self::assertArrayNotHasKey('uuid-1', WP_Application_Passwords::$items[3]); } public function testMultisiteRevokeRefusesNonMembersAndSuperAdmins(): void { Functions\when('is_multisite')->justReturn(true); Functions\when('get_current_blog_id')->justReturn(2); Functions\when('is_user_member_of_blog')->alias(static fn (int $user, int $blog): bool => $user !== 4); Functions\when('is_super_admin')->alias(static fn (?int $user = null): bool => $user === 5); $repository = new ConnectionRepository(Mockery::mock(\wpdb::class)->makePartial()); self::assertFalse($repository->revoke(4, 'uuid-4')); self::assertFalse($repository->revoke(5, 'uuid-5')); self::assertArrayHasKey('uuid-4', WP_Application_Passwords::$items[4]); self::assertArrayHasKey('uuid-5', WP_Application_Passwords::$items[5]); self::assertTrue($repository->revoke(3, 'uuid-1')); } public function testListsAndRevokesOnlyConnectionsOfUsersTheAdminMayEdit(): void { Functions\when('current_user_can')->alias( static fn (string $cap, ...$args): bool => ! ($cap === 'edit_user' && $args === [4]) ); $db = $this->db([ ['3', 'mcp_refresh_jti_uuid-1'], ['4', 'mcp_refresh_jti_uuid-4'], ]); $repository = new ConnectionRepository($db); self::assertSame(['uuid-1'], array_column($repository->all(), 'uuid')); self::assertFalse($repository->revoke(4, 'uuid-4')); self::assertArrayHasKey('uuid-4', WP_Application_Passwords::$items[4]); self::assertTrue($repository->revoke(3, 'uuid-1')); } }