big auth refactor
This commit is contained in:
parent
ac310dce99
commit
13090423d2
14 changed files with 150 additions and 159 deletions
|
@ -49,8 +49,7 @@ class AuthController extends Controller
|
|||
// Destroy the active session
|
||||
CurrentSession::stop();
|
||||
|
||||
// Return true indicating a successful logout
|
||||
return redirect(route('auth.login'));
|
||||
return redirect(route('main.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,16 +59,12 @@ class AuthController extends Controller
|
|||
public function login(): string
|
||||
{
|
||||
if (!session_check()) {
|
||||
return view('auth/login');
|
||||
return $this->json(['error' => 'Your session expired! Please refresh and try again.', 'a' => $_POST]);
|
||||
}
|
||||
|
||||
// Preliminarily set login to failed
|
||||
$redirect = route('auth.login');
|
||||
|
||||
// Get request variables
|
||||
$username = $_REQUEST['username'] ?? null;
|
||||
$password = $_REQUEST['password'] ?? null;
|
||||
$remember = isset($_REQUEST['remember']);
|
||||
$username = $_POST['username'] ?? null;
|
||||
$password = $_POST['password'] ?? null;
|
||||
|
||||
// Check if we haven't hit the rate limit
|
||||
$rates = DB::table('login_attempts')
|
||||
|
@ -79,38 +74,30 @@ class AuthController extends Controller
|
|||
->count();
|
||||
|
||||
if ($rates > 4) {
|
||||
$message = 'Your have hit the login rate limit, try again later.';
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json(['error' => 'Your have hit the login rate limit, try again later.']);
|
||||
}
|
||||
|
||||
// Get account data
|
||||
$user = User::construct(clean_string($username, true, true));
|
||||
|
||||
// Check if the user that's trying to log in actually exists
|
||||
if ($user->id === 0) {
|
||||
$this->touchRateLimit($user->id);
|
||||
$message = 'The user you tried to log into does not exist.';
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json(['error' => 'The user you tried to log into does not exist.']);
|
||||
}
|
||||
|
||||
if ($user->passwordExpired()) {
|
||||
$message = 'Your password expired.';
|
||||
$redirect = route('auth.resetpassword');
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json(['error' => 'Your password expired.']);
|
||||
}
|
||||
|
||||
if (!$user->verifyPassword($password)) {
|
||||
$this->touchRateLimit($user->id);
|
||||
$message = 'The password you entered was invalid.';
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json(['error' => 'The password you entered was invalid.']);
|
||||
}
|
||||
|
||||
// Check if the user has the required privs to log in
|
||||
if (!$user->activated) {
|
||||
$this->touchRateLimit($user->id);
|
||||
$message = 'Your account is deactivated, activate it first!';
|
||||
$redirect = route('auth.reactivate');
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json(['error' => "Your account isn't activated, check your e-mail!"]);
|
||||
}
|
||||
|
||||
// Generate a session key
|
||||
|
@ -118,33 +105,22 @@ class AuthController extends Controller
|
|||
$user->id,
|
||||
Net::ip(),
|
||||
get_country_code(),
|
||||
clean_string($_SERVER['HTTP_USER_AGENT'] ?? ''),
|
||||
$remember
|
||||
clean_string($_SERVER['HTTP_USER_AGENT'] ?? '')
|
||||
);
|
||||
|
||||
$cookiePrefix = config('cookie.prefix');
|
||||
|
||||
// User ID cookie
|
||||
setcookie(
|
||||
"{$cookiePrefix}id",
|
||||
$user->id,
|
||||
time() + 604800
|
||||
);
|
||||
|
||||
// Session ID cookie
|
||||
setcookie(
|
||||
"{$cookiePrefix}session",
|
||||
$session->key,
|
||||
time() + 604800
|
||||
);
|
||||
setcookie("{$cookiePrefix}id", $user->id, time() + 604800);
|
||||
setcookie("{$cookiePrefix}session", $session->key, time() + 604800);
|
||||
|
||||
$this->touchRateLimit($user->id, true);
|
||||
|
||||
$redirect = $user->lastOnline ? ($_REQUEST['redirect'] ?? route('main.index')) : route('info.welcome');
|
||||
$msg = ['error' => null];
|
||||
|
||||
$message = 'Welcome' . ($user->lastOnline ? ' back' : '') . '!';
|
||||
if (!$user->lastOnline) {
|
||||
$msg['go'] = route('info.welcome');
|
||||
}
|
||||
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
return $this->json($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,7 +223,7 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
// Return true with a specific message if needed
|
||||
$redirect = route('auth.login');
|
||||
$redirect = route('main.index');
|
||||
$message = 'Your registration went through!';
|
||||
$message .= $requireActive ? ' An activation e-mail has been sent.' : ' Welcome to ' . config('general.name') . '!';
|
||||
|
||||
|
@ -294,7 +270,7 @@ class AuthController extends Controller
|
|||
->where('user_id', $userId)
|
||||
->update(['user_activated' => 1]);
|
||||
|
||||
$redirect = route('auth.login');
|
||||
$redirect = route('main.index');
|
||||
$message = "Your account is activated, welcome to " . config('general.name') . "!";
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
}
|
||||
|
@ -341,7 +317,7 @@ class AuthController extends Controller
|
|||
// Send activation e-mail to user
|
||||
$this->sendActivationMail($user);
|
||||
|
||||
$redirect = route('auth.login');
|
||||
$redirect = route('main.index');
|
||||
$message = "Sent the e-mail! Make sure to check your spam folder as well!";
|
||||
return view('global/information', compact('message', 'redirect'));
|
||||
}
|
||||
|
@ -400,7 +376,7 @@ class AuthController extends Controller
|
|||
$user->setPassword($password);
|
||||
|
||||
$message = "Changed your password! You may now log in.";
|
||||
$redirect = route('auth.login');
|
||||
$redirect = route('main.index');
|
||||
} else {
|
||||
// Send the e-mail
|
||||
$this->sendPasswordMail($user);
|
||||
|
|
|
@ -74,8 +74,8 @@ class CurrentSession
|
|||
* @param int $length
|
||||
* @return Session
|
||||
*/
|
||||
public static function create(int $user, string $ip, string $country, string $agent = null, bool $remember = false, int $length = 604800)
|
||||
public static function create(int $user, string $ip, string $country, string $agent = null, int $length = 604800)
|
||||
{
|
||||
return Session::create($user, $ip, $country, $agent, $remember, $length);
|
||||
return Session::create($user, $ip, $country, $agent, $length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,12 +61,6 @@ class Session
|
|||
*/
|
||||
public $expire = 0;
|
||||
|
||||
/**
|
||||
* Whether to extend the session's lifetime.
|
||||
* @var bool
|
||||
*/
|
||||
public $remember = false;
|
||||
|
||||
/**
|
||||
* Constructor, $id can be a number or the secret key.
|
||||
* @param mixed $id
|
||||
|
@ -92,7 +86,6 @@ class Session
|
|||
$this->key = $data->session_key;
|
||||
$this->start = intval($data->session_start);
|
||||
$this->expire = intval($data->session_expire);
|
||||
$this->remember = boolval($data->session_remember);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,11 +95,10 @@ class Session
|
|||
* @param string $ip
|
||||
* @param string $country
|
||||
* @param string $agent
|
||||
* @param bool $remember
|
||||
* @param int $length
|
||||
* @return Session
|
||||
*/
|
||||
public static function create(int $user, string $ip, string $country, string $agent = null, bool $remember = false, int $length = 604800)
|
||||
public static function create(int $user, string $ip, string $country, string $agent = null, int $length = 604800)
|
||||
{
|
||||
$start = time();
|
||||
$key = bin2hex(random_bytes(64));
|
||||
|
@ -119,7 +111,6 @@ class Session
|
|||
'session_key' => $key,
|
||||
'session_start' => $start,
|
||||
'session_expire' => $start + $length,
|
||||
'session_remember' => $remember ? 1 : 0,
|
||||
'session_country' => $country,
|
||||
]);
|
||||
|
||||
|
@ -168,14 +159,10 @@ class Session
|
|||
good thing is i can probably do CIDR based checking */
|
||||
}
|
||||
|
||||
// If the remember flag is set extend the session time
|
||||
if ($session->session_remember) {
|
||||
DB::table('sessions')
|
||||
->where('session_id', $session->session_id)
|
||||
->update(['session_expire' => time() + 604800]);
|
||||
}
|
||||
|
||||
// Return 2 if the remember flag is set and return 1 if not
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -347,10 +347,6 @@ class BaseTables extends Migration
|
|||
|
||||
$table->integer('session_expire')
|
||||
->unsigned();
|
||||
|
||||
$table->tinyInteger('session_remember')
|
||||
->unsigned()
|
||||
->default(0);
|
||||
});
|
||||
|
||||
$schema->create('topics', function (Blueprint $table) {
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
.header-login {
|
||||
background: rgba(211, 191, 255, .8);
|
||||
border: 1px solid #9475B2;
|
||||
.header-login-container {
|
||||
background: #A586C3;
|
||||
box-shadow: 0 0 3px #8364A1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-login {
|
||||
max-width: 1024px;
|
||||
margin: 10px auto 0;
|
||||
padding: 6px 3px;
|
||||
border-radius: 3px;
|
||||
padding: 6px 4px;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&__text {
|
||||
width: auto !important;
|
||||
|
@ -16,17 +21,43 @@
|
|||
&__button {
|
||||
margin: 0 !important;
|
||||
padding: 2px 8px !important;
|
||||
|
||||
&--small {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-family: @cute-font;
|
||||
font-weight: 100;
|
||||
font-size: 15px;
|
||||
&__sub--buttons {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
&__label {
|
||||
display: block;
|
||||
@media (max-width: 1064px) {
|
||||
align-items: flex-start;
|
||||
|
||||
&__text {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&__button--small {
|
||||
margin-bottom: 2px !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__sub {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 1px;
|
||||
|
||||
&--form {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&--buttons {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:first-child) {
|
||||
// hackjob
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
top: -100%;
|
||||
}
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
font-size: 3em;
|
||||
line-height: 1.4em;
|
||||
transition: background .2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: fade(#000, 50%);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div class="header__avatar" style="background-image: url('{{ route('user.avatar', user.id) }}')"></div>
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="header__user" href="{{ route('auth.login') }}">
|
||||
<a class="header__user" href="#">
|
||||
<div class="header__username">login</div>
|
||||
<div class="header__avatar" style="background-image: url('/images/no-avatar.png')"></div>
|
||||
</a>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="landing__inner">
|
||||
<div class="landing__buttons">
|
||||
<a href="{{ route('auth.register') }}" class="landing__button">register</a>
|
||||
<a href="{{ route('auth.login') }}" class="landing__button">login</a>
|
||||
<a href="#" class="landing__button">login</a>
|
||||
</div>
|
||||
<div class="landing__text">
|
||||
<p>Welcome to my humble abode, it doesn't look like much but if you like rectangles this is the place for you.</p>
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set title = 'Login' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth content content--auth">
|
||||
<div class="content__header">
|
||||
Login
|
||||
</div>
|
||||
<form method="post" action="{{ route('auth.login') }}">
|
||||
<input type="hidden" name="redirect" value="{{ server['HTTP_REFERER']|default(route('main.index')) }}">
|
||||
<label>
|
||||
<div class="auth__label">Username</div>
|
||||
<input class="input__text" type="text" name="username" autofocus>
|
||||
</label>
|
||||
<label>
|
||||
<div class="auth__label">Password</div>
|
||||
<input class="input__text" type="password" name="password">
|
||||
</label>
|
||||
<label>
|
||||
<div class="auth__label auth__label--checkbox">
|
||||
<input name="remember" type="checkbox">
|
||||
Remember me!
|
||||
</div>
|
||||
</label>
|
||||
<button class="input__button auth__button" name="session" value="{{ session_id() }}">
|
||||
<i class="fa fa-sign-in"></i> Login
|
||||
</button>
|
||||
<div class="auth__subtext">
|
||||
<a href="{{ route('auth.register') }}">I don't have an account yet!</a><br>
|
||||
<a href="{{ route('auth.resetpassword') }}">I forgot my password!</a><br>
|
||||
<a href="{{ route('auth.reactivate') }}">I want to reactivate my account!</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -30,7 +30,8 @@
|
|||
</button>
|
||||
<div class="auth__subtext">
|
||||
By creating an account you agree to the <a href="{{ route('info.terms') }}">Terms of Service</a>.<br>
|
||||
You are only allowed to make a single account.
|
||||
You are only allowed to make a single account.<br>
|
||||
Didn't get your activation e-mail? <a href="{{ route('auth.reactivate') }}">Click here to resend it</a>!
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<input class="input__text" type="password" name="password" autofocus>
|
||||
</label>
|
||||
<button class="input__button auth__button">
|
||||
Save Password
|
||||
<i class="fa fa-save"></i> Save Password
|
||||
</button>
|
||||
{% else %}
|
||||
<label>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<input class="input__text" type="text" name="email">
|
||||
</label>
|
||||
<button class="input__button auth__button">
|
||||
Request Change
|
||||
<i class="fa fa-envelope"></i> Request Change
|
||||
</button>
|
||||
<div class="auth__subtext">
|
||||
<a href="{{ route('info.contact') }}">Contact us</a> if you lost access to your e-mail address!
|
||||
|
|
|
@ -62,40 +62,69 @@
|
|||
{% endif %}
|
||||
<a class="header__menu-item fa fa-cogs" href="{{ route('settings.index') }}" title="Settings"></a>
|
||||
<a class="header__menu-item fa fa-sign-out" href="{{ route('auth.logout') }}" title="Logout"></a>
|
||||
{% else %}
|
||||
<a class="header__menu-item fa fa-magic" href="{{ route('auth.register') }}" title="Register"></a>
|
||||
<a class="header__menu-item fa fa-sign-in" href="{{ route('auth.login') }}" title="Login"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.id == 0 %}
|
||||
<div class="header-login-container">
|
||||
<form class="header-login" method="post" action="javascript:void(0);" onsubmit="yuunoAttemptLogin(this)">
|
||||
<div class="header-login__sub header-login__sub--form">
|
||||
<input type="text" name="username" class="input__text header-login__text" placeholder="Username">
|
||||
<input type="password" name="password" class="input__text header-login__text" placeholder="Password">
|
||||
<input type="hidden" name="session" value="{{ session_id() }}">
|
||||
<button class="input__button header-login__button">
|
||||
<i class="fa fa-sign-in"></i> Login
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-login__sub header-login__sub--buttons">
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.register') }}">
|
||||
<i class="fa fa-magic"></i> I don't have an account yet!
|
||||
</a>
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.resetpassword') }}">
|
||||
<i class="fa fa-exclamation-triangle"></i> I don't have access to my account!
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function yuunoAttemptLogin(form) {
|
||||
var ajax = new Sakura.AJAX();
|
||||
|
||||
console.log(new FormData(form));
|
||||
|
||||
ajax.SetUrl("{{ route('auth.login') }}");
|
||||
ajax.SetFormData(new FormData(form));
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
var result = ajax.JSON();
|
||||
console.log(result);
|
||||
|
||||
if (result.error) {
|
||||
var diag = new Sakura.Dialogue;
|
||||
diag.Text = result.error;
|
||||
diag.AddCallback(Sakura.DialogueButton.Ok, function () {
|
||||
this.Close();
|
||||
});
|
||||
diag.Display();
|
||||
} else if (result.go) {
|
||||
window.location.assign(result.go);
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.POST);
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<div id="contentwrapper" class="container__content">
|
||||
<div id="notifications" class="alerts"></div>
|
||||
<div id="dialogues" class="dialogues"></div>
|
||||
|
||||
{% if not user.isActive and server['REQUEST_URI'] != route('auth.login') %}
|
||||
<div class="header-login">
|
||||
<form method="post" action="{{ route('auth.login') }}">
|
||||
<input type="hidden" name="redirect" value="{{ server['REQUEST_URI'] }}">
|
||||
<label class="header-login__label">
|
||||
Username:
|
||||
<input type="text" name="username" class="input__text header-login__text" placeholder="Username">
|
||||
</label>
|
||||
<label class="header-login__label">
|
||||
Password:
|
||||
<input type="password" name="password" class="input__text header-login__text" placeholder="Password">
|
||||
</label>
|
||||
<label class="header-login__label">
|
||||
<input type="checkbox" name="remember">
|
||||
Remember me
|
||||
</label>
|
||||
<button class="input__button header-login__button" name="session" value="{{ session_id() }}">
|
||||
<i class="fa fa-sign-in"></i> Login
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user.restricted %}
|
||||
<div class="announce-box announce-box--restricted">
|
||||
<h1>Your account is currently in <span style="font-weight: 700 !important;">restricted mode</span>!</h1>
|
||||
|
|
|
@ -186,14 +186,18 @@
|
|||
<div class="content profile">
|
||||
<div class="profile__container">
|
||||
<div class="profile__header" style="background-image: url({{ route('user.header', profile.id) }});">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeHeader) or user.perms.manageProfileImages %}
|
||||
<label class="uploader__label">
|
||||
<input type="file" data-target="{{ route('user.header', profile.id) }}" class="uploader" onchange="handleImageChange(this, this.parentElement.parentElement)">
|
||||
</label>
|
||||
{% endif %}
|
||||
<div class="profile__info">
|
||||
<div class="avatar avatar--border profile__avatar" style="background-image: url({{ route('user.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeAvatar) or user.perms.manageProfileImages %}
|
||||
<label class="uploader__label">
|
||||
<input type="file" data-target="{{ route('user.avatar', profile.id) }}" class="uploader" onchange="handleImageChange(this, this.parentElement.parentElement)">
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="profile__username">
|
||||
<h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ profile.username }}</h1>
|
||||
|
|
Reference in a new issue