Add an About view - Close #91

This commit is contained in:
Bubka 2022-07-21 15:48:23 +02:00
parent 6e1d27e08c
commit 2fa2cf8c99
9 changed files with 288 additions and 1 deletions

View File

@ -0,0 +1,63 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Services\SettingService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class SystemController extends Controller
{
/**
* The Settings Service instance.
*/
protected SettingService $settingService;
/**
* Create a new controller instance.
*/
public function __construct(SettingService $settingService)
{
$this->settingService = $settingService;
}
/**
* Get detailed information about the current installation
*
* @return \Illuminate\Http\JsonResponse
*/
public function infos(Request $request)
{
$infos['Date'] = date(DATE_RFC2822);
$infos['userAgent'] = $request->header('user-agent');
// App info
$infos['Version'] = config('2fauth.version');
$infos['Environment'] = config('app.env');
$infos['Debug'] = var_export(config('app.debug'), true);
$infos['Cache driver'] = config('cache.default');
$infos['Log channel'] = config('logging.default');
$infos['Log level'] = env('LOG_LEVEL');
$infos['DB driver'] = DB::getDriverName();
// PHP info
$infos['PHP version'] = PHP_VERSION;
$infos['Operating system'] = PHP_OS;
$infos['interface'] = PHP_SAPI;
// Auth info
$infos['Auth guard'] = config('auth.defaults.guard');
if ($infos['Auth guard'] === 'reverse-proxy-guard') {
$infos['Auth proxy header for user'] = config('auth.auth_proxy_headers.user');
$infos['Auth proxy header for email'] = config('auth.auth_proxy_headers.email');
}
$infos['webauthn user verification'] = config('larapass.login_verify');
$infos['Trusted proxies'] = config('2fauth.trustedProxies') ?: 'none';
// User info
if ($request->user()) {
$infos['options'] = $this->settingService->all()->toArray();
}
return response()->json($infos);
}
}

89
public/logo.svg Normal file
View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="41.577202mm"
height="57.150055mm"
viewBox="0 0 41.577202 57.150055"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (1:1.1+202109281944+c3084ef5ed)"
sodipodi:docname="2fauth_light.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.25323113"
inkscape:cx="98.72404"
inkscape:cy="86.877155"
inkscape:window-width="1920"
inkscape:window-height="1019"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient12303">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop12299" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop12301" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12303"
id="linearGradient12305"
x1="78.171478"
y1="153.97881"
x2="101.66649"
y2="153.97881"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-78.171476,-125.40378)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:70.5556px;line-height:1.25;font-family:Oswald;-inkscape-font-specification:'Oswald, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="73.867584"
y="182.55382"
id="text3977-5"><tspan
sodipodi:role="line"
id="tspan3975-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:70.5556px;font-family:Oswald;-inkscape-font-specification:'Oswald, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="73.867584"
y="182.55382">F</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:62.8138px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28902"
x="-115.97259"
y="199.41467"
id="text6065-2"
transform="scale(-1.0923609,0.91544836)"><tspan
sodipodi:role="line"
id="tspan6063-1"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:62.8138px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;fill-opacity:1;stroke-width:0.28902"
x="-115.97259"
y="199.41467">F</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -27,6 +27,10 @@ import {
faTimesCircle, faTimesCircle,
faUpload, faUpload,
faGlobe, faGlobe,
faBook,
faFlask,
faCode,
faCopy,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { import {
@ -58,6 +62,10 @@ library.add(
faTimesCircle, faTimesCircle,
faUpload, faUpload,
faGlobe, faGlobe,
faBook,
faFlask,
faCode,
faCopy,
); );
Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.component('font-awesome-icon', FontAwesomeIcon)

View File

@ -27,6 +27,7 @@ import SettingsWebAuthn from './views/settings/WebAuthn'
import EditCredential from './views/settings/Credentials/Edit' import EditCredential from './views/settings/Credentials/Edit'
import GeneratePAT from './views/settings/PATokens/Create' import GeneratePAT from './views/settings/PATokens/Create'
import Errors from './views/Error' import Errors from './views/Error'
import About from './views/About'
const router = new Router({ const router = new Router({
mode: 'history', mode: 'history',
@ -59,6 +60,7 @@ const router = new Router({
{ path: '/webauthn/lost', name: 'webauthn.lost', component: WebauthnLost, meta: { disabledWithAuthProxy: true, showAbout: true } }, { path: '/webauthn/lost', name: 'webauthn.lost', component: WebauthnLost, meta: { disabledWithAuthProxy: true, showAbout: true } },
{ path: '/webauthn/recover', name: 'webauthn.recover', component: WebauthnRecover, meta: { disabledWithAuthProxy: true, showAbout: true } }, { path: '/webauthn/recover', name: 'webauthn.recover', component: WebauthnRecover, meta: { disabledWithAuthProxy: true, showAbout: true } },
{ path: '/about', name: 'about',component: About, meta: { showAbout: true } },
{ path: '/flooded', name: 'flooded',component: Errors, props: true }, { path: '/flooded', name: 'flooded',component: Errors, props: true },
{ path: '/error', name: 'genericError',component: Errors, props: true }, { path: '/error', name: 'genericError',component: Errors, props: true },
{ path: '/404', name: '404',component: Errors, props: true }, { path: '/404', name: '404',component: Errors, props: true },

View File

@ -0,0 +1,118 @@
<template>
<div class="columns is-centered">
<div class="form-column column is-two-thirds-tablet is-half-desktop is-one-third-widescreen is-one-third-fullhd">
<h1 class="title">{{ $t('commons.about') }}</h1>
<p class="block">
<span class="has-text-white"><span class="is-size-5">2FAuth</span> v{{ appVersion }}</span><br />
A web app to manage your Two-Factor Authentication (2FA) accounts and generate their security codes
</p>
<img src="logo.svg" style="height: 32px" alt="logo" />
<p class="block">
©Bubka <a class="is-size-7" href="https://github.com/Bubka/2FAuth/blob/master/LICENSE">AGPL-3.0 license</a>
</p>
<div class="buttons">
<a class="button is-dark" href="https://github.com/Bubka/2FAuth">
<span class="icon is-small">
<font-awesome-icon :icon="['fab', 'github-alt']" />
</span>
<span>Github</span>
</a>
<a class="button is-dark" href="https://docs.2fauth.app/">
<span class="icon is-small">
<font-awesome-icon :icon="['fas', 'book']" />
</span>
<span>Docs</span>
</a>
<a class="button is-dark" href="https://demo.2fauth.app/">
<span class="icon is-small">
<font-awesome-icon :icon="['fas', 'flask']" />
</span>
<span>Demo</span>
</a>
<a class="button is-dark" href="https://docs.2fauth.app/resources/rapidoc.html">
<span class="icon is-small">
<font-awesome-icon :icon="['fas', 'code']" />
</span>
<span>API</span>
</a>
</div>
<h2 class="title is-5 has-text-grey-light">
{{ $t('commons.credits') }}
</h2>
<p class="block">
<ul>
<li>Made with <a href="https://docs.2fauth.app/credits/">Laravel, Bulma CSS, Vue.js and more</a></li>
<li>UI Icons by <a href="https://fontawesome.com/">Font Awesome</a>&nbsp;<a class="is-size-7" href="https://fontawesome.com/license/free">(CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)</a></li>
<li>Logos by <a href="https://2fa.directory/">2FA Directory</a>&nbsp;<a class="is-size-7" href="https://github.com/2factorauth/twofactorauth/blob/master/LICENSE.md">(MIT License)</a></li>
</ul>
</p>
<h2 class="title is-5 has-text-grey-light">
{{ $t('commons.environment') }}
</h2>
<div class="box has-background-black-bis is-family-monospace is-size-7">
<span class="is-pulled-right is-clickable" v-clipboard="() => this.$refs.listInfos.innerText" v-clipboard:success="clipboardSuccessHandler">
<font-awesome-icon :icon="['fas', 'copy']" />
</span>
<ul ref="listInfos">
<li v-for="(value, key) in infos" :value="value" :key="key"><b>{{key}}</b>: {{value}}</li>
</ul>
</div>
<div v-if="showUserOptions">
<h2 class="title is-5 has-text-grey-light">
{{ $t('settings.user_options') }}
</h2>
<div class="box has-background-black-bis is-family-monospace is-size-7">
<span class="is-pulled-right is-clickable" v-clipboard="() => this.$refs.listUserOptions.innerText" v-clipboard:success="clipboardSuccessHandler">
<font-awesome-icon :icon="['fas', 'copy']" />
</span>
<ul ref="listUserOptions">
<li v-for="(value, option) in options" :value="value" :key="option"><b>{{option}}</b>: {{value}}</li>
</ul>
</div>
</div>
<!-- footer -->
<vue-footer :showButtons="true">
<!-- close button -->
<p class="control">
<router-link :to="{ name: 'accounts', params: { toRefresh: true } }" class="button is-dark is-rounded">{{ $t('commons.close') }}</router-link>
</p>
</vue-footer>
</div>
</div>
</template>
<script>
export default {
data() {
return {
infos : null,
options : null,
showUserOptions: false,
}
},
async mounted() {
await this.axios.get('infos').then(response => {
this.infos = response.data
if (response.data.options) {
this.options = response.data.options
delete this.infos.options
this.showUserOptions = true
}
})
},
methods: {
clipboardSuccessHandler ({ value, event }) {
this.$notify({ type: 'is-success', text: this.$t('commons.copied_to_clipboard') })
},
clipboardErrorHandler ({ value, event }) {
console.log('error', value)
},
}
}
</script>

View File

@ -44,5 +44,9 @@
'generate' => 'Generate', 'generate' => 'Generate',
'open_in_browser' => 'Open in browser', 'open_in_browser' => 'Open in browser',
'continue' => 'Continue', 'continue' => 'Continue',
'discard' => 'Discard' 'discard' => 'Discard',
'about' => 'About',
'usefull_links' => 'Usefull links',
'environment' => 'Environment',
'credits' => 'Credits',
]; ];

View File

@ -19,6 +19,7 @@
'webauthn' => 'WebAuthn', 'webauthn' => 'WebAuthn',
'tokens' => 'Tokens', 'tokens' => 'Tokens',
'options' => 'Options', 'options' => 'Options',
'user_options' => 'User options',
'confirm' => [ 'confirm' => [
], ],

View File

@ -45,4 +45,5 @@
'flooded' => 'Flood', 'flooded' => 'Flood',
'genericError' => 'Error', 'genericError' => 'Error',
'404' => 'Item not found', '404' => 'Item not found',
'about' => 'About',
]; ];

View File

@ -58,6 +58,7 @@
Route::get('refresh-csrf', function(){ Route::get('refresh-csrf', function(){
return csrf_token(); return csrf_token();
}); });
Route::get('infos', 'SystemController@infos')->name('system.infos');
/** /**
* Route for the main landing view * Route for the main landing view