Misc unrelated updates
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-12 10:31:31 +01:00
parent e9afbeb028
commit 3de1d3deab
40 changed files with 433 additions and 267 deletions

View File

@@ -40,7 +40,7 @@ class Login extends BaseLogin implements HasForms
}
// SuperAdmin-spezifisch: Prüfe auf SuperAdmin-Rolle, keine Tenant-Prüfung
if ($user->role !== 'super_admin') {
if (! $user->isSuperAdmin()) {
$authGuard->logout();
throw ValidationException::withMessages([

View File

@@ -45,11 +45,11 @@ class GuestPolicySettingsPage extends Page
public int $join_token_failure_decay_minutes = 5;
public int $join_token_access_limit = 120;
public int $join_token_access_limit = 300;
public int $join_token_access_decay_minutes = 1;
public int $join_token_download_limit = 60;
public int $join_token_download_limit = 120;
public int $join_token_download_decay_minutes = 1;
@@ -69,9 +69,9 @@ class GuestPolicySettingsPage extends Page
$this->per_device_upload_limit = (int) ($settings->per_device_upload_limit ?? 50);
$this->join_token_failure_limit = (int) ($settings->join_token_failure_limit ?? 10);
$this->join_token_failure_decay_minutes = (int) ($settings->join_token_failure_decay_minutes ?? 5);
$this->join_token_access_limit = (int) ($settings->join_token_access_limit ?? 120);
$this->join_token_access_limit = (int) ($settings->join_token_access_limit ?? 300);
$this->join_token_access_decay_minutes = (int) ($settings->join_token_access_decay_minutes ?? 1);
$this->join_token_download_limit = (int) ($settings->join_token_download_limit ?? 60);
$this->join_token_download_limit = (int) ($settings->join_token_download_limit ?? 120);
$this->join_token_download_decay_minutes = (int) ($settings->join_token_download_decay_minutes ?? 1);
$this->join_token_ttl_hours = (int) ($settings->join_token_ttl_hours ?? 168);
$this->share_link_ttl_hours = (int) ($settings->share_link_ttl_hours ?? 48);

View File

@@ -16,6 +16,7 @@ use App\Models\Package;
use App\Models\PackagePurchase;
use App\Models\Photo;
use App\Models\Tenant;
use App\Models\User;
use App\Services\EventJoinTokenService;
use App\Support\ApiError;
use Illuminate\Http\JsonResponse;
@@ -88,12 +89,15 @@ class EventController extends Controller
$tenant = Tenant::findOrFail($tenantId);
}
$actor = $request->user();
$isSuperAdmin = $actor instanceof User && $actor->isSuperAdmin();
// Package check is now handled by middleware
$validated = $request->validated();
$tenantId = $tenant->id;
$requestedPackageId = $validated['package_id'] ?? null;
$requestedPackageId = $isSuperAdmin ? $request->integer('package_id') : null;
unset($validated['package_id']);
$tenantPackage = $tenant->tenantPackages()
@@ -108,6 +112,10 @@ class EventController extends Controller
$package = Package::query()->find($requestedPackageId);
}
if (! $package && $isSuperAdmin) {
$package = $this->resolveOwnerPackage();
}
if (! $package && $tenantPackage) {
$package = $tenantPackage->package ?? Package::query()->find($tenantPackage->package_id);
}
@@ -121,7 +129,7 @@ class EventController extends Controller
$requiresWaiver = $package->isEndcustomer();
$latestPurchase = $requiresWaiver ? $this->resolveLatestPackagePurchase($tenant, $package) : null;
$existingWaiver = $latestPurchase ? data_get($latestPurchase->metadata, 'consents.digital_content_waiver_at') : null;
$needsWaiver = $requiresWaiver && ! $existingWaiver;
$needsWaiver = ! $isSuperAdmin && $requiresWaiver && ! $existingWaiver;
if ($needsWaiver && ! $request->boolean('accepted_waiver')) {
throw ValidationException::withMessages([
@@ -182,7 +190,7 @@ class EventController extends Controller
$eventData = Arr::only($eventData, $allowed);
$event = DB::transaction(function () use ($tenant, $eventData, $package) {
$event = DB::transaction(function () use ($tenant, $eventData, $package, $isSuperAdmin) {
$event = Event::create($eventData);
EventPackage::create([
@@ -193,7 +201,7 @@ class EventController extends Controller
'gallery_expires_at' => $package->gallery_days ? now()->addDays($package->gallery_days) : null,
]);
if ($package->isReseller()) {
if ($package->isReseller() && ! $isSuperAdmin) {
$note = sprintf('Event #%d created (%s)', $event->id, $event->name);
if (! $tenant->consumeEventAllowance(1, 'event.create', $note)) {
@@ -229,6 +237,15 @@ class EventController extends Controller
->first();
}
private function resolveOwnerPackage(): ?Package
{
$ownerPackage = Package::query()
->where('slug', 'pro')
->first();
return $ownerPackage ?? Package::query()->find(3);
}
private function recordEventStartWaiver(Tenant $tenant, Package $package, ?PackagePurchase $purchase): void
{
$timestamp = now();

View File

@@ -135,7 +135,7 @@ class EventMemberController extends Controller
$user->password = Hash::make(Str::random(32));
}
if ($user->tenant_id && (int) $user->tenant_id !== (int) $tenant->id && $user->role !== 'super_admin') {
if ($user->tenant_id && (int) $user->tenant_id !== (int) $tenant->id && ! $user->isSuperAdmin()) {
throw ValidationException::withMessages([
'email' => __('Dieser Benutzer ist einem anderen Mandanten zugeordnet.'),
]);
@@ -143,9 +143,9 @@ class EventMemberController extends Controller
$user->tenant_id = $tenant->id;
if ($role === 'tenant_admin' && $user->role !== 'super_admin') {
if ($role === 'tenant_admin' && ! $user->isSuperAdmin()) {
$user->role = 'tenant_admin';
} elseif (! in_array($user->role, ['tenant_admin', 'super_admin'], true)) {
} elseif (! in_array($user->role, ['tenant_admin', 'super_admin', 'superadmin'], true)) {
$user->role = 'member';
}

View File

@@ -193,11 +193,11 @@ class TenantAdminTokenController extends Controller
$abilities[] = 'tenant:'.$user->tenant_id;
}
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin'], true)) {
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'superadmin'], true)) {
$abilities[] = 'tenant-admin';
}
if ($user->role === 'super_admin') {
if ($user->isSuperAdmin()) {
$abilities[] = 'super-admin';
}
@@ -219,7 +219,7 @@ class TenantAdminTokenController extends Controller
private function ensureUserCanAccessPanel(User $user): void
{
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin'], true)) {
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'superadmin'], true)) {
return;
}

View File

@@ -9,8 +9,8 @@ use App\Models\User;
use App\Notifications\TenantFeedbackSubmitted;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Notification;
use Illuminate\Validation\Rule;
class TenantFeedbackController extends Controller
{
@@ -56,7 +56,7 @@ class TenantFeedbackController extends Controller
]);
$recipients = User::query()
->where('role', 'super_admin')
->whereIn('role', ['super_admin', 'superadmin'])
->whereNotNull('email')
->get();

View File

@@ -80,7 +80,7 @@ class TenantAdminPasswordResetController extends Controller
private function canAccessEventAdmin(User $user): bool
{
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin'], true)) {
if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'superadmin'], true)) {
return true;
}

View File

@@ -155,7 +155,7 @@ class AuthenticatedSessionController extends Controller
}
// Super admins go to Filament superadmin panel
if ($user && $user->role === 'super_admin') {
if ($user && $user->isSuperAdmin()) {
return '/super-admin';
}

View File

@@ -12,7 +12,7 @@ class TenantAdminAuthController extends Controller
$user = Auth::user();
// Allow only tenant_admin and super_admin
if ($user && in_array($user->role, ['tenant_admin', 'super_admin'])) {
if ($user && in_array($user->role, ['tenant_admin', 'super_admin', 'superadmin'], true)) {
return view('admin');
}

View File

@@ -46,7 +46,7 @@ class TenantAdminGoogleController extends Controller
/** @var User|null $user */
$user = User::query()->where('email', $email)->first();
if (! $user || ! in_array($user->role, ['tenant_admin', 'super_admin'], true)) {
if (! $user || ! in_array($user->role, ['tenant_admin', 'super_admin', 'superadmin'], true)) {
return $this->sendBackWithError($request, 'google_no_match', 'No tenant admin account is linked to this Google address.');
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Middleware;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Packages\PackageLimitEvaluator;
use App\Support\ApiError;
use Closure;
@@ -26,7 +27,7 @@ class CreditCheckMiddleware
]);
}
if ($this->requiresCredits($request)) {
if ($this->requiresCredits($request) && ! $this->shouldBypassCreditCheck($request, $tenant)) {
$violation = $this->limitEvaluator->assessEventCreation($tenant);
if ($violation !== null) {
@@ -43,6 +44,24 @@ class CreditCheckMiddleware
return $next($request);
}
private function shouldBypassCreditCheck(Request $request, Tenant $tenant): bool
{
$user = $request->user();
if (! $user instanceof User) {
return false;
}
if (! $user->isSuperAdmin()) {
return false;
}
if (! $user->tenant_id) {
return false;
}
return (int) $user->tenant_id === (int) $tenant->id;
}
private function requiresCredits(Request $request): bool
{
return $request->isMethod('post')

View File

@@ -42,7 +42,7 @@ class EnsureTenantAdminToken
/** @var Tenant|null $tenant */
$tenant = $user->tenant;
if (! $tenant && $user->role === 'super_admin') {
if (! $tenant && $user->isSuperAdmin()) {
$requestedTenantId = $this->resolveRequestedTenantId($request);
if ($requestedTenantId !== null) {
@@ -50,14 +50,14 @@ class EnsureTenantAdminToken
}
}
if (! $tenant && $user->role !== 'super_admin') {
if (! $tenant && ! $user->isSuperAdmin()) {
return $this->forbiddenResponse('Tenant context missing for user.');
}
if ($tenant) {
$request->attributes->set('tenant_id', $tenant->id);
$request->attributes->set('tenant', $tenant);
} elseif ($user->role === 'super_admin') {
} elseif ($user->isSuperAdmin()) {
$requestedTenantId = $this->resolveRequestedTenantId($request);
if ($requestedTenantId !== null) {
$request->attributes->set('tenant_id', $requestedTenantId);
@@ -96,7 +96,7 @@ class EnsureTenantAdminToken
*/
protected function allowedRoles(): array
{
return ['tenant_admin', 'super_admin', 'admin'];
return ['tenant_admin', 'super_admin', 'superadmin', 'admin'];
}
protected function forbiddenRoleMessage(): string

View File

@@ -9,7 +9,7 @@ class EnsureTenantCollaboratorToken extends EnsureTenantAdminToken
{
protected function allowedRoles(): array
{
return ['tenant_admin', 'super_admin', 'admin', 'member'];
return ['tenant_admin', 'super_admin', 'superadmin', 'admin', 'member'];
}
protected function forbiddenRoleMessage(): string

View File

@@ -3,6 +3,7 @@
namespace App\Http\Middleware;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Packages\PackageLimitEvaluator;
use App\Support\ApiError;
use Closure;
@@ -26,7 +27,7 @@ class PackageMiddleware
]);
}
if ($this->requiresPackageCheck($request)) {
if ($this->requiresPackageCheck($request) && ! $this->shouldBypassPackageCheck($request, $tenant)) {
$violation = $this->detectViolation($request, $tenant);
if ($violation !== null) {
@@ -43,6 +44,24 @@ class PackageMiddleware
return $next($request);
}
private function shouldBypassPackageCheck(Request $request, Tenant $tenant): bool
{
$user = $request->user();
if (! $user instanceof User) {
return false;
}
if (! $user->isSuperAdmin()) {
return false;
}
if (! $user->tenant_id) {
return false;
}
return (int) $user->tenant_id === (int) $tenant->id;
}
private function requiresPackageCheck(Request $request): bool
{
return $request->isMethod('post') && (

View File

@@ -112,7 +112,7 @@ class RedirectIfAuthenticated extends BaseMiddleware
return '/event-admin/dashboard';
}
if ($user && $user->role === 'super_admin') {
if ($user && $user->isSuperAdmin()) {
return '/super-admin';
}

View File

@@ -4,9 +4,9 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
class SuperAdminAuth
{
@@ -21,17 +21,17 @@ class SuperAdminAuth
return $next($request);
}
if (!Auth::check()) {
if (! Auth::check()) {
abort(403, 'Nicht angemeldet.');
}
$user = Auth::user();
Log::info('SuperAdminAuth: User ID ' . $user->id . ', role: ' . $user->role);
Log::info('SuperAdminAuth: User ID '.$user->id.', role: '.$user->role);
if ($user->role !== 'super_admin') {
abort(403, 'Zugriff nur für SuperAdmin. User ID: ' . $user->id . ', Role: ' . $user->role);
if (! $user->isSuperAdmin()) {
abort(403, 'Zugriff nur für SuperAdmin. User ID: '.$user->id.', Role: '.$user->role);
}
return $next($request);
}
}
}

View File

@@ -30,6 +30,7 @@ class EventStoreRequest extends FormRequest
'event_date' => ['required', 'date', 'after_or_equal:today'],
'location' => ['nullable', 'string', 'max:255'],
'event_type_id' => ['required', 'exists:event_types,id'],
'package_id' => ['nullable', 'integer', 'exists:packages,id'],
'max_participants' => ['nullable', 'integer', 'min:1', 'max:10000'],
'public_url' => ['nullable', 'url', 'max:500'],
'custom_domain' => ['nullable', 'string', 'max:255'],

View File

@@ -41,9 +41,9 @@ class GuestPolicySetting extends Model
'per_device_upload_limit' => 50,
'join_token_failure_limit' => (int) config('join_tokens.failure_limit', 10),
'join_token_failure_decay_minutes' => (int) config('join_tokens.failure_decay_minutes', 5),
'join_token_access_limit' => (int) config('join_tokens.access_limit', 120),
'join_token_access_limit' => (int) config('join_tokens.access_limit', 300),
'join_token_access_decay_minutes' => (int) config('join_tokens.access_decay_minutes', 1),
'join_token_download_limit' => (int) config('join_tokens.download_limit', 60),
'join_token_download_limit' => (int) config('join_tokens.download_limit', 120),
'join_token_download_decay_minutes' => (int) config('join_tokens.download_decay_minutes', 1),
'join_token_ttl_hours' => 168,
'share_link_ttl_hours' => (int) config('share-links.ttl_hours', 48),

View File

@@ -69,6 +69,16 @@ class User extends Authenticatable implements FilamentHasTenants, FilamentUser,
];
}
public function isSuperAdmin(): bool
{
return self::isSuperAdminRole($this->role);
}
public static function isSuperAdminRole(?string $role): bool
{
return in_array($role, ['super_admin', 'superadmin'], true);
}
/**
* Retrieve the user by the given credentials.
*/
@@ -127,12 +137,12 @@ class User extends Authenticatable implements FilamentHasTenants, FilamentUser,
public function canAccessPanel(Panel $panel): bool
{
if (! $this->email_verified_at && $this->role !== 'super_admin') {
if (! $this->email_verified_at && ! $this->isSuperAdmin()) {
return false;
}
return match ($panel->getId()) {
'superadmin' => $this->role === 'super_admin',
'superadmin' => $this->isSuperAdmin(),
'admin' => $this->role === 'tenant_admin',
default => false,
};
@@ -140,7 +150,7 @@ class User extends Authenticatable implements FilamentHasTenants, FilamentUser,
public function canAccessTenant(Model $tenant): bool
{
if ($this->role === 'super_admin') {
if ($this->isSuperAdmin()) {
return true;
}
@@ -155,7 +165,7 @@ class User extends Authenticatable implements FilamentHasTenants, FilamentUser,
public function getTenants(Panel $panel): array|Collection
{
if ($this->role === 'super_admin') {
if ($this->isSuperAdmin()) {
return Tenant::query()->orderBy('name')->get();
}

View File

@@ -12,12 +12,11 @@ class PurchaseHistoryPolicy
public function viewAny(User $user): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
public function view(User $user, PurchaseHistory $purchaseHistory): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
}

View File

@@ -15,7 +15,7 @@ class TenantPolicy
*/
public function viewAny(User $user): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
/**
@@ -35,7 +35,7 @@ class TenantPolicy
*/
public function create(User $user): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
/**
@@ -43,7 +43,7 @@ class TenantPolicy
*/
public function update(User $user, Tenant $tenant): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
/**
@@ -51,7 +51,7 @@ class TenantPolicy
*/
public function delete(User $user, Tenant $tenant): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
/**
@@ -59,6 +59,6 @@ class TenantPolicy
*/
public function suspend(User $user, Tenant $tenant): bool
{
return $user->role === 'super_admin';
return $user->isSuperAdmin();
}
}

View File

@@ -155,7 +155,11 @@ class AppServiceProvider extends ServiceProvider
$key = $tenantId ? 'tenant:'.$tenantId : ('ip:'.($request->ip() ?? 'unknown'));
return Limit::perMinute(100)->by($key);
return Limit::perMinute(600)->by($key);
});
RateLimiter::for('guest-api', function (Request $request) {
return Limit::perMinute(300)->by('guest-api:'.($request->ip() ?? 'unknown'));
});
RateLimiter::for('tenant-auth', function (Request $request) {

View File

@@ -46,7 +46,7 @@ class AuthServiceProvider extends ServiceProvider
});
Gate::before(function (User $user): ?bool {
return $user->role === 'super_admin' ? true : null;
return $user->isSuperAdmin() ? true : null;
});
}
}

View File

@@ -83,7 +83,7 @@ class SuperAdminAuditLogger
private function shouldLog(?User $actor): bool
{
if (! $actor || $actor->role !== 'super_admin') {
if (! $actor || ! $actor->isSuperAdmin()) {
return false;
}

View File

@@ -24,15 +24,15 @@ class TenantAuth
}
$user = $request->user();
if ($user && in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'member'], true)) {
if ($user->role !== 'super_admin' || (int) $user->tenant_id === (int) $tenantId) {
if ($user && in_array($user->role, ['tenant_admin', 'admin', 'super_admin', 'superadmin', 'member'], true)) {
if (! $user->isSuperAdmin() || (int) $user->tenant_id === (int) $tenantId) {
return $user;
}
}
$user = User::query()
->where('tenant_id', $tenantId)
->whereIn('role', ['tenant_admin', 'admin', 'member'])
->whereIn('role', ['tenant_admin', 'admin', 'super_admin', 'superadmin', 'member'])
->orderByDesc('email_verified_at')
->orderBy('id')
->first();