From a36f33dfa75b36552a11347ce209161b4bc18d9c Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Thu, 18 Apr 2024 16:08:36 +0200 Subject: [PATCH] Restricted data sent by jestream; Fixed profile picture --- app/Http/Kernel.php | 1 + app/Http/Middleware/ShareInertiaData.php | 103 ++++++++++++++++++++ app/Models/Organization.php | 4 + app/Models/User.php | 26 ++++- database/factories/ProjectMemberFactory.php | 2 +- 5 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 app/Http/Middleware/ShareInertiaData.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 818ffc02..783f5125 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -40,6 +40,7 @@ class Kernel extends HttpKernel \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\HandleInertiaRequests::class, + \App\Http\Middleware\ShareInertiaData::class, \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class, \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], diff --git a/app/Http/Middleware/ShareInertiaData.php b/app/Http/Middleware/ShareInertiaData.php new file mode 100644 index 00000000..39907fdc --- /dev/null +++ b/app/Http/Middleware/ShareInertiaData.php @@ -0,0 +1,103 @@ + function () use ($request) { + /** @var User|null $user */ + $user = $request->user(); + + return [ + 'canCreateTeams' => $user !== null && + Jetstream::userHasTeamFeatures($user) && + Gate::forUser($user)->check('create', Jetstream::newTeamModel()), + 'canManageTwoFactorAuthentication' => Features::canManageTwoFactorAuthentication(), + 'canUpdatePassword' => Features::enabled(Features::updatePasswords()), + 'canUpdateProfileInformation' => Features::canUpdateProfileInformation(), + 'hasEmailVerification' => Features::enabled(Features::emailVerification()), + 'flash' => $request->session()->get('flash', []), + 'hasAccountDeletionFeatures' => Jetstream::hasAccountDeletionFeatures(), + 'hasApiFeatures' => Jetstream::hasApiFeatures(), + 'hasTeamFeatures' => Jetstream::hasTeamFeatures(), + 'hasTermsAndPrivacyPolicyFeature' => Jetstream::hasTermsAndPrivacyPolicyFeature(), + 'managesProfilePhotos' => Jetstream::managesProfilePhotos(), + ]; + }, + 'auth' => [ + 'user' => function () use ($request): array { + /** @var User|null $user */ + $user = $request->user(); + + if ($user === null) { + return []; + } + + return array_merge([ + 'id' => $user->id, + 'name' => $user->name, + 'email' => $user->email, + 'email_verified_at' => $user->email_verified_at, + 'current_team_id' => $user->current_team_id, + 'profile_photo_path' => $user->profile_photo_path, + 'timezone' => $user->timezone, + 'week_start' => $user->week_start, + 'profile_photo_url' => $user->profile_photo_url, + 'two_factor_enabled' => Features::enabled(Features::twoFactorAuthentication()) + && ! is_null($user->two_factor_secret), + 'current_team' => $user->currentTeam !== null ? [ + 'id' => $user->currentTeam->id, + 'user_id' => $user->currentTeam->user_id, + 'name' => $user->currentTeam->name, + 'personal_team' => $user->currentTeam->personal_team, + 'currency' => $user->currentTeam->currency, + ] : null, + ], array_filter([ + 'all_teams' => $user->organizations->map(function (Organization $organization): array { + return [ + 'id' => $organization->id, + 'name' => $organization->name, + 'personal_team' => $organization->personal_team, + 'currency' => $organization->currency, + 'membership' => [ + 'role' => $organization->membership->role, + ], + ]; + })->all(), + ])); + }, + ], + 'errorBags' => function () { + /** @var array|null $bags */ + $bags = Session::get('errors')?->getBags(); + $bagsCollection = collect($bags ?: []); + + return $bagsCollection->mapWithKeys(function (MessageBag $bag, string $key) { + return [$key => $bag->messages()]; + })->all(); + }, + ])); + + return $next($request); + } +} diff --git a/app/Models/Organization.php b/app/Models/Organization.php index d82712c5..2297a0f0 100644 --- a/app/Models/Organization.php +++ b/app/Models/Organization.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Carbon; use Laravel\Jetstream\Events\TeamCreated; use Laravel\Jetstream\Events\TeamDeleted; use Laravel\Jetstream\Events\TeamUpdated; @@ -24,8 +25,11 @@ use Laravel\Jetstream\Team as JetstreamTeam; * @property int|null $billable_rate * @property string $user_id * @property User $owner + * @property Carbon|null $created_at + * @property Carbon|null $updated_at * @property Collection $users * @property Collection $realUsers + * @property Membership $membership * * @method HasMany teamInvitations() * @method static OrganizationFactory factory() diff --git a/app/Models/User.php b/app/Models/User.php index bba306a0..3453ad02 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,6 +10,7 @@ use Filament\Models\Contracts\FilamentUser; use Filament\Panel; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -17,6 +18,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Storage; use Laravel\Fortify\TwoFactorAuthenticatable; use Laravel\Jetstream\HasProfilePhoto; use Laravel\Jetstream\HasTeams; @@ -28,14 +31,19 @@ use Laravel\Passport\HasApiTokens; * @property string $email * @property string|null $email_verified_at * @property string|null $password + * @property string|null $two_factor_secret * @property string $timezone * @property bool $is_placeholder * @property Weekday $week_start * @property string|null $profile_photo_path * @property-read Organization $currentTeam * @property-read string $profile_photo_url - * @property Collection $organizations - * @property Collection $timeEntries + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property string $current_team_id + * @property Collection $organizations + * @property Collection $timeEntries + * @property Membership $membership * * @method HasMany ownedTeams() * @method static UserFactory factory() @@ -99,6 +107,20 @@ class User extends Authenticatable implements FilamentUser, MustVerifyEmail 'week_start' => Weekday::Monday, ]; + /** + * Get the URL to the user's profile photo. + * + * @return Attribute + */ + protected function profilePhotoUrl(): Attribute + { + return Attribute::get(function (): string { + return $this->profile_photo_path + ? Storage::disk($this->profilePhotoDisk())->url($this->profile_photo_path) + : $this->defaultProfilePhotoUrl(); + }); + } + public function canAccessPanel(Panel $panel): bool { return in_array($this->email, config('auth.super_admins', []), true) && $this->hasVerifiedEmail(); diff --git a/database/factories/ProjectMemberFactory.php b/database/factories/ProjectMemberFactory.php index a586fee7..23a66fa4 100644 --- a/database/factories/ProjectMemberFactory.php +++ b/database/factories/ProjectMemberFactory.php @@ -22,7 +22,7 @@ class ProjectMemberFactory extends Factory public function definition(): array { return [ - 'billable_rate' => $this->faker->numberBetween(50, 1000) * 100, + 'billable_rate' => $this->faker->numberBetween(10, 10000) * 100, 'project_id' => Project::factory(), 'user_id' => User::factory(), ];