diff --git a/app/Models/User.php b/app/Models/User.php index 68907bb563..f4492f9adc 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,9 +2,12 @@ namespace App\Models; +use App\Exceptions\ModelDBException; +use App\Facades\AccessControl; use App\Models\Extensions\ThrowsConsistentExceptions; use App\Models\Extensions\UseFixedQueryBuilder; use App\Models\Extensions\UTCBasedTimes; +use Carbon\Exceptions\InvalidFormatException; use DarkGhostHunter\Larapass\Contracts\WebAuthnAuthenticatable; use DarkGhostHunter\Larapass\WebAuthnAuthentication; use Illuminate\Database\Eloquent\Collection; @@ -31,13 +34,16 @@ * @property Collection $albums * @property DatabaseNotificationCollection|DatabaseNotification[] $notifications * @property Collection $shared + * @property Collection $photos */ class User extends Authenticatable implements WebAuthnAuthenticatable { use Notifiable; use WebAuthnAuthentication; use UTCBasedTimes; - use ThrowsConsistentExceptions; + use ThrowsConsistentExceptions { + delete as parentDelete; + } use UseFixedQueryBuilder; /** @@ -77,6 +83,16 @@ public function albums(): HasMany return $this->hasMany('App\Models\BaseAlbumImpl', 'owner_id', 'id'); } + /** + * Return the photos owned by the user. + * + * @return HasMany + */ + public function photos(): HasMany + { + return $this->hasMany('App\Models\Photo', 'owner_id', 'id'); + } + /** * Return the albums shared to the user. * @@ -108,4 +124,38 @@ public function name(): string { return ($this->id == 0) ? 'Admin' : $this->username; } + + /** + * Deletes a user from the DB and re-assigns ownership of albums and photos + * to the currently authenticated user. + * + * For efficiency reasons the methods performs a mass-update without + * hydrating the actual models. + * + * @return bool always true + * + * @throws ModelDBException + * @throws InvalidFormatException + */ + public function delete(): bool + { + $now = Carbon::now(); + $newOwnerID = AccessControl::id(); + + /** @var HasMany[] $ownershipRelations */ + $ownershipRelations = [$this->photos(), $this->albums()]; + + foreach ($ownershipRelations as $relation) { + // We must also update the `updated_at` column of the related + // models in case clients have cached these models. + $relation->update([ + $relation->getForeignKeyName() => $newOwnerID, + $relation->getRelated()->getUpdatedAtColumn() => $relation->getRelated()->fromDateTime($now), + ]); + } + + $this->shared()->delete(); + + return $this->parentDelete(); + } } diff --git a/database/migrations/2020_12_12_203153_migrate_admin_user.php b/database/migrations/2020_12_12_203153_migrate_admin_user.php index 47b606201b..a9679cc297 100644 --- a/database/migrations/2020_12_12_203153_migrate_admin_user.php +++ b/database/migrations/2020_12_12_203153_migrate_admin_user.php @@ -1,8 +1,11 @@ username = Configs::get_value('username', ''); $user->password = Configs::get_value('password', ''); $user->save(); - // user will have a id which is NOT 0. - // we want this user to have an ID of 0 as it is the ADMIN ID. + // User will have an ID which is NOT 0. + // We want this user to have an ID of 0 as it is the ADMIN ID. $user->id = 0; $user->save(); } @@ -28,14 +33,15 @@ public function up() * Reverse the migrations. * * @return void + * + * @throws InvalidArgumentException */ - public function down() + public function down(): void { if (Schema::hasTable('users')) { - $user = User::find(0); - if ($user != null) { - $user->delete(); - } + DB::table('users') + ->where('id', '=', 0) + ->delete(); } } }