big auth refactor

This commit is contained in:
flash 2016-12-08 00:34:59 +01:00
parent ac310dce99
commit 13090423d2
14 changed files with 150 additions and 159 deletions

View file

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

View file

@ -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);
}
}

View file

@ -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]);
}
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;
}

View file

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

View file

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

View file

@ -17,10 +17,12 @@
display: flex;
align-items: center;
// hackjob
pointer-events: none;
position: relative;
top: -100%;
&:not(:first-child) {
// hackjob
pointer-events: none;
position: relative;
top: -100%;
}
> * {
pointer-events: auto;

View file

@ -7,6 +7,7 @@
font-size: 3em;
line-height: 1.4em;
transition: background .2s;
cursor: pointer;
&:hover {
background: fade(#000, 50%);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) }});">
<label class="uploader__label">
<input type="file" data-target="{{ route('user.header', profile.id) }}" class="uploader" onchange="handleImageChange(this, this.parentElement.parentElement)">
</label>
{% 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 %};">
<label class="uploader__label">
<input type="file" data-target="{{ route('user.avatar', profile.id) }}" class="uploader" onchange="handleImageChange(this, this.parentElement.parentElement)">
</label>
{% 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>