diff --git a/src/Application.php b/src/Application.php index e1399ade..f6ea0b98 100644 --- a/src/Application.php +++ b/src/Application.php @@ -29,7 +29,7 @@ class Application extends ApplicationBase * Session instance. * @var \Misuzu\Users\Session */ - public $session = null; + private $session = null; /** * Constructor, called by ApplicationBase::start() which also passes the arguments through. @@ -51,13 +51,29 @@ class Application extends ApplicationBase 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) { - $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. */ diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index 10e879bb..b8517e8a 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -29,30 +29,31 @@ class AuthController extends Controller $password = $_POST['password'] ?? ''; try { - $user = User::where('username', $username)->firstOrFail(); + $user = User::where('username', $username)->orWhere('email', $username)->firstOrFail(); } catch (ModelNotFoundException $e) { return ['error' => 'Invalid username or password!']; } - if (!password_verify($password, $user->password)) { + if (!$user->validatePassword($password)) { return ['error' => 'Invalid username or password!']; } $session = new Session; $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->expires_on = Carbon::now()->addMonth(); $session->session_key = bin2hex(random_bytes(32)); $session->save(); - Application::getInstance()->session = $session; + Application::getInstance()->setSession($session); $this->setCookie('uid', $session->user_id, 604800); $this->setCookie('sid', $session->session_key, 604800); // Temporary key generation for chat login. // Should eventually be replaced with a callback login system. // 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->save(); @@ -79,6 +80,10 @@ class AuthController extends Controller { $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()) { return true; } @@ -157,14 +162,7 @@ class AuthController extends Controller return ['error' => 'Your password is considered too weak!']; } - $user = new User; - $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(); + User::createUser($username, $password, $email); return ['error' => 'Welcome to Flashii! You may now log in.', 'next' => '/auth/login']; } diff --git a/src/Users/Session.php b/src/Users/Session.php index 41b43dce..dadb6a36 100644 --- a/src/Users/Session.php +++ b/src/Users/Session.php @@ -2,10 +2,27 @@ namespace Misuzu\Users; use Misuzu\Model; +use Misuzu\Net\IP; class Session extends Model { 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() { diff --git a/src/Users/User.php b/src/Users/User.php index 8dae07d1..9a56516d 100644 --- a/src/Users/User.php +++ b/src/Users/User.php @@ -3,13 +3,71 @@ namespace Misuzu\Users; use Illuminate\Database\Eloquent\SoftDeletes; use Misuzu\Model; +use Misuzu\Net\IP; class User extends Model { use SoftDeletes; + private const PASSWORD_HASH_ALGO = PASSWORD_ARGON2I; + 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() { return $this->hasMany(Session::class, 'user_id'); diff --git a/views/nova/home/landing.twig b/views/nova/home/landing.twig index 751329cd..f5b18675 100644 --- a/views/nova/home/landing.twig +++ b/views/nova/home/landing.twig @@ -3,12 +3,14 @@ {% set banner_classes = 'banner--insane landing__banner' %} {% block banner_content %} -

Well, this is embarrassing...

-{% endblock %} - -{% block content %} -
-

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 Twitter again on which I'll eventually inform when the site goes in public beta.

-

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.

+
+
+ register + login +
+
+

Registration soon, but not now.

+

Keep an eye on Twitter!

+
{% endblock %} diff --git a/views/nova/macros.twig b/views/nova/macros.twig new file mode 100644 index 00000000..bb193b0e --- /dev/null +++ b/views/nova/macros.twig @@ -0,0 +1,5 @@ +{% macro link(url, content, class) %} +{% spaceless %} +{{ content|raw }} +{% endspaceless %} +{% endmacro %} diff --git a/views/nova/master.twig b/views/nova/master.twig index 536a8fb5..af4af776 100644 --- a/views/nova/master.twig +++ b/views/nova/master.twig @@ -1,3 +1,5 @@ +{% from '@nova/macros.twig' import link %} + @@ -21,7 +23,7 @@ {% endif %}
-
{{ app.session is not null ? app.session.user.first.username : 'login' }}
+
{{ app.session is not null ? app.session.user.username : 'login' }}
@@ -41,15 +43,15 @@