1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-07-31 15:24:31 +03:00

Merge pull request #2827 from BookStackApp/mfa

MFA System
This commit is contained in:
Dan Brown
2021-08-21 15:47:55 +01:00
committed by GitHub
69 changed files with 2292 additions and 274 deletions

View File

@ -17,8 +17,8 @@
{!! csrf_field() !!}
<div class="form-group">
<label for="email">{{ trans('auth.email') }}</label>
@if(auth()->check())
@include('form.text', ['name' => 'email', 'model' => auth()->user()])
@if($user)
@include('form.text', ['name' => 'email', 'model' => $user])
@else
@include('form.text', ['name' => 'email'])
@endif

View File

@ -0,0 +1,36 @@
@extends('simple-layout')
@section('body')
<div class="container very-small py-xl">
<div class="card content-wrap auto-height">
<h1 class="list-heading">{{ trans('auth.mfa_gen_backup_codes_title') }}</h1>
<p>{{ trans('auth.mfa_gen_backup_codes_desc') }}</p>
<div class="text-center mb-xs">
<div class="text-bigger code-base p-m" style="column-count: 2">
@foreach($codes as $code)
{{ $code }} <br>
@endforeach
</div>
</div>
<p class="text-right">
<a href="{{ $downloadUrl }}" download="backup-codes.txt" class="button outline small">{{ trans('auth.mfa_gen_backup_codes_download') }}</a>
</p>
<p class="callout warning">
{{ trans('auth.mfa_gen_backup_codes_usage_warning') }}
</p>
<form action="{{ url('/mfa/backup_codes/confirm') }}" method="POST">
{{ csrf_field() }}
<div class="mt-s text-right">
<a href="{{ url('/mfa/setup') }}" class="button outline">{{ trans('common.cancel') }}</a>
<button class="button">{{ trans('auth.mfa_gen_confirm_and_enable') }}</button>
</div>
</form>
</div>
</div>
@stop

View File

@ -0,0 +1,30 @@
<div class="grid half gap-xl">
<div>
<div class="setting-list-label">{{ trans('auth.mfa_option_' . $method . '_title') }}</div>
<p class="small">
{{ trans('auth.mfa_option_' . $method . '_desc') }}
</p>
</div>
<div class="pt-m">
@if($userMethods->has($method))
<div class="text-pos">
@icon('check-circle')
{{ trans('auth.mfa_setup_configured') }}
</div>
<a href="{{ url('/mfa/' . $method . '/generate') }}" class="button outline small">{{ trans('auth.mfa_setup_reconfigure') }}</a>
<div component="dropdown" class="inline relative">
<button type="button" refs="dropdown@toggle" class="button outline small">{{ trans('common.remove') }}</button>
<div refs="dropdown@menu" class="dropdown-menu">
<p class="text-neg small px-m mb-xs">{{ trans('auth.mfa_setup_remove_confirmation') }}</p>
<form action="{{ url('/mfa/' . $method . '/remove') }}" method="post">
{{ csrf_field() }}
{{ method_field('delete') }}
<button class="text-primary small delete">{{ trans('common.confirm') }}</button>
</form>
</div>
</div>
@else
<a href="{{ url('/mfa/' . $method . '/generate') }}" class="button outline">{{ trans('auth.mfa_setup_action') }}</a>
@endif
</div>
</div>

View File

@ -0,0 +1,18 @@
@extends('simple-layout')
@section('body')
<div class="container small py-xl">
<div class="card content-wrap auto-height">
<h1 class="list-heading">{{ trans('auth.mfa_setup') }}</h1>
<p class="mb-none"> {{ trans('auth.mfa_setup_desc') }}</p>
<div class="setting-list">
@foreach(['totp', 'backup_codes'] as $method)
@include('mfa.setup-method-row', ['method' => $method])
@endforeach
</div>
</div>
</div>
@stop

View File

@ -0,0 +1,37 @@
@extends('simple-layout')
@section('body')
<div class="container very-small py-xl">
<div class="card content-wrap auto-height">
<h1 class="list-heading">{{ trans('auth.mfa_gen_totp_title') }}</h1>
<p>{{ trans('auth.mfa_gen_totp_desc') }}</p>
<p>{{ trans('auth.mfa_gen_totp_scan') }}</p>
<div class="text-center">
<div class="block inline">
{!! $svg !!}
</div>
</div>
<h2 class="list-heading">{{ trans('auth.mfa_gen_totp_verify_setup') }}</h2>
<p id="totp-verify-input-details" class="mb-s">{{ trans('auth.mfa_gen_totp_verify_setup_desc') }}</p>
<form action="{{ url('/mfa/totp/confirm') }}" method="POST">
{{ csrf_field() }}
<input type="text"
name="code"
aria-labelledby="totp-verify-input-details"
placeholder="{{ trans('auth.mfa_gen_totp_provide_code_here') }}"
class="input-fill-width {{ $errors->has('code') ? 'neg' : '' }}">
@if($errors->has('code'))
<div class="text-neg text-small px-xs">{{ $errors->first('code') }}</div>
@endif
<div class="mt-s text-right">
<a href="{{ url('/mfa/setup') }}" class="button outline">{{ trans('common.cancel') }}</a>
<button class="button">{{ trans('auth.mfa_gen_confirm_and_enable') }}</button>
</div>
</form>
</div>
</div>
@stop

View File

@ -0,0 +1,35 @@
@extends('simple-layout')
@section('body')
<div class="container very-small py-xl">
<div class="card content-wrap auto-height">
<h1 class="list-heading">{{ trans('auth.mfa_verify_access') }}</h1>
<p class="mb-none">{{ trans('auth.mfa_verify_access_desc') }}</p>
@if(!$method)
<hr class="my-l">
<h5>{{ trans('auth.mfa_verify_no_methods') }}</h5>
<p class="small">{{ trans('auth.mfa_verify_no_methods_desc') }}</p>
<div>
<a href="{{ url('/mfa/setup') }}" class="button outline">{{ trans('common.configure') }}</a>
</div>
@endif
@if($method)
<hr class="my-l">
@include('mfa.verify.' . $method)
@endif
@if(count($otherMethods) > 0)
<hr class="my-l">
@foreach($otherMethods as $otherMethod)
<div class="text-center">
<a href="{{ url("/mfa/verify?method={$otherMethod}") }}">{{ trans('auth.mfa_verify_use_' . $otherMethod) }}</a>
</div>
@endforeach
@endif
</div>
</div>
@stop

View File

@ -0,0 +1,17 @@
<div class="setting-list-label">{{ trans('auth.mfa_verify_backup_code') }}</div>
<p class="small mb-m">{{ trans('auth.mfa_verify_backup_code_desc') }}</p>
<form action="{{ url('/mfa/backup_codes/verify') }}" method="post">
{{ csrf_field() }}
<input type="text"
name="code"
placeholder="{{ trans('auth.mfa_verify_backup_code_enter_here') }}"
class="input-fill-width {{ $errors->has('code') ? 'neg' : '' }}">
@if($errors->has('code'))
<div class="text-neg text-small px-xs">{{ $errors->first('code') }}</div>
@endif
<div class="mt-s text-right">
<button class="button">{{ trans('common.confirm') }}</button>
</div>
</form>

View File

@ -0,0 +1,17 @@
<div class="setting-list-label">{{ trans('auth.mfa_option_totp_title') }}</div>
<p class="small mb-m">{{ trans('auth.mfa_verify_totp_desc') }}</p>
<form action="{{ url('/mfa/totp/verify') }}" method="post">
{{ csrf_field() }}
<input type="text"
name="code"
placeholder="{{ trans('auth.mfa_gen_totp_provide_code_here') }}"
class="input-fill-width {{ $errors->has('code') ? 'neg' : '' }}">
@if($errors->has('code'))
<div class="text-neg text-small px-xs">{{ $errors->first('code') }}</div>
@endif
<div class="mt-s text-right">
<button class="button">{{ trans('common.confirm') }}</button>
</div>
</form>

View File

@ -11,13 +11,16 @@
</div>
<div>
<div class="form-group">
<label for="name">{{ trans('settings.role_name') }}</label>
<label for="display_name">{{ trans('settings.role_name') }}</label>
@include('form.text', ['name' => 'display_name'])
</div>
<div class="form-group">
<label for="name">{{ trans('settings.role_desc') }}</label>
<label for="description">{{ trans('settings.role_desc') }}</label>
@include('form.text', ['name' => 'description'])
</div>
<div class="form-group">
@include('form.checkbox', ['name' => 'mfa_enforced', 'label' => trans('settings.role_mfa_enforced') ])
</div>
@if(config('auth.method') === 'ldap' || config('auth.method') === 'saml2')
<div class="form-group">

View File

@ -27,7 +27,12 @@
@foreach($roles as $role)
<tr>
<td><a href="{{ url("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td>
<td>{{ $role->description }}</td>
<td>
@if($role->mfa_enforced)
<span title="{{ trans('settings.role_mfa_enforced') }}">@icon('lock') </span>
@endif
{{ $role->description }}
</td>
<td class="text-center">{{ $role->users->count() }}</td>
</tr>
@endforeach

View File

@ -63,6 +63,27 @@
</form>
</section>
<section class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.users_mfa') }}</h2>
<p>{{ trans('settings.users_mfa_desc') }}</p>
<div class="grid half gap-xl v-center pb-s">
<div>
@if ($mfaMethods->count() > 0)
<span class="text-pos">@icon('check-circle')</span>
@else
<span class="text-neg">@icon('cancel')</span>
@endif
{{ trans_choice('settings.users_mfa_x_methods', $mfaMethods->count()) }}
</div>
<div class="text-m-right">
@if($user->id === user()->id)
<a href="{{ url('/mfa/setup') }}" class="button outline">{{ trans('settings.users_mfa_configure') }}</a>
@endif
</div>
</div>
</section>
@if(user()->id === $user->id && count($activeSocialDrivers) > 0)
<section class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.users_social_accounts') }}</h2>

View File

@ -43,7 +43,12 @@
<td class="text-center" style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
<td>
<a href="{{ url("/settings/users/{$user->id}") }}">
{{ $user->name }} <br> <span class="text-muted">{{ $user->email }}</span>
{{ $user->name }}
<br>
<span class="text-muted">{{ $user->email }}</span>
@if($user->mfa_values_count > 0)
<span title="MFA Configured" class="text-pos">@icon('lock')</span>
@endif
</a>
</td>
<td>