Merge branch 'master' into release

This commit is contained in:
Dan Brown 2021-10-15 14:34:23 +01:00
commit 1e56aaea04
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
19 changed files with 526 additions and 434 deletions

View File

@ -8,6 +8,7 @@ use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\Fill;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use BookStack\Auth\User;
use PragmaRX\Google2FA\Google2FA;
use PragmaRX\Google2FA\Support\Constants;
@ -36,11 +37,11 @@ class TotpService
/**
* Generate a TOTP URL from secret key.
*/
public function generateUrl(string $secret): string
public function generateUrl(string $secret, User $user): string
{
return $this->google2fa->getQRCodeUrl(
setting('app-name'),
user()->email,
$user->email,
$secret
);
}

View File

@ -70,6 +70,7 @@ return [
'email' => 'emails.password',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],

View File

@ -70,7 +70,7 @@ return [
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
'chroot' => realpath(base_path()),
'chroot' => realpath(public_path()),
/**
* Whether to use Unicode fonts or not.

View File

@ -0,0 +1,49 @@
<?php
namespace BookStack\Exceptions;
use Whoops\Handler\Handler;
class WhoopsBookStackPrettyHandler extends Handler
{
/**
* @return int|null A handler may return nothing, or a Handler::HANDLE_* constant
*/
public function handle()
{
$exception = $this->getException();
echo view('errors.debug', [
'error' => $exception->getMessage(),
'errorClass' => get_class($exception),
'trace' => $exception->getTraceAsString(),
'environment' => $this->getEnvironment(),
])->render();
return Handler::QUIT;
}
protected function safeReturn(callable $callback, $default = null)
{
try {
return $callback();
} catch (\Exception $e) {
return $default;
}
}
protected function getEnvironment(): array
{
return [
'PHP Version' => phpversion(),
'BookStack Version' => $this->safeReturn(function () {
$versionFile = base_path('version');
return trim(file_get_contents($versionFile));
}, 'unknown'),
'Theme Configured' => $this->safeReturn(function () {
return config('view.theme');
}) ?? 'None',
];
}
}

View File

@ -56,7 +56,7 @@ class ForgotPasswordController extends Controller
$this->logActivity(ActivityType::AUTH_PASSWORD_RESET, $request->get('email'));
}
if ($response === Password::RESET_LINK_SENT || $response === Password::INVALID_USER) {
if (in_array($response, [Password::RESET_LINK_SENT, Password::INVALID_USER, Password::RESET_THROTTLED])) {
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
$this->showSuccessNotification($message);

View File

@ -31,7 +31,7 @@ class MfaTotpController extends Controller
session()->put(static::SETUP_SECRET_SESSION_KEY, encrypt($totpSecret));
}
$qrCodeUrl = $totp->generateUrl($totpSecret);
$qrCodeUrl = $totp->generateUrl($totpSecret, $this->currentOrLastAttemptedUser());
$svg = $totp->generateQrCodeSvg($qrCodeUrl);
return view('mfa.totp-generate', [

View File

@ -9,6 +9,7 @@ use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use BookStack\Exceptions\WhoopsBookStackPrettyHandler;
use BookStack\Settings\Setting;
use BookStack\Settings\SettingService;
use BookStack\Util\CspService;
@ -20,6 +21,7 @@ use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Laravel\Socialite\Contracts\Factory as SocialiteFactory;
use Whoops\Handler\HandlerInterface;
class AppServiceProvider extends ServiceProvider
{
@ -65,6 +67,10 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->bind(HandlerInterface::class, function ($app) {
return $app->make(WhoopsBookStackPrettyHandler::class);
});
$this->app->singleton(SettingService::class, function ($app) {
return new SettingService($app->make(Setting::class), $app->make(Repository::class));
});

View File

@ -17,8 +17,8 @@
"barryvdh/laravel-dompdf": "^0.9.0",
"barryvdh/laravel-snappy": "^0.4.8",
"doctrine/dbal": "^2.12.1",
"facade/ignition": "^1.16.4",
"fideloper/proxy": "^4.4.1",
"filp/whoops": "^2.14",
"intervention/image": "^2.5.1",
"laravel/framework": "^6.20.33",
"laravel/socialite": "^5.1",

586
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,17 +14,18 @@ A platform for storing and organising information and documentation. Details for
* [Documentation](https://www.bookstackapp.com/docs)
* [Demo Instance](https://demo.bookstackapp.com)
* [Admin Login](https://demo.bookstackapp.com/login?email=admin@example.com&password=password)
* [Screenshots](https://www.bookstackapp.com/#screenshots)
* [BookStack Blog](https://www.bookstackapp.com/blog)
* [Issue List](https://github.com/BookStackApp/BookStack/issues)
* [Discord Chat](https://discord.gg/ztkBqR2)
## 📚 Project Definition
BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
BookStack is an opinionated wiki system that provides a pleasant and simple out-of-the-box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above.
In regards to development philosophy, BookStack has a relaxed, open & positive approach. At the end of the day this is free software developed and maintained by people donating their own free time.
In regard to development philosophy, BookStack has a relaxed, open & positive approach. At the end of the day this is free software developed and maintained by people donating their own free time.
## 🛣️ Road Map
@ -41,17 +42,23 @@ Below is a high-level road map view for BookStack to provide a sense of directio
## 🚀 Release Versioning & Process
BookStack releases are each assigned a version number, such as "v0.25.2", in the format `v<phase>.<feature>.<patch>`. A change only in the `patch` number indicates a fairly minor release that mainly contains fixes and therefore is very unlikely to cause breakages upon update. A change in the `feature` number indicates a release which will generally bring new features in addition to fixes and enhancements. These releases have a small chance of introducing breaking changes upon update so it's worth checking for any notes in the [update guide](https://www.bookstackapp.com/docs/admin/updates/). A change in the `phase` indicates a much large change in BookStack that will likely incur breakages requiring manual intervention.
BookStack releases are each assigned a date-based version number in the format `v<year>.<month>[.<optional_patch_number>]`. For example:
- `v20.12` - New feature released launched during December 2020.
- `v21.06.2` - Second patch release upon the June 2021 feature release.
Patch releases are generally fairly minor, primarily intended for fixes and therefore is fairly unlikely to cause breakages upon update.
Feature releases are generally larger, bringing new features in addition to fixes and enhancements. These releases have a greater chance of introducing breaking changes upon update, so it's worth checking for any notes in the [update guide](https://www.bookstackapp.com/docs/admin/updates/).
Each BookStack release will have a [milestone](https://github.com/BookStackApp/BookStack/milestones) created with issues & pull requests assigned to it to define what will be in that release. Milestones are built up then worked through until complete at which point, after some testing and documentation updates, the release will be deployed.
For feature releases, and some patch releases, the release will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
Feature releases, and some patch releases, will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
## 🛠️ Development & Testing
All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at its version. Here are the current development requirements:
* [Node.js](https://nodejs.org/en/) v12.0+
* [Node.js](https://nodejs.org/en/) v14.0+
This project uses SASS for CSS development and this is built, along with the JavaScript, using a range of npm scripts. The below npm commands can be used to install the dependencies & run the build tasks:

View File

@ -48,8 +48,8 @@ return [
'favourite_remove_notification' => '".name" se eliminó de sus favoritos',
// MFA
'mfa_setup_method_notification' => 'Método de Autenticación en Dos Pasos configurado correctamente',
'mfa_remove_method_notification' => 'Método de Autenticación en Dos Pasos eliminado correctamente',
'mfa_setup_method_notification' => 'Método de autenticación de múltiples factores configurado satisfactoriamente',
'mfa_remove_method_notification' => 'Método de autenticación de múltiples factores eliminado satisfactoriamente',
// Other
'commented_on' => 'comentado',

View File

@ -76,37 +76,37 @@ return [
'user_invite_success' => 'Contraseña establecida, ahora tiene acceso a :appName!',
// Multi-factor Authentication
'mfa_setup' => 'Configurar Autenticación en Dos Pasos',
'mfa_setup_desc' => 'La autenticación en dos pasos añade una capa de seguridad adicional a tu cuenta de usuario.',
'mfa_setup' => 'Configurar autenticación de múltiples factores',
'mfa_setup_desc' => 'Configure la autenticación de múltiples factores como una capa extra de seguridad para su cuenta de usuario.',
'mfa_setup_configured' => 'Ya está configurado',
'mfa_setup_reconfigure' => 'Reconfigurar',
'mfa_setup_remove_confirmation' => '¿Está seguro de que desea eliminar este método de autenticación de dos pasos?',
'mfa_setup_remove_confirmation' => '¿Está seguro que desea eliminar este método de autenticación de múltiples factores?',
'mfa_setup_action' => 'Configuración',
'mfa_backup_codes_usage_limit_warning' => 'Quedan menos de 5 códigos de respaldo, Por favor, genera y almacena un nuevo conjunto antes de que te quedes sin códigos para evitar que te bloquees fuera de tu cuenta.',
'mfa_option_totp_title' => 'Aplicación para móviles',
'mfa_option_totp_desc' => 'Para utilizar la autenticación de dos pasos necesitarás una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.',
'mfa_backup_codes_usage_limit_warning' => 'Quedan menos de 5 códigos de respaldo, Por favor, genere y guarde un nuevo conjunto antes de que se quede sin códigos para evitar que se bloquee su cuenta.',
'mfa_option_totp_title' => 'Aplicación móvil',
'mfa_option_totp_desc' => 'Para utilizar la autenticación en dos pasos necesitará una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Códigos de Respaldo',
'mfa_option_backup_codes_desc' => 'Almacena de forma segura un conjunto de códigos de respaldo de un solo uso que puedes introducir para verificar tu identidad.',
'mfa_option_backup_codes_desc' => 'Almacene de forma segura un conjunto de códigos de respaldo de un solo uso que pueda introducir para verificar su identidad.',
'mfa_gen_confirm_and_enable' => 'Confirmar y Activar',
'mfa_gen_backup_codes_title' => 'Configuración de Códigos de Respaldo',
'mfa_gen_backup_codes_desc' => 'Guarda la siguiente lista de códigos en un lugar seguro. Al acceder al sistema podrás usar uno de los códigos como un segundo mecanismo de autenticación.',
'mfa_gen_backup_codes_desc' => 'Guarde la siguiente lista de códigos en un lugar seguro. Al acceder al sistema podrá usar uno de los códigos como un segundo mecanismo de autenticación.',
'mfa_gen_backup_codes_download' => 'Descargar Códigos',
'mfa_gen_backup_codes_usage_warning' => 'Cada código sólo puede utilizarse una vez',
'mfa_gen_backup_codes_usage_warning' => 'Cada código puede utilizarse sólo una vez',
'mfa_gen_totp_title' => 'Configuración de Aplicación móvil',
'mfa_gen_totp_desc' => 'Para utilizar la autenticación de dos pasos necesitarás una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.',
'mfa_gen_totp_desc' => 'Para utilizar la autenticación en dos pasos necesitará una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.',
'mfa_gen_totp_scan' => 'Escanea el código QR mostrado a continuación usando tu aplicación de autenticación preferida para empezar.',
'mfa_gen_totp_verify_setup' => 'Verificar Configuración',
'mfa_gen_totp_verify_setup_desc' => 'Verifica que todo está funcionando introduciendo un código, generado en tu aplicación de autenticación, en el campo de texto a continuación:',
'mfa_gen_totp_provide_code_here' => 'Introduce aquí tu código generado por la aplicación',
'mfa_verify_access' => 'Verificar Acceso',
'mfa_verify_access_desc' => 'Tu cuenta de usuario requiere que confirmes tu identidad a través de un nivel adicional de verificación antes de que te conceda el acceso. Verifica tu identidad usando uno de los métodos configurados para continuar.',
'mfa_verify_access_desc' => 'Su cuenta de usuario requiere que confirme su identidad a través de un nivel adicional de verificación antes de que se le conceda el acceso. Verifique su identidad usando uno de los métodos configurados para continuar.',
'mfa_verify_no_methods' => 'No hay Métodos Configurados',
'mfa_verify_no_methods_desc' => 'No se han encontrado métodos de autenticación de dos pasos para tu cuenta. Tendrás que configurar al menos un método antes de obtener acceso.',
'mfa_verify_no_methods_desc' => 'No se han encontrado métodos de autenticación de múltiples factores para su cuenta. Tendrá que configurar al menos un método antes de obtener acceso.',
'mfa_verify_use_totp' => 'Verificar usando una aplicación móvil',
'mfa_verify_use_backup_codes' => 'Verificar usando un código de respaldo',
'mfa_verify_backup_code' => 'Códigos de Respaldo',
'mfa_verify_backup_code' => 'Código de Respaldo',
'mfa_verify_backup_code_desc' => 'Introduzca uno de sus códigos de respaldo restantes a continuación:',
'mfa_verify_backup_code_enter_here' => 'Introduce el código de respaldo aquí',
'mfa_verify_totp_desc' => 'Introduzca el código, generado con tu aplicación móvil, a continuación:',
'mfa_setup_login_notification' => 'Método de dos factores configurado. Por favor, inicia sesión de nuevo utilizando el método configurado.',
'mfa_verify_backup_code_enter_here' => 'Introduzca el código de respaldo aquí',
'mfa_verify_totp_desc' => 'A continuación, introduzca el código generado con su aplicación móvil:',
'mfa_setup_login_notification' => 'Método de dos factores configurado. Por favor, inicie sesión nuevamente utilizando el método configurado.',
];

View File

@ -234,7 +234,7 @@ return [
'pages_initial_name' => 'Página nueva',
'pages_editing_draft_notification' => 'Usted está actualmente editando un borrador que fue guardado por última vez el :timeDiff.',
'pages_draft_edited_notification' => 'Esta página ha sido actualizada desde aquel momento. Se recomienda que cancele este borrador.',
'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.',
'pages_draft_page_changed_since_creation' => 'Esta página fue actualizada desde que se creó este borrador. Se recomienda descartar este borrador o tener cuidado de no sobrescribir ningún cambio en la página.',
'pages_draft_edit_active' => [
'start_a' => ':count usuarios han comenzado a editar esta página',
'start_b' => ':userName ha comenzado a editar esta página',

View File

@ -92,7 +92,7 @@ return [
'recycle_bin' => 'Papelera de Reciclaje',
'recycle_bin_desc' => 'Aquí puede restaurar elementos que hayan sido eliminados o elegir eliminarlos permanentemente del sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
'recycle_bin_deleted_item' => 'Elemento Eliminado',
'recycle_bin_deleted_parent' => 'Superior',
'recycle_bin_deleted_parent' => 'Padre',
'recycle_bin_deleted_by' => 'Eliminado por',
'recycle_bin_deleted_at' => 'Fecha de eliminación',
'recycle_bin_permanently_delete' => 'Eliminar permanentemente',
@ -105,7 +105,7 @@ return [
'recycle_bin_restore_list' => 'Elementos a restaurar',
'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
'recycle_bin_restore_parent' => 'Restaurar Superior',
'recycle_bin_restore_parent' => 'Restaurar Padre',
'recycle_bin_destroy_notification' => 'Eliminados :count elementos de la papelera de reciclaje.',
'recycle_bin_restore_notification' => 'Restaurados :count elementos desde la papelera de reciclaje.',
@ -119,7 +119,7 @@ return [
'audit_table_user' => 'Usuario',
'audit_table_event' => 'Evento',
'audit_table_related' => 'Elemento o detalle relacionados',
'audit_table_ip' => 'IP Address',
'audit_table_ip' => 'Dirección IP',
'audit_table_date' => 'Fecha de la Actividad',
'audit_date_from' => 'Inicio del Rango de Fecha',
'audit_date_to' => 'Final del Rango de Fecha',
@ -139,7 +139,7 @@ return [
'role_details' => 'Detalles de rol',
'role_name' => 'Nombre de rol',
'role_desc' => 'Descripción corta de rol',
'role_mfa_enforced' => 'Requiere Autenticación en Dos Pasos',
'role_mfa_enforced' => 'Requiere autenticación de múltiples factores',
'role_external_auth_id' => 'IDs de Autenticación Externa',
'role_system' => 'Permisos de sistema',
'role_manage_users' => 'Gestionar usuarios',
@ -208,8 +208,8 @@ return [
'users_api_tokens_create' => 'Crear token',
'users_api_tokens_expires' => 'Expira',
'users_api_tokens_docs' => 'Documentación API',
'users_mfa' => 'Autenticación en Dos Pasos',
'users_mfa_desc' => 'La autenticación en dos pasos añade una capa de seguridad adicional a tu cuenta.',
'users_mfa' => 'Autenticación de múltiples factores',
'users_mfa_desc' => 'Configure la autenticación de múltiples factores como una capa extra de seguridad para su cuenta de usuario.',
'users_mfa_x_methods' => ':count método configurado|:count métodos configurados',
'users_mfa_configure' => 'Configurar Métodos',

View File

@ -15,7 +15,7 @@ return [
'alpha_dash' => 'El :attribute solo puede contener letras, números y guiones.',
'alpha_num' => 'El :attribute solo puede contener letras y número.',
'array' => 'El :attribute debe de ser un array.',
'backup_codes' => 'El código suministrado no es válido o ya ha sido utilizado.',
'backup_codes' => 'El código suministrado no es válido o ya fue utilizado.',
'before' => 'El :attribute debe ser una fecha anterior a :date.',
'between' => [
'numeric' => 'El :attribute debe estar entre :min y :max.',
@ -99,7 +99,7 @@ return [
],
'string' => 'El atributo :attribute debe ser una cadena.',
'timezone' => 'El atributo :attribute debe ser una zona válida.',
'totp' => 'El código suministrado no es válido o ya ha expirado.',
'totp' => 'El código suministrado no es válido o ya expiró.',
'unique' => 'El atributo :attribute ya ha sido tomado.',
'url' => 'El atributo :attribute tiene un formato inválido.',
'uploaded' => 'El archivo no se pudo subir. Puede ser que el servidor no acepte archivos de este tamaño.',

View File

@ -0,0 +1,146 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Error: {{ $error }}</title>
<style>
html, body {
background-color: #F2F2F2;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
html {
padding: 0;
}
body {
margin: 0;
border-top: 6px solid #206ea7;
}
h1 {
margin-top: 0;
}
h2 {
color: #666;
font-size: 1rem;
margin-bottom: 0;
}
.container {
max-width: 800px;
margin: 1rem auto;
}
.panel {
background-color: #FFF;
border-radius: 3px;
box-shadow: 0 1px 6px -1px rgba(0, 0, 0, 0.1);
padding: 1rem 2rem;
margin: 2rem 1rem;
}
.panel-title {
font-weight: bold;
font-size: 1rem;
color: #FFF;
margin-top: 0;
margin-bottom: 0;
background-color: #206ea7;
padding: 0.25rem .5rem;
display: inline-block;
border-radius: 3px;
}
pre {
overflow-x: scroll;
background-color: #EEE;
border: 1px solid #DDD;
padding: .25rem;
border-radius: 3px;
}
a {
color: #206ea7;
text-decoration: none;
}
a:hover, a:focus {
text-decoration: underline;
color: #105282;
}
ul {
margin-left: 0;
padding-left: 1rem;
}
li {
margin-bottom: .4rem;
}
.notice {
margin-top: 2rem;
padding: 0 2rem;
font-weight: bold;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<p class="notice">
WARNING: Application is in debug mode. This mode has the potential to leak confidential
information and therefore should not be used in production or publicly
accessible environments.
</p>
<div class="panel">
<h4 class="panel-title">Error</h4>
<h2>{{ $errorClass }}</h2>
<h1>{{ $error }}</h1>
</div>
<div class="panel">
<h4 class="panel-title">Help Resources</h4>
<ul>
<li>
<a href="https://www.bookstackapp.com/docs/admin/debugging/" target="_blank">Review BookStack debugging documentation &raquo;</a>
</li>
<li>
<a href="https://github.com/BookStackApp/BookStack/releases" target="_blank">Ensure your instance is up-to-date &raquo;</a>
</li>
<li>
<a href="https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue+{{ urlencode($error) }}" target="_blank">Search for the issue on GitHub &raquo;</a>
</li>
<li>
<a href="https://discord.gg/ztkBqR2" target="_blank">Ask for help via Discord &raquo;</a>
</li>
<li>
<a href="https://duckduckgo.com/?q={{urlencode("BookStack {$error}")}}" target="_blank">Search the error message &raquo;</a>
</li>
</ul>
</div>
<div class="panel">
<h4 class="panel-title">Environment</h4>
<ul>
@foreach($environment as $label => $text)
<li><strong>{{ $label }}:</strong> {{ $text }}</li>
@endforeach
</ul>
</div>
<div class="panel">
<h4 class="panel-title">Stack Trace</h4>
<pre>{{ $trace }}</pre>
</div>
</div>
</body>
</html>

View File

@ -282,6 +282,22 @@ class AuthTest extends TestCase
->assertElementContains('a', 'Sign up');
}
public function test_reset_password_request_is_throttled()
{
$editor = $this->getEditor();
Notification::fake();
$this->get('/password/email');
$this->followingRedirects()->post('/password/email', [
'email' => $editor->email,
]);
$resp = $this->followingRedirects()->post('/password/email', [
'email' => $editor->email,
]);
Notification::assertTimesSent(1, ResetPassword::class);
$resp->assertSee('A password reset link will be sent to ' . $editor->email . ' if that email address is found in the system.');
}
public function test_login_redirects_to_initially_requested_url_correctly()
{
config()->set('app.url', 'http://localhost');

View File

@ -4,6 +4,7 @@ namespace Tests\Auth;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\Mfa\MfaValue;
use BookStack\Auth\Role;
use BookStack\Auth\User;
use PragmaRX\Google2FA\Google2FA;
use Tests\TestCase;
@ -164,4 +165,22 @@ class MfaConfigurationTest extends TestCase
$this->assertActivityExists(ActivityType::MFA_REMOVE_METHOD);
$this->assertEquals(0, $admin->mfaValues()->count());
}
public function test_totp_setup_url_shows_correct_user_when_setup_forced_upon_login()
{
$admin = $this->getAdmin();
/** @var Role $role */
$role = $admin->roles()->first();
$role->mfa_enforced = true;
$role->save();
$resp = $this->post('/login', ['email' => $admin->email, 'password' => 'password']);
$this->assertFalse(auth()->check());
$resp->assertRedirect('/mfa/verify');
$resp = $this->get('/mfa/totp/generate');
$resp->assertSeeText('Mobile App Setup');
$resp->assertDontSee('otpauth://totp/BookStack:guest%40example.com');
$resp->assertSee('otpauth://totp/BookStack:admin%40admin.com');
}
}

53
tests/DebugViewTest.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace Tests;
use BookStack\Auth\Access\SocialAuthService;
class DebugViewTest extends TestCase
{
public function test_debug_view_shows_expected_details()
{
config()->set('app.debug', true);
$resp = $this->getDebugViewForException(new \InvalidArgumentException('An error occurred during testing'));
// Error message
$resp->assertSeeText('An error occurred during testing');
// Exception Class
$resp->assertSeeText('InvalidArgumentException');
// Stack trace
$resp->assertSeeText('#0');
$resp->assertSeeText('#1');
// Warning message
$resp->assertSeeText('WARNING: Application is in debug mode. This mode has the potential to leak');
// PHP version
$resp->assertSeeText('PHP Version: ' . phpversion());
// BookStack version
$resp->assertSeeText('BookStack Version: ' . trim(file_get_contents(base_path('version'))));
// Dynamic help links
$resp->assertElementExists('a[href*="q=' . urlencode('BookStack An error occurred during testing') . '"]');
$resp->assertElementExists('a[href*="?q=is%3Aissue+' . urlencode('An error occurred during testing') . '"]');
}
public function test_debug_view_only_shows_when_debug_mode_is_enabled()
{
config()->set('app.debug', true);
$resp = $this->getDebugViewForException(new \InvalidArgumentException('An error occurred during testing'));
$resp->assertSeeText('Stack Trace');
$resp->assertDontSeeText('An unknown error occurred');
config()->set('app.debug', false);
$resp = $this->getDebugViewForException(new \InvalidArgumentException('An error occurred during testing'));
$resp->assertDontSeeText('Stack Trace');
$resp->assertSeeText('An unknown error occurred');
}
protected function getDebugViewForException(\Exception $exception): TestResponse
{
// Fake an error via social auth service used on login page
$mockService = $this->mock(SocialAuthService::class);
$mockService->shouldReceive('getActiveDrivers')->andThrow($exception);
return $this->get('/login');
}
}