Use some of Eloquent's features to make life easier.
This commit is contained in:
parent
de56e9ede8
commit
8d90699f40
7 changed files with 128 additions and 30 deletions
|
@ -29,7 +29,7 @@ class Application extends ApplicationBase
|
||||||
* Session instance.
|
* Session instance.
|
||||||
* @var \Misuzu\Users\Session
|
* @var \Misuzu\Users\Session
|
||||||
*/
|
*/
|
||||||
public $session = null;
|
private $session = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, called by ApplicationBase::start() which also passes the arguments through.
|
* Constructor, called by ApplicationBase::start() which also passes the arguments through.
|
||||||
|
@ -51,12 +51,28 @@ class Application extends ApplicationBase
|
||||||
|
|
||||||
public function startSession(int $user_id, string $session_key): void
|
public function startSession(int $user_id, string $session_key): void
|
||||||
{
|
{
|
||||||
$session = Session::where('session_key', $session_key)->where('user_id', $user_id)->first();
|
$session = Session::where('session_key', $session_key)
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->first();
|
||||||
|
|
||||||
if ($session !== null) {
|
if ($session !== null) {
|
||||||
$this->session = $session;
|
if ($session->hasExpired()) {
|
||||||
|
$session->delete();
|
||||||
|
} else {
|
||||||
|
$this->setSession($session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSession(): Session
|
||||||
|
{
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSession(Session $session): void
|
||||||
|
{
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the database module.
|
* Sets up the database module.
|
||||||
|
|
|
@ -29,30 +29,31 @@ class AuthController extends Controller
|
||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = User::where('username', $username)->firstOrFail();
|
$user = User::where('username', $username)->orWhere('email', $username)->firstOrFail();
|
||||||
} catch (ModelNotFoundException $e) {
|
} catch (ModelNotFoundException $e) {
|
||||||
return ['error' => 'Invalid username or password!'];
|
return ['error' => 'Invalid username or password!'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password_verify($password, $user->password)) {
|
if (!$user->validatePassword($password)) {
|
||||||
return ['error' => 'Invalid username or password!'];
|
return ['error' => 'Invalid username or password!'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$session = new Session;
|
$session = new Session;
|
||||||
$session->user_id = $user->user_id;
|
$session->user_id = $user->user_id;
|
||||||
$session->session_ip = IP::unpack(IP::remote());
|
$session->session_ip = IP::remote();
|
||||||
$session->user_agent = 'Misuzu Testing 1';
|
$session->user_agent = 'Misuzu Testing 1';
|
||||||
$session->expires_on = Carbon::now()->addMonth();
|
$session->expires_on = Carbon::now()->addMonth();
|
||||||
$session->session_key = bin2hex(random_bytes(32));
|
$session->session_key = bin2hex(random_bytes(32));
|
||||||
$session->save();
|
$session->save();
|
||||||
|
|
||||||
Application::getInstance()->session = $session;
|
Application::getInstance()->setSession($session);
|
||||||
$this->setCookie('uid', $session->user_id, 604800);
|
$this->setCookie('uid', $session->user_id, 604800);
|
||||||
$this->setCookie('sid', $session->session_key, 604800);
|
$this->setCookie('sid', $session->session_key, 604800);
|
||||||
|
|
||||||
// Temporary key generation for chat login.
|
// Temporary key generation for chat login.
|
||||||
// Should eventually be replaced with a callback login system.
|
// Should eventually be replaced with a callback login system.
|
||||||
// Also uses different cookies since $httponly is required to be false for these.
|
// Also uses different cookies since $httponly is required to be false for these.
|
||||||
|
$user->last_ip = IP::remote();
|
||||||
$user->user_chat_key = bin2hex(random_bytes(16));
|
$user->user_chat_key = bin2hex(random_bytes(16));
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
|
@ -79,6 +80,10 @@ class AuthController extends Controller
|
||||||
{
|
{
|
||||||
$ipAddr = IP::unpack($ipAddr ?? IP::remote());
|
$ipAddr = IP::unpack($ipAddr ?? IP::remote());
|
||||||
|
|
||||||
|
if ($ipAddr === IP::unpack('127.0.0.1') || $ipAddr === IP::unpack('::1')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (User::withTrashed()->where('register_ip', $ipAddr)->orWhere('last_ip', $ipAddr)->count()) {
|
if (User::withTrashed()->where('register_ip', $ipAddr)->orWhere('last_ip', $ipAddr)->count()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -157,14 +162,7 @@ class AuthController extends Controller
|
||||||
return ['error' => 'Your password is considered too weak!'];
|
return ['error' => 'Your password is considered too weak!'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = new User;
|
User::createUser($username, $password, $email);
|
||||||
$user->username = $username;
|
|
||||||
$user->password = password_hash($password, PASSWORD_ARGON2I);
|
|
||||||
$user->email = $email;
|
|
||||||
$user->register_ip = IP::unpack(IP::remote());
|
|
||||||
$user->last_ip = IP::unpack(IP::remote());
|
|
||||||
$user->user_country = get_country_code(IP::remote());
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
return ['error' => 'Welcome to Flashii! You may now log in.', 'next' => '/auth/login'];
|
return ['error' => 'Welcome to Flashii! You may now log in.', 'next' => '/auth/login'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,27 @@
|
||||||
namespace Misuzu\Users;
|
namespace Misuzu\Users;
|
||||||
|
|
||||||
use Misuzu\Model;
|
use Misuzu\Model;
|
||||||
|
use Misuzu\Net\IP;
|
||||||
|
|
||||||
class Session extends Model
|
class Session extends Model
|
||||||
{
|
{
|
||||||
protected $primaryKey = 'session_id';
|
protected $primaryKey = 'session_id';
|
||||||
|
protected $dates = ['expires_on'];
|
||||||
|
|
||||||
|
public function getSessionIpAttribute(string $ipAddress): string
|
||||||
|
{
|
||||||
|
return IP::pack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSessionIpAttribute(string $ipAddress): void
|
||||||
|
{
|
||||||
|
$this->attributes['session_ip'] = IP::unpack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasExpired(): bool
|
||||||
|
{
|
||||||
|
return $this->expires_on->isPast();
|
||||||
|
}
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,13 +3,71 @@ namespace Misuzu\Users;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Misuzu\Model;
|
use Misuzu\Model;
|
||||||
|
use Misuzu\Net\IP;
|
||||||
|
|
||||||
class User extends Model
|
class User extends Model
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
|
private const PASSWORD_HASH_ALGO = PASSWORD_ARGON2I;
|
||||||
|
|
||||||
protected $primaryKey = 'user_id';
|
protected $primaryKey = 'user_id';
|
||||||
|
|
||||||
|
public static function createUser(
|
||||||
|
string $username,
|
||||||
|
string $password,
|
||||||
|
string $email,
|
||||||
|
?string $ipAddress = null
|
||||||
|
): User {
|
||||||
|
$ipAddress = $ipAddress ?? IP::remote();
|
||||||
|
|
||||||
|
$user = new User;
|
||||||
|
$user->username = $username;
|
||||||
|
$user->password = $password;
|
||||||
|
$user->email = $email;
|
||||||
|
$user->register_ip = $ipAddress;
|
||||||
|
$user->last_ip = $ipAddress;
|
||||||
|
$user->user_country = get_country_code($ipAddress);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRegisterIpAttribute(string $ipAddress): string
|
||||||
|
{
|
||||||
|
return IP::pack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRegisterIpAttribute(string $ipAddress): void
|
||||||
|
{
|
||||||
|
$this->attributes['register_ip'] = IP::unpack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastIpAttribute(string $ipAddress): string
|
||||||
|
{
|
||||||
|
return IP::pack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLastIpAttribute(string $ipAddress): void
|
||||||
|
{
|
||||||
|
$this->attributes['last_ip'] = IP::unpack($ipAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPasswordAttribute(string $password): void
|
||||||
|
{
|
||||||
|
$this->attributes['password'] = password_hash($password, self::PASSWORD_HASH_ALGO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validatePassword(string $password): bool
|
||||||
|
{
|
||||||
|
if (password_needs_rehash($this->password, self::PASSWORD_HASH_ALGO)) {
|
||||||
|
$this->password = $password;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return password_verify($password, $this->password);
|
||||||
|
}
|
||||||
|
|
||||||
public function sessions()
|
public function sessions()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Session::class, 'user_id');
|
return $this->hasMany(Session::class, 'user_id');
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
{% set banner_classes = 'banner--insane landing__banner' %}
|
{% set banner_classes = 'banner--insane landing__banner' %}
|
||||||
|
|
||||||
{% block banner_content %}
|
{% block banner_content %}
|
||||||
<h1 style="align-self: center; text-align: left; flex-grow: 1; padding-left: 2em">Well, this is embarrassing...</h1>
|
<div class="landing__inner">
|
||||||
{% endblock %}
|
<div class="landing__buttons">
|
||||||
|
<a href="/auth/register" class="landing__button">register</a>
|
||||||
{% block content %}
|
<a href="/auth/login" class="landing__button">login</a>
|
||||||
<div class="platform" style="text-align: left;">
|
</div>
|
||||||
<p>Long story short, almost nothing is ready and going live now (or within the next week) will just lead to greater disappointment. I set up a <a href="https://twitter.com/flashiinet" class="container__footer-link" target="_blank" rel="noreferrer noopener">Twitter</a> again on which I'll eventually inform when the site goes in public beta.</p>
|
<div class="landing__text">
|
||||||
<p>I offer my sincerest apologies for not being able to live up to the hype I've created (twice, even) but understand that this decision will make for a less rushed end product and less coping with hurried code in the future.</p>
|
<p>Registration soon, but not now.</p>
|
||||||
|
<p>Keep an eye on <a href="https://twitter.com/flashiinet" class="container__footer-link" target="_blank" rel="noreferrer noopener">Twitter</a>!</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
5
views/nova/macros.twig
Normal file
5
views/nova/macros.twig
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{% macro link(url, content, class) %}
|
||||||
|
{% spaceless %}
|
||||||
|
<a href="{{ url }}" {% if '://' in url %} target="_blank" rel="noreferrer noopener"{% endif %} {% if class is defined %}class="{{ class }}"{% endif %}>{{ content|raw }}</a>
|
||||||
|
{% endspaceless %}
|
||||||
|
{% endmacro %}
|
|
@ -1,3 +1,5 @@
|
||||||
|
{% from '@nova/macros.twig' import link %}
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
@ -21,7 +23,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<a class="header__user" href="{{ app.session is not null ? '/users/' ~ app.session.user_id : '/auth/login' }}">
|
<a class="header__user" href="{{ app.session is not null ? '/users/' ~ app.session.user_id : '/auth/login' }}">
|
||||||
<div class="header__username">{{ app.session is not null ? app.session.user.first.username : 'login' }}</div>
|
<div class="header__username">{{ app.session is not null ? app.session.user.username : 'login' }}</div>
|
||||||
<div class="header__avatar" style="background-image: url('https://static.flash.moe/images/{{ app.session is not null ? 'discord-logo' : 'nova-none' }}.png')"></div>
|
<div class="header__avatar" style="background-image: url('https://static.flash.moe/images/{{ app.session is not null ? 'discord-logo' : 'nova-none' }}.png')"></div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,15 +43,15 @@
|
||||||
|
|
||||||
<div class="container__footer">
|
<div class="container__footer">
|
||||||
<div class="container__footer-copyright">
|
<div class="container__footer-copyright">
|
||||||
<a href="https://flash.moe" class="container__footer-link">flash.moe 2013-{{ ''|date('Y') }}</a> |
|
{{ link('https://flash.moe', 'flash.moe 2013-' ~ ''|date('Y'), 'container__footer-link') }} |
|
||||||
<a href="https://github.com/flashwave/misuzu/tree/{{ git_branch() }}" class="container__footer-link">{{ git_branch() }}</a>#<a href="https://github.com/flashwave/misuzu/commit/{{ git_hash(true) }}" class="container__footer-link">{{ git_hash() }}</a>
|
{{ link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'container__footer-link') }}#{{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'container__footer-link') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="container__footer-links">
|
<div class="container__footer-links">
|
||||||
<a href="#" class="container__footer-link">Terms of Service</a>
|
{{ link('#', 'Terms of Service', 'container__footer-link') }}
|
||||||
<a href="#" class="container__footer-link">Rules</a>
|
{{ link('#', 'Rules', 'container__footer-link') }}
|
||||||
<a href="#" class="container__footer-link">Contact</a>
|
{{ link('#', 'Contact', 'container__footer-link') }}
|
||||||
<a href="#" class="container__footer-link">Status</a>
|
{{ link('#', 'Status', 'container__footer-link') }}
|
||||||
<a href="https://twitter.com/flashiinet" class="container__footer-link" target="_blank" rel="noreferrer noopener">@flashiinet</a>
|
{{ link('https://twitter.com/flashiinet', '@flashiinet', 'container__footer-link') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue