diff --git a/app/Access/Controllers/ForgotPasswordController.php b/app/Access/Controllers/ForgotPasswordController.php index bc59e9d2f..86fbe8fa3 100644 --- a/app/Access/Controllers/ForgotPasswordController.php +++ b/app/Access/Controllers/ForgotPasswordController.php @@ -9,11 +9,6 @@ use Illuminate\Support\Facades\Password; class ForgotPasswordController extends Controller { - /** - * Create a new controller instance. - * - * @return void - */ public function __construct() { $this->middleware('guest'); @@ -30,10 +25,6 @@ class ForgotPasswordController extends Controller /** * Send a reset link to the given user. - * - * @param \Illuminate\Http\Request $request - * - * @return \Illuminate\Http\RedirectResponse */ public function sendResetLinkEmail(Request $request) { @@ -56,13 +47,13 @@ class ForgotPasswordController extends Controller $message = trans('auth.reset_password_sent', ['email' => $request->get('email')]); $this->showSuccessNotification($message); - return back()->with('status', trans($response)); + return redirect('/password/email')->with('status', trans($response)); } // If an error was returned by the password broker, we will get this message // translated so we can notify a user of the problem. We'll redirect back // to where the users came from so they can attempt this process again. - return back()->withErrors( + return redirect('/password/email')->withErrors( ['email' => trans($response)] ); } diff --git a/app/Access/Controllers/ResetPasswordController.php b/app/Access/Controllers/ResetPasswordController.php index eae4e5e25..a8a45dddf 100644 --- a/app/Access/Controllers/ResetPasswordController.php +++ b/app/Access/Controllers/ResetPasswordController.php @@ -66,7 +66,7 @@ class ResetPasswordController extends Controller // redirect them back to where they came from with their error message. return $response === Password::PASSWORD_RESET ? $this->sendResetResponse() - : $this->sendResetFailedResponse($request, $response); + : $this->sendResetFailedResponse($request, $response, $request->get('token')); } /** @@ -83,7 +83,7 @@ class ResetPasswordController extends Controller /** * Get the response for a failed password reset. */ - protected function sendResetFailedResponse(Request $request, string $response): RedirectResponse + protected function sendResetFailedResponse(Request $request, string $response, string $token): RedirectResponse { // We show invalid users as invalid tokens as to not leak what // users may exist in the system. @@ -91,7 +91,7 @@ class ResetPasswordController extends Controller $response = Password::INVALID_TOKEN; } - return redirect()->back() + return redirect("/password/reset/{$token}") ->withInput($request->only('email')) ->withErrors(['email' => trans($response)]); } diff --git a/app/Access/Controllers/SocialController.php b/app/Access/Controllers/SocialController.php index bbdabb4ab..07f57062d 100644 --- a/app/Access/Controllers/SocialController.php +++ b/app/Access/Controllers/SocialController.php @@ -91,7 +91,7 @@ class SocialController extends Controller return $this->socialRegisterCallback($socialDriver, $socialUser); } - return redirect()->back(); + return redirect('/'); } /** diff --git a/app/Activity/Controllers/FavouriteController.php b/app/Activity/Controllers/FavouriteController.php index d2534ddfe..b3aff1cef 100644 --- a/app/Activity/Controllers/FavouriteController.php +++ b/app/Activity/Controllers/FavouriteController.php @@ -2,9 +2,6 @@ namespace BookStack\Activity\Controllers; -use BookStack\Activity\Models\Favouritable; -use BookStack\App\Model; -use BookStack\Entities\Models\Entity; use BookStack\Entities\Queries\TopFavourites; use BookStack\Entities\Tools\MixedEntityRequestHelper; use BookStack\Http\Controller; @@ -52,7 +49,7 @@ class FavouriteController extends Controller 'name' => $entity->name, ])); - return redirect()->back(); + return redirect($entity->getUrl()); } /** @@ -70,6 +67,6 @@ class FavouriteController extends Controller 'name' => $entity->name, ])); - return redirect()->back(); + return redirect($entity->getUrl()); } } diff --git a/app/Activity/Controllers/WatchController.php b/app/Activity/Controllers/WatchController.php index c0b1c5872..5df75da39 100644 --- a/app/Activity/Controllers/WatchController.php +++ b/app/Activity/Controllers/WatchController.php @@ -24,6 +24,6 @@ class WatchController extends Controller $this->showSuccessNotification(trans('activities.watch_update_level_notification')); - return redirect()->back(); + return redirect($watchable->getUrl()); } } diff --git a/app/Entities/Controllers/ChapterController.php b/app/Entities/Controllers/ChapterController.php index ee1df0581..40a537303 100644 --- a/app/Entities/Controllers/ChapterController.php +++ b/app/Entities/Controllers/ChapterController.php @@ -12,6 +12,7 @@ use BookStack\Entities\Tools\HierarchyTransformer; use BookStack\Entities\Tools\NextPreviousContentLocator; use BookStack\Exceptions\MoveOperationException; use BookStack\Exceptions\NotFoundException; +use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; use BookStack\References\ReferenceFetcher; @@ -170,7 +171,7 @@ class ChapterController extends Controller /** * Perform the move action for a chapter. * - * @throws NotFoundException + * @throws NotFoundException|NotifyException */ public function move(Request $request, string $bookSlug, string $chapterSlug) { @@ -184,13 +185,13 @@ class ChapterController extends Controller } try { - $newBook = $this->chapterRepo->move($chapter, $entitySelection); + $this->chapterRepo->move($chapter, $entitySelection); } catch (PermissionsException $exception) { $this->showPermissionError(); } catch (MoveOperationException $exception) { $this->showErrorNotification(trans('errors.selected_book_not_found')); - return redirect()->back(); + return redirect($chapter->getUrl('/move')); } return redirect($chapter->getUrl()); @@ -231,7 +232,7 @@ class ChapterController extends Controller if (is_null($newParentBook)) { $this->showErrorNotification(trans('errors.selected_book_not_found')); - return redirect()->back(); + return redirect($chapter->getUrl('/copy')); } $this->checkOwnablePermission('chapter-create', $newParentBook); diff --git a/app/Entities/Controllers/PageController.php b/app/Entities/Controllers/PageController.php index 624931065..4d8c7e809 100644 --- a/app/Entities/Controllers/PageController.php +++ b/app/Entities/Controllers/PageController.php @@ -391,7 +391,7 @@ class PageController extends Controller } catch (Exception $exception) { $this->showErrorNotification(trans('errors.selected_book_chapter_not_found')); - return redirect()->back(); + return redirect($page->getUrl('/move')); } return redirect($page->getUrl()); @@ -431,7 +431,7 @@ class PageController extends Controller if (is_null($newParent)) { $this->showErrorNotification(trans('errors.selected_book_chapter_not_found')); - return redirect()->back(); + return redirect($page->getUrl('/copy')); } $this->checkOwnablePermission('page-create', $newParent); diff --git a/app/Http/Controller.php b/app/Http/Controller.php index 6e81dfd65..8facf5dab 100644 --- a/app/Http/Controller.php +++ b/app/Http/Controller.php @@ -9,6 +9,8 @@ use BookStack\Facades\Activity; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Http\JsonResponse; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; use Illuminate\Routing\Controller as BaseController; abstract class Controller extends BaseController @@ -165,4 +167,20 @@ abstract class Controller extends BaseController { return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)]; } + + /** + * Redirect to the URL provided in the request as a '_return' parameter. + * Will check that the parameter leads to a URL under the root path of the system. + */ + protected function redirectToRequest(Request $request): RedirectResponse + { + $basePath = url('/'); + $returnUrl = $request->input('_return') ?? $basePath; + + if (!str_starts_with($returnUrl, $basePath)) { + return redirect($basePath); + } + + return redirect($returnUrl); + } } diff --git a/app/Users/Controllers/RoleController.php b/app/Users/Controllers/RoleController.php index 0052d829d..a874ce4d6 100644 --- a/app/Users/Controllers/RoleController.php +++ b/app/Users/Controllers/RoleController.php @@ -154,7 +154,7 @@ class RoleController extends Controller } catch (PermissionsException $e) { $this->showErrorNotification($e->getMessage()); - return redirect()->back(); + return redirect("/settings/roles/delete/{$id}"); } return redirect('/settings/roles'); diff --git a/app/Users/Controllers/UserPreferencesController.php b/app/Users/Controllers/UserPreferencesController.php index 3600dc55f..0bed2d22a 100644 --- a/app/Users/Controllers/UserPreferencesController.php +++ b/app/Users/Controllers/UserPreferencesController.php @@ -3,9 +3,6 @@ namespace BookStack\Users\Controllers; use BookStack\Http\Controller; -use BookStack\Permissions\PermissionApplicator; -use BookStack\Settings\UserNotificationPreferences; -use BookStack\Settings\UserShortcutMap; use BookStack\Users\UserRepo; use Illuminate\Http\Request; @@ -23,7 +20,7 @@ class UserPreferencesController extends Controller { $valueViewTypes = ['books', 'bookshelves', 'bookshelf']; if (!in_array($type, $valueViewTypes)) { - return redirect()->back(500); + return $this->redirectToRequest($request); } $view = $request->get('view'); @@ -34,7 +31,7 @@ class UserPreferencesController extends Controller $key = $type . '_view_type'; setting()->putForCurrentUser($key, $view); - return redirect()->back(302, [], "/"); + return $this->redirectToRequest($request); } /** @@ -44,7 +41,7 @@ class UserPreferencesController extends Controller { $validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks', 'tags', 'page_revisions']; if (!in_array($type, $validSortTypes)) { - return redirect()->back(500); + return $this->redirectToRequest($request); } $sort = substr($request->get('sort') ?: 'name', 0, 50); @@ -55,18 +52,18 @@ class UserPreferencesController extends Controller setting()->putForCurrentUser($sortKey, $sort); setting()->putForCurrentUser($orderKey, $order); - return redirect()->back(302, [], "/"); + return $this->redirectToRequest($request); } /** * Toggle dark mode for the current user. */ - public function toggleDarkMode() + public function toggleDarkMode(Request $request) { $enabled = setting()->getForCurrentUser('dark-mode-enabled'); setting()->putForCurrentUser('dark-mode-enabled', $enabled ? 'false' : 'true'); - return redirect()->back(); + return $this->redirectToRequest($request); } /** diff --git a/resources/views/common/dark-mode-toggle.blade.php b/resources/views/common/dark-mode-toggle.blade.php index d6ecbc4d6..531755109 100644 --- a/resources/views/common/dark-mode-toggle.blade.php +++ b/resources/views/common/dark-mode-toggle.blade.php @@ -1,6 +1,7 @@