Pārlūkot izejas kodu

Add an About view - Close #91

Bubka 3 gadi atpakaļ
vecāks
revīzija
2fa2cf8c99

+ 63 - 0
app/Http/Controllers/SystemController.php

@@ -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 - 0
public/logo.svg

@@ -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>

+ 8 - 0
resources/js/packages/fontawesome.js

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

+ 2 - 0
resources/js/routes.js

@@ -27,6 +27,7 @@ import SettingsWebAuthn from './views/settings/WebAuthn'
 import EditCredential   from './views/settings/Credentials/Edit'
 import GeneratePAT      from './views/settings/PATokens/Create'
 import Errors           from './views/Error'
+import About            from './views/About'
 
 const router = new Router({
     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/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: '/error', name: 'genericError',component: Errors, props: true },
         { path: '/404', name: '404',component: Errors, props: true },

+ 118 - 0
resources/js/views/About.vue

@@ -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>

+ 5 - 1
resources/lang/en/commons.php

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

+ 1 - 0
resources/lang/en/settings.php

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

+ 1 - 0
resources/lang/en/titles.php

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

+ 1 - 0
routes/web.php

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