Ctrl K
ComunidadGuías…

app/Providers/RouteServiceProvider.php

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php

namespace App\Providers;

use Artwork\Modules\Project\Models\Project;
use Dedoc\Scramble\Scramble;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Laravel\Passport\AccessToken;

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';

    public function register(): void
    {
        parent::register();

        if (class_exists(Scramble::class)) {
            Scramble::ignoreDefaultRoutes();
        }
    }

    public function boot(): void
    {
        $this->configureRateLimiting();

        if (config('app.force_https')) {
            \Illuminate\Support\Facades\URL::forceScheme('https');
        }

        // Links und Weiterleitungen nie aus dem Host-Header bauen: sonst landen in synchron
        // versandten Mails (z. B. Login-Links) Adressen eines fremden Hosts. Eine lokale APP_URL
        // (häufig unverändert aus der .env.example) würde dagegen jede Weiterleitung kaputt machen.
        if ($this->app->isProduction() && $this->hasPublicAppUrl()) {
            \Illuminate\Support\Facades\URL::forceRootUrl(config('app.url'));
        }

        $this->routes(function (): void {
            // Vor routes/api.php registriert, damit die versionierten Pfade zuerst greifen.
            Route::prefix('api/v1')
                ->middleware('api.machine')
                ->group(base_path('routes/api_v1.php'));

            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));

            // External-access routes carry their own middleware groups (declared per-group in the files).
            Route::group([], base_path('routes/external-guest.php'));
            Route::group([], base_path('routes/external.php'));

            Route::prefix('api/app/v1')
                ->middleware('api.app')
                ->name('app.v1.')
                ->group(base_path('routes/app-api.php'));
        });

        Route::bind('projects', function ($value) {
            return Project::withTrashed()->whereKey($value)->firstOrFail();
        });
    }

    protected function configureRateLimiting(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        // Login-Discovery der App: fragt jede bekannte Instanz nach einem E-Mail-Hash. Das
        // Limit bremst das Abklopfen fremder Adressen, normale Logins brauchen eine Anfrage.
        RateLimiter::for('app-discover', function (Request $request) {
            // Die App fragt pro Anmeldung einmal; die Stundengrenze bremst das Durchprobieren von Adressen.
            return [
                Limit::perMinute(10)->by('app-discover:ip:' . $request->ip()),
                Limit::perHour(60)->by('app-discover:ip-hour:' . $request->ip()),
            ];
        });

        RateLimiter::for('app-login', function (Request $request) {
            // Läuft vor der Request-Validierung: Bots posten "email" auch als Array (sonst 500)
            $email = $request->input('email');
            $email = Str::lower(is_string($email) ? $email : '');
            $ip = (string) $request->ip();

            return [
                Limit::perMinute(10)->by('app-login:ip:' . $ip),
                Limit::perMinute(5)->by('app-login:identity:' . hash('sha256', $email . '|' . $ip)),
                // Pro Konto unabhängig von der IP (wie der Web-Login): bremst Password-Spraying und das
                // Durchprobieren von 2FA-Codes über viele Adressen; höchstens 15 Minuten Wartezeit.
                Limit::perMinutes(15, 10)->by('app-login:account:' . hash('sha256', trim($email))),
            ];
        });

        RateLimiter::for('api-token', function (Request $request) {
            $token = $request->user()?->token();
            $key = $token instanceof AccessToken
                ? $token->oauth_access_token_id
                : $request->ip();

            return Limit::perMinute(120)->by($key);
        });
    }

    /**
     * Nur Loopback-/Entwicklungsadressen gelten als „nicht gesetzt“; Intranet-Namen und -IPs sind legitim.
     */
    private function hasPublicAppUrl(): bool
    {
        $host = strtolower((string) parse_url((string) config('app.url'), PHP_URL_HOST));

        return $host !== ''
            && !in_array($host, ['localhost', '127.0.0.1', '::1', '[::1]'], true)
            && !str_ends_with($host, '.localhost')
            && !str_ends_with($host, '.test');
    }
}