Put back google social login
This commit is contained in:
parent
89fb347806
commit
52f192f622
@ -24,6 +24,6 @@ class CollectionOrNull implements CastsAttributes
|
|||||||
*/
|
*/
|
||||||
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||||
{
|
{
|
||||||
return $value->count() ? json_encode($value) : NULL;
|
return count($value) ? json_encode($value) : NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,10 +3,13 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Laravel\Socialite\Facades\Socialite;
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Mail\SocialLink;
|
||||||
use App\Models\{ProviderOauth,ProviderToken,User,UserOauth};
|
use App\Models\{ProviderOauth,ProviderToken,User,UserOauth};
|
||||||
|
|
||||||
class SocialLoginController extends Controller
|
class SocialLoginController extends Controller
|
||||||
@ -22,54 +25,58 @@ class SocialLoginController extends Controller
|
|||||||
$openiduser = Socialite::with($provider)->user();
|
$openiduser = Socialite::with($provider)->user();
|
||||||
|
|
||||||
if (! $openiduser)
|
if (! $openiduser)
|
||||||
return redirect('/home')->with('error','No user details obtained.');
|
return redirect('/home')
|
||||||
|
->with('error','No user details obtained.');
|
||||||
|
|
||||||
$oo = ProviderOauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]);
|
$oo = ProviderOauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]);
|
||||||
|
|
||||||
// See if this user has connected and linked previously
|
// See if this user has connected and linked previously
|
||||||
$aoo = $oo->users->where('userid',$openiduser->id);
|
$aoo = $oo->users->where('userid',$openiduser->id);
|
||||||
|
|
||||||
if ($aoo->count() == 1) {
|
if ($aoo->count() === 1) {
|
||||||
$aoo = $aoo->first();
|
$aoo = $aoo->first();
|
||||||
|
|
||||||
if ((is_null($user=$aoo->user) AND (is_null($aoo->account) OR is_null($user=$aoo->account->user))) OR ! $user->active) {
|
if ((is_null($user=$aoo->user) && (is_null($aoo->account) || is_null($user=$aoo->account->user))) || ! $user->active) {
|
||||||
if (! $user) {
|
if (! $user)
|
||||||
$user = User::where('email',$openiduser->email)->first();
|
$user = User::where('email',$openiduser->email)->first();
|
||||||
}
|
|
||||||
|
|
||||||
if (! $user OR ! $user->active) {
|
if ((! $user) || (! $user->active))
|
||||||
return redirect('/login')->with('error','Invalid account, or account inactive, please contact an admin.');
|
return redirect('/login')
|
||||||
}
|
->with('error','Invalid account, or account inactive, please contact an admin.');
|
||||||
|
|
||||||
return $this->link($provider,$aoo,$user);
|
return $this->link($provider,$aoo,$user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All Set to login
|
// All Set to login
|
||||||
Auth::login($user,FALSE);
|
Auth::login($user);
|
||||||
|
|
||||||
// If there are too many users, then we have a problem
|
// If there are too many users, then we have a problem
|
||||||
} elseif ($aoo->count() > 1) {
|
} elseif ($aoo->count() > 1) {
|
||||||
return redirect('/login')->with('error','Seems you have multiple oauth IDs, please contact an admin.');
|
return redirect('/login')
|
||||||
|
->with('error','Seems you have multiple oauth IDs, please contact an admin.');
|
||||||
|
|
||||||
// User is using OAUTH for the first time.
|
// User is using OAUTH for the first time.
|
||||||
} else {
|
} else {
|
||||||
$uo = User::active()->where('email',$openiduser->email);
|
$uo = User::active()->where('email',$openiduser->email);
|
||||||
|
|
||||||
// See if their is an account with this email address
|
// See if their is an account with this email address
|
||||||
if ($uo->count() == 1) {
|
if ($uo->count() === 1) {
|
||||||
$aoo = new UserOauth;
|
$aoo = new UserOauth;
|
||||||
$aoo->userid = $openiduser->id;
|
$aoo->userid = $openiduser->id;
|
||||||
$aoo->oauth_data = $openiduser->user;
|
$aoo->oauth_data = $openiduser->user;
|
||||||
|
|
||||||
$oo->users()->save($aoo);
|
$oo->users()->save($aoo);
|
||||||
|
|
||||||
return $this->link($provider,$aoo,$uo->first());
|
return $this->link($provider,$aoo,$uo->first());
|
||||||
|
|
||||||
// If there are too many users, then we have a problem
|
// If there are too many users, then we have a problem
|
||||||
} elseif ($uo->count() > 1) {
|
} elseif ($uo->count() > 1) {
|
||||||
return redirect('/login')->with('error','Seems you have multiple accounts, please contact an admin.');
|
return redirect('/login')
|
||||||
|
->with('error','Seems you have multiple accounts, please contact an admin.');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return redirect('/login')->with('error','Seems you dont have an account with that email, please contact an admin.');
|
return redirect('/login')
|
||||||
|
->with('error','Seems you dont have an account with that email, please contact an admin.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +89,8 @@ class SocialLoginController extends Controller
|
|||||||
$openiduser = Socialite::with($provider)->user();
|
$openiduser = Socialite::with($provider)->user();
|
||||||
|
|
||||||
if (! $openiduser)
|
if (! $openiduser)
|
||||||
return redirect('/home')->with('error','No user details obtained.');
|
return redirect('/home')
|
||||||
|
->with('error','No user details obtained.');
|
||||||
|
|
||||||
$po = ProviderOauth::where('name',$provider)->singleOrFail();
|
$po = ProviderOauth::where('name',$provider)->singleOrFail();
|
||||||
|
|
||||||
@ -101,4 +109,48 @@ class SocialLoginController extends Controller
|
|||||||
->intended('/home')
|
->intended('/home')
|
||||||
->with('success','Token refreshed.');
|
->with('success','Token refreshed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have identified the user and oauth, just need them to confirm the link
|
||||||
|
*
|
||||||
|
* @param $provider
|
||||||
|
* @param UserOauth $ao
|
||||||
|
* @param User $uo
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function link($provider,UserOauth $ao,User $uo): \Illuminate\View\View
|
||||||
|
{
|
||||||
|
// @note If this is sent now (send()), it results in the caller to be executed a second time (handleProviderCallback()).
|
||||||
|
Mail::to($uo->email)->queue(new SocialLink($ao));
|
||||||
|
|
||||||
|
return view('auth.social_link')
|
||||||
|
->with('oauthid',$ao->id)
|
||||||
|
->with('provider',$provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function linkcomplete(Request $request,$provider)
|
||||||
|
{
|
||||||
|
// Load our oauth id
|
||||||
|
$aoo = UserOauth::findOrFail($request->post('oauthid'));
|
||||||
|
|
||||||
|
// Check our email matches
|
||||||
|
if (Arr::get($aoo->oauth_data,'email','invalid') !== $request->post('email'))
|
||||||
|
return redirect('/login')
|
||||||
|
->with('error','Account details didnt match to make link.');
|
||||||
|
|
||||||
|
// Check our token matches
|
||||||
|
if ($aoo->link_token !== $request->post('token'))
|
||||||
|
return redirect('/login')
|
||||||
|
->with('error','Token details didnt match to make link.');
|
||||||
|
|
||||||
|
// Load our email.
|
||||||
|
$uo = User::where('email',$request->post('email'))->firstOrFail();
|
||||||
|
|
||||||
|
$aoo->user_id = $uo->id;
|
||||||
|
$aoo->save();
|
||||||
|
Auth::login($uo);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->intended('/home');
|
||||||
|
}
|
||||||
}
|
}
|
48
app/Mail/SocialLink.php
Normal file
48
app/Mail/SocialLink.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{Site,User,UserOauth};
|
||||||
|
|
||||||
|
class SocialLink extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public string $token;
|
||||||
|
public Site $site;
|
||||||
|
public ?User $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @param UserOauth $o
|
||||||
|
*/
|
||||||
|
public function __construct(UserOauth $o)
|
||||||
|
{
|
||||||
|
$this->site = $o->site;
|
||||||
|
$this->token = $o->link_token;
|
||||||
|
$this->user = $o->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
Config::set('site',$this->site);
|
||||||
|
|
||||||
|
return $this
|
||||||
|
->markdown('email.system.social_link')
|
||||||
|
->subject('Link your Account')
|
||||||
|
->with([
|
||||||
|
'site'=>$this->site,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
216
config/auth.php
216
config/auth.php
@ -2,120 +2,128 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Authentication Defaults
|
| Authentication Defaults
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option defines the default authentication "guard" and password
|
| This option defines the default authentication "guard" and password
|
||||||
| reset "broker" for your application. You may change these values
|
| reset "broker" for your application. You may change these values
|
||||||
| as required, but they're a perfect start for most applications.
|
| as required, but they're a perfect start for most applications.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'defaults' => [
|
'defaults' => [
|
||||||
'guard' => env('AUTH_GUARD', 'web'),
|
'guard' => env('AUTH_GUARD', 'web'),
|
||||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Authentication Guards
|
| Authentication Guards
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Next, you may define every authentication guard for your application.
|
| Next, you may define every authentication guard for your application.
|
||||||
| Of course, a great default configuration has been defined for you
|
| Of course, a great default configuration has been defined for you
|
||||||
| which utilizes session storage plus the Eloquent user provider.
|
| which utilizes session storage plus the Eloquent user provider.
|
||||||
|
|
|
|
||||||
| All authentication guards have a user provider, which defines how the
|
| All authentication guards have a user provider, which defines how the
|
||||||
| users are actually retrieved out of your database or other storage
|
| users are actually retrieved out of your database or other storage
|
||||||
| system used by the application. Typically, Eloquent is utilized.
|
| system used by the application. Typically, Eloquent is utilized.
|
||||||
|
|
|
|
||||||
| Supported: "session"
|
| Supported: "session"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'guards' => [
|
'guards' => [
|
||||||
'web' => [
|
'web' => [
|
||||||
'driver' => 'session',
|
'driver' => 'session',
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
'driver' => 'passport',
|
'driver' => 'passport',
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| User Providers
|
| User Providers
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| All authentication guards have a user provider, which defines how the
|
| All authentication guards have a user provider, which defines how the
|
||||||
| users are actually retrieved out of your database or other storage
|
| users are actually retrieved out of your database or other storage
|
||||||
| system used by the application. Typically, Eloquent is utilized.
|
| system used by the application. Typically, Eloquent is utilized.
|
||||||
|
|
|
|
||||||
| If you have multiple user tables or models you may configure multiple
|
| If you have multiple user tables or models you may configure multiple
|
||||||
| providers to represent the model / table. These providers may then
|
| providers to represent the model / table. These providers may then
|
||||||
| be assigned to any extra authentication guards you have defined.
|
| be assigned to any extra authentication guards you have defined.
|
||||||
|
|
|
|
||||||
| Supported: "database", "eloquent"
|
| Supported: "database", "eloquent"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'providers' => [
|
'providers' => [
|
||||||
'users' => [
|
'users' => [
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||||
],
|
],
|
||||||
|
|
||||||
// 'users' => [
|
// 'users' => [
|
||||||
// 'driver' => 'database',
|
// 'driver' => 'database',
|
||||||
// 'table' => 'users',
|
// 'table' => 'users',
|
||||||
// ],
|
// ],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Resetting Passwords
|
| Resetting Passwords
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| These configuration options specify the behavior of Laravel's password
|
| These configuration options specify the behavior of Laravel's password
|
||||||
| reset functionality, including the table utilized for token storage
|
| reset functionality, including the table utilized for token storage
|
||||||
| and the user provider that is invoked to actually retrieve users.
|
| and the user provider that is invoked to actually retrieve users.
|
||||||
|
|
|
|
||||||
| The expiry time is the number of minutes that each reset token will be
|
| The expiry time is the number of minutes that each reset token will be
|
||||||
| considered valid. This security feature keeps tokens short-lived so
|
| considered valid. This security feature keeps tokens short-lived so
|
||||||
| they have less time to be guessed. You may change this as needed.
|
| they have less time to be guessed. You may change this as needed.
|
||||||
|
|
|
|
||||||
| The throttle setting is the number of seconds a user must wait before
|
| The throttle setting is the number of seconds a user must wait before
|
||||||
| generating more password reset tokens. This prevents the user from
|
| generating more password reset tokens. This prevents the user from
|
||||||
| quickly generating a very large amount of password reset tokens.
|
| quickly generating a very large amount of password reset tokens.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'passwords' => [
|
'passwords' => [
|
||||||
'users' => [
|
'users' => [
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||||
'expire' => 60,
|
'expire' => 60,
|
||||||
'throttle' => 60,
|
'throttle' => 60,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Password Confirmation Timeout
|
| Password Confirmation Timeout
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may define the amount of seconds before a password confirmation
|
| Here you may define the amount of seconds before a password confirmation
|
||||||
| window expires and users are asked to re-enter their password via the
|
| window expires and users are asked to re-enter their password via the
|
||||||
| confirmation screen. By default, the timeout lasts for three hours.
|
| confirmation screen. By default, the timeout lasts for three hours.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||||
|
|
||||||
];
|
'social' => [
|
||||||
|
'google' => [
|
||||||
|
'name' => 'Google',
|
||||||
|
'id' => 'google',
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'icon' => 'fab fa-google',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
@ -42,6 +42,12 @@ return [
|
|||||||
'guid' => env('EZYPAY_GUID'),
|
'guid' => env('EZYPAY_GUID'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'google' => [
|
||||||
|
'client_id' => env('AUTH_GOOGLE_CLIENT_ID'),
|
||||||
|
'client_secret' => env('AUTH_GOOGLE_SECRET'),
|
||||||
|
'redirect' => '/auth/google/callback',
|
||||||
|
],
|
||||||
|
|
||||||
'provider' => [
|
'provider' => [
|
||||||
'intuit' => [
|
'intuit' => [
|
||||||
'api'=> \Intuit\API::class,
|
'api'=> \Intuit\API::class,
|
||||||
|
21
resources/views/email/system/social_link.blade.php
Normal file
21
resources/views/email/system/social_link.blade.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
@component('mail::message',['site'=>$site,'heading'=>'Link Your Account'])
|
||||||
|
Hi {{ isset($user) ? $user->full_name.',' : '' }}
|
||||||
|
|
||||||
|
A request was made to link your account to a social login.
|
||||||
|
If you didnt make this request, you can ignore this, and the request will be ignored.
|
||||||
|
If you did make the request, then please enter the code displayed below.
|
||||||
|
|
||||||
|
@component('mail::panel')
|
||||||
|
{{ $token }}
|
||||||
|
@endcomponent
|
||||||
|
|
||||||
|
Once you've keyed in this code, you'll be able to login to your account using your social login instead of a username and a password.
|
||||||
|
|
||||||
|
Thanks,
|
||||||
|
|
||||||
|
{{ config('mail.from.name') }}
|
||||||
|
|
||||||
|
@component('mail::subcopy')
|
||||||
|
If you didnt make this request, you can safely ignore this email - no change was made to your account, nor was it accessed by an unauthorised person.
|
||||||
|
@endcomponent
|
||||||
|
@endcomponent
|
@ -0,0 +1,80 @@
|
|||||||
|
@extends('adminlte::layouts.auth')
|
||||||
|
|
||||||
|
@section('htmlheader_title')
|
||||||
|
Link Account
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-logo">
|
||||||
|
<a>{!! config('app.name_html_long') !!}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>NOTE:</strong> Link your account.<br><br>
|
||||||
|
<ul>
|
||||||
|
<li>An email has been sent to you with a token, please use those details here:</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (count($errors) > 0)
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>Whoops!</strong> {{ trans('adminlte_lang::message.someproblems') }}<br><br>
|
||||||
|
<ul>
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- /.login-logo -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body login-card-body">
|
||||||
|
<p class="login-box-msg">Link your account</p>
|
||||||
|
|
||||||
|
<form method="post" action="{{ url('/auth/'.$provider.'/linkcomplete') }}">
|
||||||
|
{{ csrf_field() }}
|
||||||
|
<input type="hidden" name="oauthid" value="{{ $oauthid }}">
|
||||||
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="email" name="email" class="form-control" placeholder="Email">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="fa fa-envelope input-group-text"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" name="token" class="form-control" placeholder="Token">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="fa fa-lock input-group-text"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
<div class="col-4">
|
||||||
|
<button type="submit" name="submit" class="btn btn-primary btn-block btn-flat">Link</button>
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p class="mb-1">
|
||||||
|
<a name="reset" href="{{ url('/password/reset') }}">{{ trans('adminlte_lang::message.forgotpassword') }}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@isset($register)
|
||||||
|
<p class="mb-0">
|
||||||
|
<a href="register.html" class="text-center">Register a new account</a>
|
||||||
|
</p>
|
||||||
|
@endisset
|
||||||
|
</div>
|
||||||
|
<!-- /.login-card-body -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.login-box -->
|
||||||
|
@endsection
|
Loading…
Reference in New Issue
Block a user