Use custom session system.
This commit is contained in:
parent
e6eb44303b
commit
3862a66807
10 changed files with 175 additions and 25 deletions
65
database/2018_02_10_222914_create_sessions_table.php
Normal file
65
database/2018_02_10_222914_create_sessions_table.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Misuzu\Database;
|
||||||
|
|
||||||
|
// phpcs:disable
|
||||||
|
class CreateSessionsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD)
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$schema = Database::connection()->getSchemaBuilder();
|
||||||
|
$schema->create('sessions', function (Blueprint $table) {
|
||||||
|
$table->increments('session_id');
|
||||||
|
|
||||||
|
$table->integer('user_id')
|
||||||
|
->unsigned();
|
||||||
|
|
||||||
|
$table->string('session_key', 255);
|
||||||
|
|
||||||
|
$table->binary('session_ip');
|
||||||
|
|
||||||
|
$table->string('user_agent', 255)
|
||||||
|
->nullable()
|
||||||
|
->default(null);
|
||||||
|
|
||||||
|
$table->timestamp('expires_on')
|
||||||
|
->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->foreign('user_id')
|
||||||
|
->references('user_id')
|
||||||
|
->on('users')
|
||||||
|
->onUpdate('cascade')
|
||||||
|
->onDelete('cascade');
|
||||||
|
});
|
||||||
|
|
||||||
|
$schema->table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('user_registered');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD)
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$schema = Database::connection()->getSchemaBuilder();
|
||||||
|
$schema->drop('sessions');
|
||||||
|
|
||||||
|
$schema->table('users', function (Blueprint $table) {
|
||||||
|
$table->integer('user_registered')
|
||||||
|
->unsigned()
|
||||||
|
->default(0);
|
||||||
|
|
||||||
|
$table->dropSoftDeletes();
|
||||||
|
$table->dropTimestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ if (!$migrator->repositoryExists()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$migrator->run(__DIR__ . '/database');
|
$migrator->run(__DIR__ . '/database');
|
||||||
|
//$migrator->rollback(__DIR__ . '/database');
|
||||||
|
|
||||||
foreach ($migrator->getNotes() as $note) {
|
foreach ($migrator->getNotes() as $note) {
|
||||||
echo strip_tags($note) . PHP_EOL;
|
echo strip_tags($note) . PHP_EOL;
|
||||||
|
|
|
@ -5,11 +5,15 @@ use Aitemu\RouterRequest;
|
||||||
|
|
||||||
require_once __DIR__ . '/../misuzu.php';
|
require_once __DIR__ . '/../misuzu.php';
|
||||||
|
|
||||||
ob_start('ob_gzhandler');
|
//ob_start('ob_gzhandler');
|
||||||
|
|
||||||
$app = Application::getInstance();
|
$app = Application::getInstance();
|
||||||
|
|
||||||
$app->startRouter(include_once __DIR__ . '/../routes.php');
|
if (isset($_COOKIE['msz_uid'], $_COOKIE['msz_sid'])) {
|
||||||
|
$app->startSession((int)$_COOKIE['msz_uid'], $_COOKIE['msz_sid']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$app->startRouter(include __DIR__ . '/../routes.php');
|
||||||
$app->startTemplating();
|
$app->startTemplating();
|
||||||
|
|
||||||
echo $app->router->resolve(
|
echo $app->router->resolve(
|
||||||
|
|
|
@ -3,6 +3,7 @@ namespace Misuzu;
|
||||||
|
|
||||||
use Aitemu\RouteCollection;
|
use Aitemu\RouteCollection;
|
||||||
use Misuzu\Config\ConfigManager;
|
use Misuzu\Config\ConfigManager;
|
||||||
|
use Misuzu\Users\Session;
|
||||||
use UnexpectedValueException;
|
use UnexpectedValueException;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
@ -22,9 +23,14 @@ class Application extends ApplicationBase
|
||||||
*/
|
*/
|
||||||
private const DATABASE_CONNECTIONS = [
|
private const DATABASE_CONNECTIONS = [
|
||||||
'mysql-main',
|
'mysql-main',
|
||||||
//'mysql-ayase',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session instance.
|
||||||
|
* @var \Misuzu\Users\Session
|
||||||
|
*/
|
||||||
|
public $session = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, called by ApplicationBase::start() which also passes the arguments through.
|
* Constructor, called by ApplicationBase::start() which also passes the arguments through.
|
||||||
* @param ?string $configFile
|
* @param ?string $configFile
|
||||||
|
@ -36,9 +42,6 @@ class Application extends ApplicationBase
|
||||||
ExceptionHandler::register();
|
ExceptionHandler::register();
|
||||||
ExceptionHandler::debug($this->debugMode);
|
ExceptionHandler::debug($this->debugMode);
|
||||||
$this->addModule('config', new ConfigManager($configFile));
|
$this->addModule('config', new ConfigManager($configFile));
|
||||||
|
|
||||||
// temporary session system
|
|
||||||
session_start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
|
@ -46,6 +49,15 @@ class Application extends ApplicationBase
|
||||||
ExceptionHandler::unregister();
|
ExceptionHandler::unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function startSession(int $user_id, string $session_key): void
|
||||||
|
{
|
||||||
|
$session = Session::where('session_key', $session_key)->where('user_id', $user_id)->first();
|
||||||
|
|
||||||
|
if ($session !== null) {
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the database module.
|
* Sets up the database module.
|
||||||
*/
|
*/
|
||||||
|
@ -68,7 +80,7 @@ class Application extends ApplicationBase
|
||||||
$database = $this->database;
|
$database = $this->database;
|
||||||
|
|
||||||
foreach (self::DATABASE_CONNECTIONS as $name) {
|
foreach (self::DATABASE_CONNECTIONS as $name) {
|
||||||
$section = 'Database.' . $name;
|
$section = "Database.{$name}";
|
||||||
|
|
||||||
if (!$config->contains($section)) {
|
if (!$config->contains($section)) {
|
||||||
throw new InvalidArgumentException("Database {$name} is not configured.");
|
throw new InvalidArgumentException("Database {$name} is not configured.");
|
||||||
|
@ -100,7 +112,7 @@ class Application extends ApplicationBase
|
||||||
$twig->addFunction('git_hash', [Application::class, 'gitCommitHash']);
|
$twig->addFunction('git_hash', [Application::class, 'gitCommitHash']);
|
||||||
$twig->addFunction('git_branch', [Application::class, 'gitBranch']);
|
$twig->addFunction('git_branch', [Application::class, 'gitBranch']);
|
||||||
|
|
||||||
$twig->vars(['app' => $this, 'tsession' => $_SESSION]);
|
$twig->var('app', $this);
|
||||||
|
|
||||||
$twig->addPath('nova', __DIR__ . '/../views/nova');
|
$twig->addPath('nova', __DIR__ . '/../views/nova');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
namespace Misuzu\Controllers;
|
namespace Misuzu\Controllers;
|
||||||
|
|
||||||
use Aitemu\RouterResponse;
|
use Aitemu\RouterResponse;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Misuzu\Application;
|
use Misuzu\Application;
|
||||||
use Misuzu\Database;
|
use Misuzu\Database;
|
||||||
use Misuzu\Net\IP;
|
use Misuzu\Net\IP;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
|
use Misuzu\Users\Session;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -36,23 +38,48 @@ class AuthController extends Controller
|
||||||
return ['error' => 'Invalid username or password!'];
|
return ['error' => 'Invalid username or password!'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$_SESSION['user_id'] = $user->user_id;
|
$session = new Session;
|
||||||
$_SESSION['username'] = $user->username;
|
$session->user_id = $user->user_id;
|
||||||
|
$session->session_ip = IP::unpack(IP::remote());
|
||||||
|
$session->user_agent = 'Misuzu Testing 1';
|
||||||
|
$session->expires_on = Carbon::now()->addMonth();
|
||||||
|
$session->session_key = bin2hex(random_bytes(32));
|
||||||
|
$session->save();
|
||||||
|
|
||||||
$user->user_chat_key = $_SESSION['chat_key'] = bin2hex(random_bytes(16));
|
Application::getInstance()->session = $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->user_chat_key = bin2hex(random_bytes(16));
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
setcookie('msz_tmp_id', $_SESSION['user_id'], time() + 604800, '/', '.flashii.net');
|
setcookie('msz_tmp_id', $user->user_id, time() + 604800, '/', '.flashii.net');
|
||||||
setcookie('msz_tmp_key', $_SESSION['chat_key'], time() + 604800, '/', '.flashii.net');
|
setcookie('msz_tmp_key', $user->user_chat_key, time() + 604800, '/', '.flashii.net');
|
||||||
|
|
||||||
return ['error' => 'You are now logged in!', 'next' => '/'];
|
return ['error' => 'You are now logged in!', 'next' => '/'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function setCookie(string $name, string $value, int $expires): void
|
||||||
|
{
|
||||||
|
setcookie(
|
||||||
|
"msz_{$name}",
|
||||||
|
$value,
|
||||||
|
time() + $expires,
|
||||||
|
'/',
|
||||||
|
'',
|
||||||
|
!empty($_SERVER['HTTPS']),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function hasRegistrations(?string $ipAddr = null): bool
|
private function hasRegistrations(?string $ipAddr = null): bool
|
||||||
{
|
{
|
||||||
$ipAddr = IP::unpack($ipAddr ?? IP::remote());
|
$ipAddr = IP::unpack($ipAddr ?? IP::remote());
|
||||||
|
|
||||||
if (User::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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +164,6 @@ class AuthController extends Controller
|
||||||
$user->register_ip = IP::unpack(IP::remote());
|
$user->register_ip = IP::unpack(IP::remote());
|
||||||
$user->last_ip = IP::unpack(IP::remote());
|
$user->last_ip = IP::unpack(IP::remote());
|
||||||
$user->user_country = get_country_code(IP::remote());
|
$user->user_country = get_country_code(IP::remote());
|
||||||
$user->user_registered = time();
|
|
||||||
$user->save();
|
$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'];
|
||||||
|
@ -145,8 +171,19 @@ class AuthController extends Controller
|
||||||
|
|
||||||
public function logout()
|
public function logout()
|
||||||
{
|
{
|
||||||
session_destroy();
|
$app = Application::getInstance();
|
||||||
return 'Logged out.<meta http-equiv="refresh" content="0; url=/">';
|
|
||||||
|
if ($app->session === null) {
|
||||||
|
echo "You aren't logged in.";
|
||||||
|
} else {
|
||||||
|
echo "You've been logged out.";
|
||||||
|
$this->setCookie('uid', '', -3600);
|
||||||
|
$this->setCookie('sid', '', -3600);
|
||||||
|
$app->session->delete();
|
||||||
|
$app->session = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<meta http-equiv="refresh" content="1; url=/">';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateUsername(string $username): string
|
private function validateUsername(string $username): string
|
||||||
|
|
|
@ -113,6 +113,16 @@ class TemplateEngine
|
||||||
$this->loader->addPath($path, $name);
|
$this->loader->addPath($path, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a render var.
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function var(string $name, $value): void
|
||||||
|
{
|
||||||
|
$this->vars[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets render vars.
|
* Sets render vars.
|
||||||
* @param array $vars
|
* @param array $vars
|
||||||
|
|
14
src/Users/Session.php
Normal file
14
src/Users/Session.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
namespace Misuzu\Users;
|
||||||
|
|
||||||
|
use Misuzu\Model;
|
||||||
|
|
||||||
|
class Session extends Model
|
||||||
|
{
|
||||||
|
protected $primaryKey = 'session_id';
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,9 @@ use Misuzu\Model;
|
||||||
class User extends Model
|
class User extends Model
|
||||||
{
|
{
|
||||||
protected $primaryKey = 'user_id';
|
protected $primaryKey = 'user_id';
|
||||||
public $timestamps = false;
|
|
||||||
|
public function sessions()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Session::class, 'user_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,13 @@
|
||||||
<div class="header__navigation">
|
<div class="header__navigation">
|
||||||
<a class="header__entry fa-home" href="/">home</a>
|
<a class="header__entry fa-home" href="/">home</a>
|
||||||
<a class="header__entry fa-comments" href="https://chat.flashii.net">chat</a>
|
<a class="header__entry fa-comments" href="https://chat.flashii.net">chat</a>
|
||||||
|
{% if app.session is not null %}
|
||||||
|
<a class="header__entry fa-sign-out" href="/auth/logout">logout</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<a class="header__user" href="/auth/{{ tsession is defined and tsession.username is defined ? 'logout' : 'login' }}">
|
<a class="header__user" href="{{ app.session is not null ? '/users/' ~ app.session.user_id : '/auth/login' }}">
|
||||||
<div class="header__username">{{ tsession is defined and tsession.username is defined ? tsession.username : 'login' }}</div>
|
<div class="header__username">{{ app.session is not null ? app.session.user.first.username : 'login' }}</div>
|
||||||
<div class="header__avatar" style="background-image: url('https://static.flash.moe/images/{{ tsession is defined and tsession.username is defined ? '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>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
'text': 'Contributor',
|
'text': 'Contributor',
|
||||||
},
|
},
|
||||||
'premium': {
|
'premium': {
|
||||||
'display': false,
|
'display': app.session is not null and app.session.user_id == profile.user_id,
|
||||||
'icon': 'fa-heart',
|
'icon': 'fa-user',
|
||||||
'text': 'Contributor',
|
'text': 'You!',
|
||||||
},
|
},
|
||||||
'banned': {
|
'banned': {
|
||||||
'display': false,
|
'display': false,
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
<div class="profile__username">{{ profile.username }}</div>
|
<div class="profile__username">{{ profile.username }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile__header-sub profile__dates">
|
<div class="profile__header-sub profile__dates">
|
||||||
<div class="profile__date--joined">Joined {{ profile.user_registered|date('r') }}</div>
|
<div class="profile__date--joined">Joined {{ profile.created_at.format('r') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endspaceless %}
|
{% endspaceless %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue