AccountInterstitial::class, 'App\DirectMessage' => DirectMessage::class, 'App\Follower' => Follower::class, 'App\Like' => Like::class, 'App\Media' => Media::class, 'App\MediaTag' => MediaTag::class, 'App\Notification' => Notification::class, 'App\Profile' => Profile::class, 'App\Report' => Report::class, 'App\Status' => Status::class, 'App\Story' => Story::class, 'App\User' => User::class, 'App\UserFilter' => UserFilter::class, ]); Horizon::auth(function ($request) { return Auth::check() && $request->user()->is_admin; }); Validator::includeUnvalidatedArrayKeys(); Gate::policy(CustomFilter::class, CustomFilterPolicy::class); Event::listen(Login::class, AuthLogin::class); Event::listen(Failed::class, LogFailedLogin::class); Gate::define('viewPulse', function (User $user) { // is_admin is cast to bool on the User model, so a strict `=== 1` // never matches. Use a boolean check, consistent with viewHorizon. return (bool) $user->is_admin === true; }); if (config('pulse.enabled', false)) { Pulse::user(function ($user) { $acct = AccountService::get($user->profile_id, true); return $acct ? [ 'name' => $acct['username'], 'extra' => $user->email, 'avatar' => $acct['avatar'], ] : [ 'name' => $user->username, 'extra' => 'DELETED', 'avatar' => '/storage/avatars/default.jpg', ]; }); } RateLimiter::for('api', function (Request $request) { return Limit::perMinute(512)->by($request->user()?->id ?: $request->ip()); }); RateLimiter::for('app-signup', function (Request $request) { return Limit::perDay(100)->by($request->ip()); }); RateLimiter::for('app-code-verify', function (Request $request) { $email = strtolower(trim((string) $request->input('email'))); $emailKey = $email !== '' ? hash('sha256', $email) : 'missing'; return [ Limit::perHour(20)->by('app-code-verify:ip:'.$request->ip()), Limit::perHour(10)->by('app-code-verify:email:'.$emailKey), ]; }); RateLimiter::for('app-code-resend', function (Request $request) { return Limit::perHour(10)->by($request->ip()); }); RateLimiter::for('account-lookup', function (Request $request) { return Limit::perDay(50)->by($request->ip()); }); RateLimiter::for('oauth-pat', function (Request $request) { $user = $request->user('web'); $actor = $user ? 'u:'.$user->getAuthIdentifier() : 'ip:'.$request->ip(); $tooMany = function (Request $request, array $headers) { return response()->json([ 'message' => 'Too many requests', 'retry_after' => isset($headers['Retry-After']) ? (int) $headers['Retry-After'] : null, 'debug' => 'oauth-pat limiter hit', 'headers' => $headers, ], 429)->withHeaders($headers)->header('X-Debug-Limiter', 'oauth-pat'); }; return [ Limit::perMinute(3) ->by("minute:{$actor}"), Limit::perHour(15) ->by("hour:{$actor}"), Limit::perDay(20) ->by("day:{$actor}"), ]; }); Passport::useTokenModel(OAuthToken::class); Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356))); Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400))); Passport::tokensCan([ 'read' => 'Full read access to your account', 'write' => 'Full write access to your account', 'follow' => 'Ability to follow other profiles', 'admin:read' => 'Read all data on the server', 'admin:read:domain_blocks' => 'Read sensitive information of all domain blocks', 'admin:write' => 'Modify all data on the server', 'admin:write:domain_blocks' => 'Perform moderation actions on domain blocks', 'push' => 'Receive your push notifications', 'security:read' => 'See which apps and devices have access to your account', 'security:write' => 'Change your password and revoke access for other apps and devices', ]); Passport::defaultScopes([ 'read', 'write', 'follow', 'push', ]); URL::forceRootUrl(config('app.url')); // Enable strict testing in dev/test only (false in production) // Model::preventLazyLoading(! $this->app->isProduction()); // Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); // Model::preventAccessingMissingAttributes(! $this->app->isProduction()); } /** * Register any application services. * * @return void */ public function register() { Passport::ignoreRoutes(); Pulse::ignoreRoutes(); $this->app->bind(UserOidcService::class, function () { return UserOidcService::build(); }); // Swap the translation loader so empty (untranslated) strings are // dropped at load time. This lets Laravel fall back to the fallback // locale for partially-translated locales instead of rendering blanks. $this->app->extend('translation.loader', function ($loader, $app) { return new EmptyStrippingFileLoader( $app['files'], $app['path.lang'] ); }); } }