Skip to content

Commit

Permalink
Merge branch 'master' into country-access
Browse files Browse the repository at this point in the history
  • Loading branch information
notbakaneko authored Nov 21, 2024
2 parents 77ff38a + 3d826c2 commit 604eff0
Show file tree
Hide file tree
Showing 21 changed files with 290 additions and 53 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ CLIENT_CHECK_VERSION=false
# USER_PROFILE_SCORES_NOTICE=

# MULTIPLAYER_MAX_ATTEMPTS_LIMIT=128
# MULTIPLAYER_ROOM_CLOSE_GRACE_PERIOD_MINUTES=5

# NOTIFICATION_QUEUE=notification
# NOTIFICATION_REDIS_HOST=127.0.0.1
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,16 @@ jobs:
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
restore-keys: ${{ runner.os }}-composer-

- name: Get current date
id: current-date
run: echo "today=$(date '+%Y%m%d')" >> "$GITHUB_OUTPUT"

- name: Cache ip2asn
uses: actions/cache@v4
with:
path: database/ip2asn/
key: ip2asn
key: ip2asn-${{ steps.current-date.outputs.today }}
restore-keys: ip2asn-

- run: ./build.sh

Expand Down
7 changes: 7 additions & 0 deletions app/Http/Controllers/Multiplayer/RoomsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public function __construct()
$this->middleware('require-scopes:public', ['only' => ['index', 'leaderboard', 'show']]);
}

public function destroy($id)
{
Room::findOrFail($id)->endGame(\Auth::user());

return response(null, 204);
}

/**
* Get Multiplayer Rooms
*
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Controllers/RankingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ public function index($mode, $type)

$maxResults = $this->maxResults($modeInt, $stats);
$maxPages = ceil($maxResults / static::PAGE_SIZE);
// TODO: less repeatedly getting params out of request.
$page = \Number::clamp(get_int(request('cursor.page') ?? request('page') ?? 1), 1, $maxPages);
$params = get_params(\Request::all(), null, ['cursor.page:int', 'page:int']);
$page = \Number::clamp($params['cursor']['page'] ?? $params['page'] ?? 1, 1, $maxPages);

$stats = $stats->limit(static::PAGE_SIZE)
->offset(static::PAGE_SIZE * ($page - 1))
Expand Down
11 changes: 9 additions & 2 deletions app/Http/Controllers/ScoresController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use App\Models\Solo\Score as SoloScore;
use App\Transformers\ScoreTransformer;
use App\Transformers\UserCompactTransformer;
use Illuminate\Auth\AuthenticationException;

class ScoresController extends Controller
{
Expand All @@ -22,6 +23,7 @@ public function __construct()

$this->middleware('auth', ['except' => [
'show',
'download',
]]);

$this->middleware('require-scopes:public');
Expand All @@ -42,6 +44,11 @@ private static function parseIdOrFail(string $id): int

public function download($rulesetOrSoloId, $id = null)
{
$currentUser = \Auth::user();
if (!is_api_request() && $currentUser === null) {
throw new AuthenticationException('User is not logged in.');
}

$shouldRedirect = !is_api_request() && !from_app_url();
if ($id === null) {
if ($shouldRedirect) {
Expand All @@ -68,9 +75,9 @@ public function download($rulesetOrSoloId, $id = null)
abort(404);
}

$currentUser = \Auth::user();
if (
!$currentUser->isRestricted()
$currentUser !== null
&& !$currentUser->isRestricted()
&& $currentUser->getKey() !== $score->user_id
&& ($currentUser->token()?->client->password_client ?? false)
) {
Expand Down
12 changes: 12 additions & 0 deletions app/Http/Controllers/SessionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

namespace App\Http\Controllers;

use App\Libraries\User\CountryChange;
use App\Libraries\User\DatadogLoginAttempt;
use App\Libraries\User\ForceReactivation;
use App\Models\Country;
use App\Models\User;
use App\Transformers\CurrentUserTransformer;
use Auth;
Expand Down Expand Up @@ -88,6 +90,16 @@ public function store()
return ujs_redirect(route('password-reset'));
}

// try fixing user country if it's currently set to unknown
if ($user->country_acronym === Country::UNKNOWN) {
try {
CountryChange::handle($user, request_country(), 'automated unknown country fixup on login');
} catch (\Throwable $e) {
// report failures but continue anyway
log_error($e);
}
}

DatadogLoginAttempt::log(null);
$this->login($user, $remember);

Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/UserCoverPresetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,6 @@ public function update(string $id): Response
$item->update(['active' => $params['active']]);
}

return ujs_redirect(route('user-cover-presets.index').'#cover-'.$item->getKey());
return response(null, 204);
}
}
24 changes: 22 additions & 2 deletions app/Models/Multiplayer/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ public function calculateMissingTopScores()

public function completePlay(ScoreToken $scoreToken, array $params): ScoreLink
{
priv_check_user($scoreToken->user, 'MultiplayerScoreSubmit')->ensureCan();
priv_check_user($scoreToken->user, 'MultiplayerScoreSubmit', $this)->ensureCan();

$this->assertValidCompletePlay();

Expand Down Expand Up @@ -622,9 +622,29 @@ public function startGame(User $host, array $rawParams, array $extraParams = [])
return $this->fresh();
}

/**
* @throws InvariantException
*/
public function endGame(User $requestingUser)
{
priv_check_user($requestingUser, 'MultiplayerRoomDestroy', $this)->ensureCan();

if ($this->isRealtime()) {
throw new InvariantException('Realtime rooms cannot be closed.');
}

$gracePeriodMinutes = $GLOBALS['cfg']['osu']['multiplayer']['room_close_grace_period_minutes'];
if ($this->starts_at->addMinutes($gracePeriodMinutes)->isPast()) {
throw new InvariantException('The grace period for closing this room has expired.');
}

$this->ends_at = now();
$this->save();
}

public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId)
{
priv_check_user($user, 'MultiplayerScoreSubmit')->ensureCan();
priv_check_user($user, 'MultiplayerScoreSubmit', $this)->ensureCan();

$this->assertValidStartPlay($user, $playlistItem);

Expand Down
53 changes: 33 additions & 20 deletions app/Singletons/OsuAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ public function checkChatChannelSend(?User $user, Channel $channel): string
{
$prefix = 'chat.';

$this->ensureLoggedIn($user);
$this->ensureSessionVerified($user);
$this->ensureCleanRecord($user, $prefix);
// This check becomes useless when min_plays_allow_verified_bypass is enabled.
Expand Down Expand Up @@ -1836,14 +1837,41 @@ public function checkMultiplayerRoomCreate(?User $user): string

/**
* @param User|null $user
* @param Room $room
* @return string
* @throws AuthorizationCheckException
*/
public function checkMultiplayerScoreSubmit(?User $user): string
public function checkMultiplayerRoomDestroy(?User $user, Room $room): string
{
$prefix = 'room.destroy.';

$this->ensureLoggedIn($user);
$this->ensureCleanRecord($user);

if ($room->user_id !== $user->getKey()) {
return $prefix.'not_owner';
}

return 'ok';
}

/**
* @param User|null $user
* @return string
* @throws AuthorizationCheckException
*/
public function checkMultiplayerScoreSubmit(?User $user, Room $room): string
{
$this->ensureLoggedIn($user);

if ($room->isRealtime()) {
$this->ensureCleanRecord($user);
} else {
if ($user->isRestricted()) {
throw new AuthorizationCheckException('restricted');
}
}

return 'ok';
}

Expand Down Expand Up @@ -2023,8 +2051,6 @@ public function checkWikiPageRefresh(?User $user): string
}

/**
* @param User|null $user
* @param string $prefix
* @throws AuthorizationCheckException
*/
public function ensureLoggedIn(?User $user, string $prefix = ''): void
Expand All @@ -2035,35 +2061,25 @@ public function ensureLoggedIn(?User $user, string $prefix = ''): void
}

/**
* @param User|null $user
* @param string $prefix
* @return string
* @throws AuthorizationCheckException
*/
public function ensureCleanRecord(?User $user, string $prefix = ''): string
private function ensureCleanRecord(User $user, string $prefix = ''): void
{
if ($user === null) {
return 'unauthorized';
}

if ($user->isRestricted()) {
throw new AuthorizationCheckException($prefix.'restricted');
}

if ($user->isSilenced()) {
throw new AuthorizationCheckException($prefix.'silenced');
}

return 'ok';
}

/**
* @param User|null $user
* @throws AuthorizationCheckException
*/
public function ensureHasPlayed(?User $user): void
private function ensureHasPlayed(User $user): void
{
if ($user === null || $user->isBot()) {
if ($user->isBot()) {
return;
}

Expand All @@ -2087,13 +2103,10 @@ public function ensureHasPlayed(?User $user): void
/**
* Ensure User is logged in and verified.
*
* @param User|null $user
* @throws AuthorizationCheckException
*/
public function ensureSessionVerified(?User $user)
private function ensureSessionVerified(User $user)
{
$this->ensureLoggedIn($user);

if (!$user->isSessionVerified()) {
throw new AuthorizationCheckException('require_verification');
}
Expand Down
1 change: 1 addition & 0 deletions config/osu.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
],
'multiplayer' => [
'max_attempts_limit' => get_int(env('MULTIPLAYER_MAX_ATTEMPTS_LIMIT')) ?? 128,
'room_close_grace_period_minutes' => get_int(env('MULTIPLAYER_ROOM_CLOSE_GRACE_PERIOD_MINUTES')) ?? 5,
],
'notification' => [
'endpoint' => presence(env('NOTIFICATION_ENDPOINT'), '/home/notifications/feed'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// raw statement used as laravel syntax doesn't support a fixed size BINARY column
DB::statement('CREATE TABLE `beatmapset_files` (
`file_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`sha2_hash` BINARY(32) NOT NULL,
`file_size` INT UNSIGNED NOT NULL,
UNIQUE (`sha2_hash`)
)');

Schema::create('beatmapset_versions', function (Blueprint $table) {
$table->bigIncrements('version_id');
$table->mediumInteger('beatmapset_id')->unsigned();
$table->timestamp('created_at')->useCurrent();
$table->bigInteger('previous_version_id')->unsigned()->nullable();

$table->index('beatmapset_id');
$table->index('previous_version_id');
});

Schema::create('beatmapset_version_files', function (Blueprint $table) {
$table->bigInteger('file_id')->unsigned();
$table->bigInteger('version_id')->unsigned();
$table->string('filename', length: 500);

$table->primary(['file_id', 'version_id']);
$table->index('file_id');
$table->index('version_id');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('beatmapset_version_files');
Schema::dropIfExists('beatmapset_versions');
Schema::dropIfExists('beatmapset_files');
}
};
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
x-env: &x-env
APP_KEY: "${APP_KEY}"
BEATMAPS_DIFFICULTY_CACHE_SERVER_URL: http://beatmap-difficulty-lookup-cache
BEATMAPS_DIFFICULTY_CACHE_SERVER_URL: http://beatmap-difficulty-lookup-cache:8080
BROADCAST_DRIVER: redis
DB_CONNECTION_STRING: Server=db;Database=osu;Uid=osuweb;
DB_HOST: db
Expand Down
6 changes: 6 additions & 0 deletions resources/js/setup-turbo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import '@hotwired/turbo';
import { hideLoadingOverlay, showLoadingOverlay } from 'utils/loading-overlay';
import { reloadPage } from 'utils/turbolinks';

Turbo.config.drive.progressBarDelay = 0;

Expand All @@ -17,6 +18,11 @@ document.addEventListener('turbo:submit-start', (e) => {
}
});
document.addEventListener('turbo:submit-end', hideLoadingOverlay);
document.addEventListener('turbo:submit-end', (e) => {
if (e.detail.success && e.detail.formSubmission.formElement.dataset.reloadOnSuccess === '1') {
reloadPage();
}
});

// disable turbo navigation for old webs
document.addEventListener('turbo:click', (event) => {
Expand Down
6 changes: 6 additions & 0 deletions resources/lang/en/authorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@
],
],

'room' => [
'destroy' => [
'not_owner' => 'Only room owner can close it.',
],
],

'score' => [
'pin' => [
'disabled_type' => "Can't pin this type of score",
Expand Down
7 changes: 3 additions & 4 deletions resources/views/layout/ujs-redirect.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
See the LICENCE file in the repository root for full licence text.
--}}
;(function() {
$(document).off(".ujsHideLoadingOverlay")
Turbo.visit({!! json_encode($url) !!})
}).call(this);
$(document).off('.ujsHideLoadingOverlay');
Turbo.cache.clear();
Turbo.visit({!! json_encode($url) !!});
Loading

0 comments on commit 604eff0

Please sign in to comment.