From a868012048215d9ad080eee3e7bd66cfe9b1beaf Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 17 Oct 2023 17:38:07 +0100 Subject: [PATCH] Users: Built out auth page for my-account section --- app/Access/SocialAuthService.php | 1 + .../Controllers/UserAccountController.php | 54 ++++++++++-- lang/en/preferences.php | 6 ++ lang/en/settings.php | 4 +- resources/icons/security.svg | 1 + resources/views/settings/layout.blade.php | 2 +- resources/views/users/account/auth.blade.php | 87 +++++++++++++++++++ .../views/users/account/layout.blade.php | 7 +- .../users/api-tokens/parts/list.blade.php | 1 + resources/views/users/edit.blade.php | 36 ++++---- resources/views/users/parts/form.blade.php | 2 +- routes/web.php | 2 + 12 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 resources/icons/security.svg create mode 100644 resources/views/users/account/auth.blade.php diff --git a/app/Access/SocialAuthService.php b/app/Access/SocialAuthService.php index 24a04ef7e..fe9195430 100644 --- a/app/Access/SocialAuthService.php +++ b/app/Access/SocialAuthService.php @@ -214,6 +214,7 @@ class SocialAuthService /** * Gets the names of the active social drivers. + * @returns array */ public function getActiveDrivers(): array { diff --git a/app/Users/Controllers/UserAccountController.php b/app/Users/Controllers/UserAccountController.php index 9152eb5e8..3dd13b851 100644 --- a/app/Users/Controllers/UserAccountController.php +++ b/app/Users/Controllers/UserAccountController.php @@ -2,18 +2,25 @@ namespace BookStack\Users\Controllers; +use BookStack\Access\SocialAuthService; use BookStack\Http\Controller; use BookStack\Permissions\PermissionApplicator; use BookStack\Settings\UserNotificationPreferences; use BookStack\Settings\UserShortcutMap; use BookStack\Users\UserRepo; +use Closure; use Illuminate\Http\Request; +use Illuminate\Validation\Rules\Password; class UserAccountController extends Controller { public function __construct( - protected UserRepo $userRepo + protected UserRepo $userRepo, ) { + $this->middleware(function (Request $request, Closure $next) { + $this->preventGuestAccess(); + return $next($request); + }); } /** @@ -21,8 +28,7 @@ class UserAccountController extends Controller */ public function index() { - $guest = user()->isGuest(); - $mfaMethods = $guest ? [] : user()->mfaValues->groupBy('method'); + $mfaMethods = user()->mfaValues->groupBy('method'); return view('users.account.index', [ 'mfaMethods' => $mfaMethods, @@ -40,6 +46,7 @@ class UserAccountController extends Controller $this->setPageTitle(trans('preferences.shortcuts_interface')); return view('users.account.shortcuts', [ + 'category' => 'shortcuts', 'shortcuts' => $shortcuts, 'enabled' => $enabled, ]); @@ -68,7 +75,6 @@ class UserAccountController extends Controller public function showNotifications(PermissionApplicator $permissions) { $this->checkPermission('receive-notifications'); - $this->preventGuestAccess(); $preferences = (new UserNotificationPreferences(user())); @@ -79,6 +85,7 @@ class UserAccountController extends Controller $this->setPageTitle(trans('preferences.notifications')); return view('users.account.notifications', [ + 'category' => 'notifications', 'preferences' => $preferences, 'watches' => $watches, ]); @@ -90,7 +97,6 @@ class UserAccountController extends Controller public function updateNotifications(Request $request) { $this->checkPermission('receive-notifications'); - $this->preventGuestAccess(); $data = $this->validate($request, [ 'preferences' => ['required', 'array'], 'preferences.*' => ['required', 'string'], @@ -102,4 +108,42 @@ class UserAccountController extends Controller return redirect('/my-account/notifications'); } + + /** + * Show the view for the "Access & Security" account options. + */ + public function showAuth(SocialAuthService $socialAuthService) + { + $mfaMethods = user()->mfaValues->groupBy('method'); + + $this->setPageTitle(trans('preferences.auth')); + + return view('users.account.auth', [ + 'category' => 'auth', + 'mfaMethods' => $mfaMethods, + 'authMethod' => config('auth.method'), + 'activeSocialDrivers' => $socialAuthService->getActiveDrivers(), + ]); + } + + /** + * Handle the submission for the auth change password form. + */ + public function updatePassword(Request $request) + { + if (config('auth.method') !== 'standard') { + $this->showPermissionError(); + } + + $validated = $this->validate($request, [ + 'password' => ['required_with:password_confirm', Password::default()], + 'password-confirm' => ['same:password', 'required_with:password'], + ]); + + $this->userRepo->update(user(), $validated, false); + + $this->showSuccessNotification(trans('preferences.auth_change_password_success')); + + return redirect('/my-account/auth'); + } } diff --git a/lang/en/preferences.php b/lang/en/preferences.php index cf1ee2b37..d112b9ebb 100644 --- a/lang/en/preferences.php +++ b/lang/en/preferences.php @@ -29,5 +29,11 @@ return [ 'notifications_watched' => 'Watched & Ignored Items', 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'auth' => 'Access & Security', + 'auth_change_password' => 'Change Password', + 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', + 'auth_change_password_success' => 'Password has been updated!', + + 'profile' => 'Profile Details', 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.', ]; diff --git a/lang/en/settings.php b/lang/en/settings.php index 9f60606ac..579c4b5c8 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -194,7 +194,7 @@ return [ 'users_send_invite_option' => 'Send user invite email', 'users_external_auth_id' => 'External Authentication ID', 'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.', - 'users_password_warning' => 'Only fill the below if you would like to change your password.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', 'users_delete' => 'Delete User', 'users_delete_named' => 'Delete user :userName', @@ -210,12 +210,14 @@ return [ 'users_preferred_language' => 'Preferred Language', 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', 'users_social_accounts' => 'Social Accounts', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', 'users_social_connect' => 'Connect Account', 'users_social_disconnect' => 'Disconnect Account', 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', 'users_api_tokens' => 'API Tokens', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', 'users_api_tokens_none' => 'No API tokens have been created for this user', 'users_api_tokens_create' => 'Create Token', 'users_api_tokens_expires' => 'Expires', diff --git a/resources/icons/security.svg b/resources/icons/security.svg new file mode 100644 index 000000000..4fc0d20b9 --- /dev/null +++ b/resources/icons/security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/views/settings/layout.blade.php b/resources/views/settings/layout.blade.php index 7c6f8b002..94a8f7725 100644 --- a/resources/views/settings/layout.blade.php +++ b/resources/views/settings/layout.blade.php @@ -12,7 +12,7 @@
{{ trans('settings.system_version') }}
diff --git a/resources/views/users/account/auth.blade.php b/resources/views/users/account/auth.blade.php new file mode 100644 index 000000000..3503978cf --- /dev/null +++ b/resources/views/users/account/auth.blade.php @@ -0,0 +1,87 @@ +@extends('users.account.layout') + +@section('main') + + @if($authMethod === 'standard') +
+
+ {{ method_field('put') }} + {{ csrf_field() }} + +

{{ trans('preferences.auth_change_password') }}

+ +

+ {{ trans('preferences.auth_change_password_desc') }} +

+ +
+
+ + @include('form.password', ['name' => 'password', 'autocomplete' => 'new-password']) +
+
+ + @include('form.password', ['name' => 'password-confirm']) +
+
+ +
+ +
+ +
+
+ @endif + +
+
+

{{ trans('settings.users_mfa') }}

+

{{ trans('settings.users_mfa_desc') }}

+

+ @if ($mfaMethods->count() > 0) + @icon('check-circle') + @else + @icon('cancel') + @endif + {{ trans_choice('settings.users_mfa_x_methods', $mfaMethods->count()) }} +

+
+ +
+ + @if(count($activeSocialDrivers) > 0) +
+

{{ trans('settings.users_social_accounts') }}

+

{{ trans('settings.users_social_accounts_info') }}

+
+
+ @foreach($activeSocialDrivers as $driver => $enabled) +
+
@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])
+
+ @if(user()->hasSocialAccount($driver)) +
+ {{ csrf_field() }} + +
+ @else + {{ trans('settings.users_social_connect') }} + @endif +
+
+ @endforeach +
+
+
+ @endif + + @if(userCan('access-api')) + @include('users.api-tokens.parts.list', ['user' => user()]) + @endif +@stop diff --git a/resources/views/users/account/layout.blade.php b/resources/views/users/account/layout.blade.php index 9eaa1eca1..ff5ad3622 100644 --- a/resources/views/users/account/layout.blade.php +++ b/resources/views/users/account/layout.blade.php @@ -9,9 +9,10 @@ diff --git a/resources/views/users/api-tokens/parts/list.blade.php b/resources/views/users/api-tokens/parts/list.blade.php index 58617fb85..3081682a4 100644 --- a/resources/views/users/api-tokens/parts/list.blade.php +++ b/resources/views/users/api-tokens/parts/list.blade.php @@ -8,6 +8,7 @@ @endif +

{{ trans('settings.users_api_tokens_desc') }}

@if (count($user->apiTokens) > 0)
@foreach($user->apiTokens as $token) diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 832186930..e6b477a12 100644 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -51,7 +51,7 @@

{{ trans('settings.users_mfa') }}

-

{{ trans('settings.users_mfa_desc') }}

+

{{ trans('settings.users_mfa_desc') }}

@if ($mfaMethods->count() > 0) @@ -71,28 +71,28 @@
- @if(user()->id === $user->id && count($activeSocialDrivers) > 0) + @if(count($activeSocialDrivers) > 0)
-

{{ trans('settings.users_social_accounts') }}

-

{{ trans('settings.users_social_accounts_info') }}

+
+

{{ trans('settings.users_social_accounts') }}

+
+ @if(user()->id === $user->id) + {{ trans('common.manage') }} + @endif +
+
+

{{ trans('settings.users_social_accounts_desc') }}

- @foreach($activeSocialDrivers as $driver => $enabled) + @foreach($activeSocialDrivers as $driver => $driverName)
@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])
-
- @if($user->hasSocialAccount($driver)) -
- {{ csrf_field() }} - -
- @else - {{ trans('settings.users_social_connect') }} - @endif -
+

{{ $driverName }}

+ @if($user->hasSocialAccount($driver)) +

Connected

+ @else +

Disconnected

+ @endif
@endforeach
diff --git a/resources/views/users/parts/form.blade.php b/resources/views/users/parts/form.blade.php index 7ff48a83d..d9f958837 100644 --- a/resources/views/users/parts/form.blade.php +++ b/resources/views/users/parts/form.blade.php @@ -64,7 +64,7 @@ @endif
-

{{ trans('settings.users_password_desc') }}

+

{{ trans('settings.users_password_desc') }}

@if(isset($model))

{{ trans('settings.users_password_warning') }} diff --git a/routes/web.php b/routes/web.php index df845bfbc..a7d3534bd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -238,6 +238,8 @@ Route::middleware('auth')->group(function () { Route::put('/my-account/shortcuts', [UserControllers\UserAccountController::class, 'updateShortcuts']); Route::get('/my-account/notifications', [UserControllers\UserAccountController::class, 'showNotifications']); Route::put('/my-account/notifications', [UserControllers\UserAccountController::class, 'updateNotifications']); + Route::get('/my-account/auth', [UserControllers\UserAccountController::class, 'showAuth']); + Route::put('/my-account/auth/password', [UserControllers\UserAccountController::class, 'updatePassword']); Route::patch('/preferences/change-view/{type}', [UserControllers\UserPreferencesController::class, 'changeView']); Route::patch('/preferences/change-sort/{type}', [UserControllers\UserPreferencesController::class, 'changeSort']); Route::patch('/preferences/change-expansion/{type}', [UserControllers\UserPreferencesController::class, 'changeExpansion']);