Skip to content

Commit 0bfd799

Browse files
authored
Merge pull request #5844 from BookStackApp/user_ids
Updated handling of deleted user ID handling in DB
2 parents 4c7d642 + efff870 commit 0bfd799

24 files changed

+525
-56
lines changed

app/Access/Mfa/MfaValue.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BookStack\Users\Models\User;
66
use Carbon\Carbon;
7+
use Illuminate\Database\Eloquent\Factories\HasFactory;
78
use Illuminate\Database\Eloquent\Model;
89

910
/**
@@ -16,6 +17,8 @@
1617
*/
1718
class MfaValue extends Model
1819
{
20+
use HasFactory;
21+
1922
protected static $unguarded = true;
2023

2124
const METHOD_TOTP = 'totp';

app/Access/SocialAccount.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
use BookStack\Activity\Models\Loggable;
66
use BookStack\App\Model;
77
use BookStack\Users\Models\User;
8+
use Illuminate\Database\Eloquent\Factories\HasFactory;
9+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
810

911
/**
10-
* Class SocialAccount.
11-
*
1212
* @property string $driver
1313
* @property User $user
1414
*/
1515
class SocialAccount extends Model implements Loggable
1616
{
17-
protected $fillable = ['user_id', 'driver', 'driver_id', 'timestamps'];
17+
use HasFactory;
1818

19-
public function user()
19+
protected $fillable = ['user_id', 'driver', 'driver_id'];
20+
21+
/**
22+
* @return BelongsTo<User, $this>
23+
*/
24+
public function user(): BelongsTo
2025
{
2126
return $this->belongsTo(User::class);
2227
}

app/Activity/Models/Activity.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use BookStack\Entities\Models\Entity;
77
use BookStack\Permissions\Models\JointPermission;
88
use BookStack\Users\Models\User;
9+
use Illuminate\Database\Eloquent\Factories\HasFactory;
910
use Illuminate\Database\Eloquent\Relations\BelongsTo;
1011
use Illuminate\Database\Eloquent\Relations\HasMany;
1112
use Illuminate\Database\Eloquent\Relations\MorphTo;
@@ -24,6 +25,8 @@
2425
*/
2526
class Activity extends Model
2627
{
28+
use HasFactory;
29+
2730
/**
2831
* Get the loggable model related to this activity.
2932
* Currently only used for entities (previously entity_[id/type] columns).

app/Activity/Models/Favourite.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
use BookStack\App\Model;
66
use BookStack\Permissions\Models\JointPermission;
7+
use Illuminate\Database\Eloquent\Factories\HasFactory;
78
use Illuminate\Database\Eloquent\Relations\HasMany;
89
use Illuminate\Database\Eloquent\Relations\MorphTo;
910

1011
class Favourite extends Model
1112
{
13+
use HasFactory;
14+
1215
protected $fillable = ['user_id'];
1316

1417
/**

app/Activity/Models/Watch.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BookStack\Activity\WatchLevels;
66
use BookStack\Permissions\Models\JointPermission;
77
use Carbon\Carbon;
8+
use Illuminate\Database\Eloquent\Factories\HasFactory;
89
use Illuminate\Database\Eloquent\Model;
910
use Illuminate\Database\Eloquent\Relations\HasMany;
1011
use Illuminate\Database\Eloquent\Relations\MorphTo;
@@ -20,6 +21,8 @@
2021
*/
2122
class Watch extends Model
2223
{
24+
use HasFactory;
25+
2326
protected $guarded = [];
2427

2528
public function watchable(): MorphTo

app/Entities/Models/Deletion.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BookStack\Activity\Models\Loggable;
66
use BookStack\Users\Models\User;
7+
use Illuminate\Database\Eloquent\Factories\HasFactory;
78
use Illuminate\Database\Eloquent\Model;
89
use Illuminate\Database\Eloquent\Relations\BelongsTo;
910
use Illuminate\Database\Eloquent\Relations\MorphTo;
@@ -17,6 +18,8 @@
1718
*/
1819
class Deletion extends Model implements Loggable
1920
{
21+
use HasFactory;
22+
2023
protected $hidden = [];
2124

2225
/**

app/Entities/Models/PageRevision.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use BookStack\App\Model;
77
use BookStack\Users\Models\User;
88
use Carbon\Carbon;
9+
use Illuminate\Database\Eloquent\Factories\HasFactory;
910
use Illuminate\Database\Eloquent\Relations\BelongsTo;
1011

1112
/**
@@ -30,6 +31,8 @@
3031
*/
3132
class PageRevision extends Model implements Loggable
3233
{
34+
use HasFactory;
35+
3336
protected $fillable = ['name', 'text', 'summary'];
3437
protected $hidden = ['html', 'markdown', 'text'];
3538

app/Users/Models/User.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
use Illuminate\Support\Collection;
3232

3333
/**
34-
* Class User.
35-
*
3634
* @property int $id
3735
* @property string $name
3836
* @property string $slug

app/Users/UserRepo.php

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
use BookStack\Access\UserInviteException;
66
use BookStack\Access\UserInviteService;
77
use BookStack\Activity\ActivityType;
8-
use BookStack\Entities\EntityProvider;
9-
use BookStack\Entities\Models\Entity;
108
use BookStack\Exceptions\NotifyException;
119
use BookStack\Exceptions\UserUpdateException;
1210
use BookStack\Facades\Activity;
@@ -27,7 +25,6 @@ public function __construct(
2725
) {
2826
}
2927

30-
3128
/**
3229
* Get a user by their email address.
3330
*/
@@ -161,15 +158,12 @@ public function update(User $user, array $data, bool $manageUsersAllowed): User
161158
*
162159
* @throws Exception
163160
*/
164-
public function destroy(User $user, ?int $newOwnerId = null)
161+
public function destroy(User $user, ?int $newOwnerId = null): void
165162
{
166163
$this->ensureDeletable($user);
167164

168-
$user->socialAccounts()->delete();
169-
$user->apiTokens()->delete();
170-
$user->favourites()->delete();
171-
$user->mfaValues()->delete();
172-
$user->watches()->delete();
165+
$this->removeUserDependantRelations($user);
166+
$this->nullifyUserNonDependantRelations($user);
173167
$user->delete();
174168

175169
// Delete user profile images
@@ -178,17 +172,52 @@ public function destroy(User $user, ?int $newOwnerId = null)
178172
// Delete related activities
179173
setting()->deleteUserSettings($user->id);
180174

175+
// Migrate or nullify ownership
176+
$newOwner = null;
181177
if (!empty($newOwnerId)) {
182178
$newOwner = User::query()->find($newOwnerId);
183-
if (!is_null($newOwner)) {
184-
$this->migrateOwnership($user, $newOwner);
185-
}
186-
// TODO - Should be be nullifying ownership instead?
187179
}
180+
$this->migrateOwnership($user, $newOwner);
188181

189182
Activity::add(ActivityType::USER_DELETE, $user);
190183
}
191184

185+
protected function removeUserDependantRelations(User $user): void
186+
{
187+
$user->apiTokens()->delete();
188+
$user->socialAccounts()->delete();
189+
$user->favourites()->delete();
190+
$user->mfaValues()->delete();
191+
$user->watches()->delete();
192+
193+
$tables = ['email_confirmations', 'user_invites', 'views'];
194+
foreach ($tables as $table) {
195+
DB::table($table)->where('user_id', '=', $user->id)->delete();
196+
}
197+
}
198+
protected function nullifyUserNonDependantRelations(User $user): void
199+
{
200+
$toNullify = [
201+
'attachments' => ['created_by', 'updated_by'],
202+
'comments' => ['created_by', 'updated_by'],
203+
'deletions' => ['deleted_by'],
204+
'entities' => ['created_by', 'updated_by'],
205+
'images' => ['created_by', 'updated_by'],
206+
'imports' => ['created_by'],
207+
'joint_permissions' => ['owner_id'],
208+
'page_revisions' => ['created_by'],
209+
'sessions' => ['user_id'],
210+
];
211+
212+
foreach ($toNullify as $table => $columns) {
213+
foreach ($columns as $column) {
214+
DB::table($table)
215+
->where($column, '=', $user->id)
216+
->update([$column => null]);
217+
}
218+
}
219+
}
220+
192221
/**
193222
* @throws NotifyException
194223
*/
@@ -206,11 +235,12 @@ protected function ensureDeletable(User $user): void
206235
/**
207236
* Migrate ownership of items in the system from one user to another.
208237
*/
209-
protected function migrateOwnership(User $fromUser, User $toUser): void
238+
protected function migrateOwnership(User $fromUser, User|null $toUser): void
210239
{
240+
$newOwnerValue = $toUser ? $toUser->id : null;
211241
DB::table('entities')
212242
->where('owned_by', '=', $fromUser->id)
213-
->update(['owned_by' => $toUser->id]);
243+
->update(['owned_by' => $newOwnerValue]);
214244
}
215245

216246
/**
@@ -248,7 +278,7 @@ protected function isOnlyAdmin(User $user): bool
248278
*
249279
* @throws UserUpdateException
250280
*/
251-
protected function setUserRoles(User $user, array $roles)
281+
protected function setUserRoles(User $user, array $roles): void
252282
{
253283
$roles = array_filter(array_values($roles));
254284

@@ -261,7 +291,7 @@ protected function setUserRoles(User $user, array $roles)
261291

262292
/**
263293
* Check if the given user is the last admin and their new roles no longer
264-
* contains the admin role.
294+
* contain the admin role.
265295
*/
266296
protected function demotingLastAdmin(User $user, array $newRoles): bool
267297
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Database\Factories\Access\Mfa;
4+
5+
use BookStack\Users\Models\User;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
/**
9+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\BookStack\Access\Mfa\MfaValue>
10+
*/
11+
class MfaValueFactory extends Factory
12+
{
13+
protected $model = \BookStack\Access\Mfa\MfaValue::class;
14+
15+
/**
16+
* Define the model's default state.
17+
*
18+
* @return array<string, mixed>
19+
*/
20+
public function definition(): array
21+
{
22+
return [
23+
'user_id' => User::factory(),
24+
'method' => 'totp',
25+
'value' => '123456',
26+
];
27+
}
28+
}

0 commit comments

Comments
 (0)