X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/b8c16b15a9f945b72d2ca4fe0c0172ba422199bc..refs/pull/3373/head:/app/Auth/UserRepo.php diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php index cfa7bfce1..28ce96c49 100644 --- a/app/Auth/UserRepo.php +++ b/app/Auth/UserRepo.php @@ -1,31 +1,30 @@ -user = $user; - $this->role = $role; + $this->userAvatar = $userAvatar; + $this->inviteService = $inviteService; } /** @@ -33,234 +32,224 @@ class UserRepo */ public function getByEmail(string $email): ?User { - return $this->user->where('email', '=', $email)->first(); + return User::query()->where('email', '=', $email)->first(); } /** - * @param int $id - * @return User + * Get a user by their ID. */ - public function getById($id) + public function getById(int $id): User { - return $this->user->newQuery()->findOrFail($id); + return User::query()->findOrFail($id); } /** - * Get all the users with their permissions. - * @return Builder|static + * Get a user by their slug. */ - public function getAllUsers() + public function getBySlug(string $slug): User { - return $this->user->with('roles', 'avatar')->orderBy('name', 'asc')->get(); + return User::query()->where('slug', '=', $slug)->firstOrFail(); } /** - * Get all the users with their permissions in a paginated format. - * @param int $count - * @param $sortData - * @return Builder|static + * Create a new basic instance of user with the given pre-validated data. + * + * @param array{name: string, email: string, password: ?string, external_auth_id: ?string, language: ?string, roles: ?array} $data */ - public function getAllUsersPaginatedAndSorted($count, $sortData) + public function createWithoutActivity(array $data, bool $emailConfirmed = false): User { - $query = $this->user->with('roles', 'avatar')->orderBy($sortData['sort'], $sortData['order']); - - if ($sortData['search']) { - $term = '%' . $sortData['search'] . '%'; - $query->where(function ($query) use ($term) { - $query->where('name', 'like', $term) - ->orWhere('email', 'like', $term); - }); + $user = new User(); + $user->name = $data['name']; + $user->email = $data['email']; + $user->password = bcrypt(empty($data['password']) ? Str::random(32) : $data['password']); + $user->email_confirmed = $emailConfirmed; + $user->external_auth_id = $data['external_auth_id'] ?? ''; + + $user->refreshSlug(); + $user->save(); + + if (!empty($data['language'])) { + setting()->putUser($user, 'language', $data['language']); } - return $query->paginate($count); - } + if (isset($data['roles'])) { + $this->setUserRoles($user, $data['roles']); + } - /** - * Creates a new user and attaches a role to them. - */ - public function registerNew(array $data, bool $emailConfirmed = false): User - { - $user = $this->create($data, $emailConfirmed); - $user->attachDefaultRole(); $this->downloadAndAssignUserAvatar($user); return $user; } /** - * Assign a user to a system-level role. - * @param User $user - * @param $systemRoleName - * @throws NotFoundException + * As per "createWithoutActivity" but records a "create" activity. + * + * @param array{name: string, email: string, password: ?string, external_auth_id: ?string, language: ?string, roles: ?array} $data */ - public function attachSystemRole(User $user, $systemRoleName) + public function create(array $data, bool $sendInvite = false): User { - $role = $this->role->newQuery()->where('system_name', '=', $systemRoleName)->first(); - if ($role === null) { - throw new NotFoundException("Role '{$systemRoleName}' not found"); + $user = $this->createWithoutActivity($data, true); + + if ($sendInvite) { + $this->inviteService->sendInvitation($user); } - $user->attachRole($role); + + Activity::add(ActivityType::USER_CREATE, $user); + + return $user; } /** - * Checks if the give user is the only admin. - * @param User $user - * @return bool + * Update the given user with the given data. + * + * @param array{name: ?string, email: ?string, external_auth_id: ?string, password: ?string, roles: ?array, language: ?string} $data + * + * @throws UserUpdateException */ - public function isOnlyAdmin(User $user) + public function update(User $user, array $data, bool $manageUsersAllowed): User { - if (!$user->hasSystemRole('admin')) { - return false; + if (!empty($data['name'])) { + $user->name = $data['name']; + $user->refreshSlug(); } - $adminRole = $this->role->getSystemRole('admin'); - if ($adminRole->users->count() > 1) { - return false; + if (!empty($data['email']) && $manageUsersAllowed) { + $user->email = $data['email']; } - return true; - } - /** - * Set the assigned user roles via an array of role IDs. - * @param User $user - * @param array $roles - * @throws UserUpdateException - */ - public function setUserRoles(User $user, array $roles) - { - if ($this->demotingLastAdmin($user, $roles)) { - throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl()); + if (!empty($data['external_auth_id']) && $manageUsersAllowed) { + $user->external_auth_id = $data['external_auth_id']; } - $user->roles()->sync($roles); - } + if (isset($data['roles']) && $manageUsersAllowed) { + $this->setUserRoles($user, $data['roles']); + } - /** - * Check if the given user is the last admin and their new roles no longer - * contains the admin role. - * @param User $user - * @param array $newRoles - * @return bool - */ - protected function demotingLastAdmin(User $user, array $newRoles) : bool - { - if ($this->isOnlyAdmin($user)) { - $adminRole = $this->role->getSystemRole('admin'); - if (!in_array(strval($adminRole->id), $newRoles)) { - return true; - } + if (!empty($data['password'])) { + $user->password = bcrypt($data['password']); } - return false; - } + if (!empty($data['language'])) { + setting()->putUser($user, 'language', $data['language']); + } - /** - * Create a new basic instance of user. - */ - public function create(array $data, bool $emailConfirmed = false): User - { - return $this->user->forceCreate([ - 'name' => $data['name'], - 'email' => $data['email'], - 'password' => bcrypt($data['password']), - 'email_confirmed' => $emailConfirmed, - 'external_auth_id' => $data['external_auth_id'] ?? '', - ]); + $user->save(); + Activity::add(ActivityType::USER_UPDATE, $user); + + return $user; } /** * Remove the given user from storage, Delete all related content. - * @param User $user + * * @throws Exception */ - public function destroy(User $user) + public function destroy(User $user, ?int $newOwnerId = null) { + $this->ensureDeletable($user); + $user->socialAccounts()->delete(); $user->apiTokens()->delete(); + $user->favourites()->delete(); + $user->mfaValues()->delete(); $user->delete(); - + // Delete user profile images - $profileImages = Image::where('type', '=', 'user')->where('uploaded_to', '=', $user->id)->get(); - foreach ($profileImages as $image) { - Images::destroy($image); + $this->userAvatar->destroyAllForUser($user); + + if (!empty($newOwnerId)) { + $newOwner = User::query()->find($newOwnerId); + if (!is_null($newOwner)) { + $this->migrateOwnership($user, $newOwner); + } } + + Activity::add(ActivityType::USER_DELETE, $user); } /** - * Get the latest activity for a user. - * @param User $user - * @param int $count - * @param int $page - * @return array + * @throws NotifyException */ - public function getActivity(User $user, $count = 20, $page = 0) + protected function ensureDeletable(User $user): void { - return Activity::userActivity($user, $count, $page); + if ($this->isOnlyAdmin($user)) { + throw new NotifyException(trans('errors.users_cannot_delete_only_admin'), $user->getEditUrl()); + } + + if ($user->system_name === 'public') { + throw new NotifyException(trans('errors.users_cannot_delete_guest'), $user->getEditUrl()); + } } /** - * Get the recently created content for this given user. + * Migrate ownership of items in the system from one user to another. */ - public function getRecentlyCreated(User $user, int $count = 20): array + protected function migrateOwnership(User $fromUser, User $toUser) { - $query = function (Builder $query) use ($user, $count) { - return $query->orderBy('created_at', 'desc') - ->where('created_by', '=', $user->id) - ->take($count) - ->get(); - }; - - return [ - 'pages' => $query(Page::visible()->where('draft', '=', false)), - 'chapters' => $query(Chapter::visible()), - 'books' => $query(Book::visible()), - 'shelves' => $query(Bookshelf::visible()), - ]; + $entities = (new EntityProvider())->all(); + foreach ($entities as $instance) { + $instance->newQuery()->where('owned_by', '=', $fromUser->id) + ->update(['owned_by' => $toUser->id]); + } } /** - * Get asset created counts for the give user. + * Get an avatar image for a user and set it as their avatar. + * Returns early if avatars disabled or not set in config. */ - public function getAssetCounts(User $user): array + protected function downloadAndAssignUserAvatar(User $user): void { - $createdBy = ['created_by' => $user->id]; - return [ - 'pages' => Page::visible()->where($createdBy)->count(), - 'chapters' => Chapter::visible()->where($createdBy)->count(), - 'books' => Book::visible()->where($createdBy)->count(), - 'shelves' => Bookshelf::visible()->where($createdBy)->count(), - ]; + try { + $this->userAvatar->fetchAndAssignToUser($user); + } catch (Exception $e) { + Log::error('Failed to save user avatar image'); + } } /** - * Get the roles in the system that are assignable to a user. - * @return mixed + * Checks if the give user is the only admin. */ - public function getAllRoles() + protected function isOnlyAdmin(User $user): bool { - return $this->role->newQuery()->orderBy('name', 'asc')->get(); + if (!$user->hasSystemRole('admin')) { + return false; + } + + $adminRole = Role::getSystemRole('admin'); + if ($adminRole->users()->count() > 1) { + return false; + } + + return true; } /** - * Get an avatar image for a user and set it as their avatar. - * Returns early if avatars disabled or not set in config. - * @param User $user - * @return bool + * Set the assigned user roles via an array of role IDs. + * + * @throws UserUpdateException */ - public function downloadAndAssignUserAvatar(User $user) + protected function setUserRoles(User $user, array $roles) { - if (!Images::avatarFetchEnabled()) { - return false; + if ($this->demotingLastAdmin($user, $roles)) { + throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl()); } - try { - $avatar = Images::saveUserAvatar($user); - $user->avatar()->associate($avatar); - $user->save(); - return true; - } catch (Exception $e) { - Log::error('Failed to save user avatar image'); - return false; + $user->roles()->sync($roles); + } + + /** + * Check if the given user is the last admin and their new roles no longer + * contains the admin role. + */ + protected function demotingLastAdmin(User $user, array $newRoles): bool + { + if ($this->isOnlyAdmin($user)) { + $adminRole = Role::getSystemRole('admin'); + if (!in_array(strval($adminRole->id), $newRoles)) { + return true; + } } + + return false; } }