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->rollback(__DIR__ . '/database');
|
||||
|
||||
foreach ($migrator->getNotes() as $note) {
|
||||
echo strip_tags($note) . PHP_EOL;
|
||||
|
|
|
@ -5,11 +5,15 @@ use Aitemu\RouterRequest;
|
|||
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
ob_start('ob_gzhandler');
|
||||
//ob_start('ob_gzhandler');
|
||||
|
||||
$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();
|
||||
|
||||
echo $app->router->resolve(
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace Misuzu;
|
|||
|
||||
use Aitemu\RouteCollection;
|
||||
use Misuzu\Config\ConfigManager;
|
||||
use Misuzu\Users\Session;
|
||||
use UnexpectedValueException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
@ -22,9 +23,14 @@ class Application extends ApplicationBase
|
|||
*/
|
||||
private const DATABASE_CONNECTIONS = [
|
||||
'mysql-main',
|
||||
//'mysql-ayase',
|
||||
];
|
||||
|
||||
/**
|
||||
* Session instance.
|
||||
* @var \Misuzu\Users\Session
|
||||
*/
|
||||
public $session = null;
|
||||
|
||||
/**
|
||||
* Constructor, called by ApplicationBase::start() which also passes the arguments through.
|
||||
* @param ?string $configFile
|
||||
|
@ -36,9 +42,6 @@ class Application extends ApplicationBase
|
|||
ExceptionHandler::register();
|
||||
ExceptionHandler::debug($this->debugMode);
|
||||
$this->addModule('config', new ConfigManager($configFile));
|
||||
|
||||
// temporary session system
|
||||
session_start();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
|
@ -46,6 +49,15 @@ class Application extends ApplicationBase
|
|||
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.
|
||||
*/
|
||||
|
@ -68,7 +80,7 @@ class Application extends ApplicationBase
|
|||
$database = $this->database;
|
||||
|
||||
foreach (self::DATABASE_CONNECTIONS as $name) {
|
||||
$section = 'Database.' . $name;
|
||||
$section = "Database.{$name}";
|
||||
|
||||
if (!$config->contains($section)) {
|
||||
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_branch', [Application::class, 'gitBranch']);
|
||||
|
||||
$twig->vars(['app' => $this, 'tsession' => $_SESSION]);
|
||||
$twig->var('app', $this);
|
||||
|
||||
$twig->addPath('nova', __DIR__ . '/../views/nova');
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
namespace Misuzu\Controllers;
|
||||
|
||||
use Aitemu\RouterResponse;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Misuzu\Application;
|
||||
use Misuzu\Database;
|
||||
use Misuzu\Net\IP;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\Session;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
@ -36,23 +38,48 @@ class AuthController extends Controller
|
|||
return ['error' => 'Invalid username or password!'];
|
||||
}
|
||||
|
||||
$_SESSION['user_id'] = $user->user_id;
|
||||
$_SESSION['username'] = $user->username;
|
||||
$session = new Session;
|
||||
$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();
|
||||
|
||||
setcookie('msz_tmp_id', $_SESSION['user_id'], time() + 604800, '/', '.flashii.net');
|
||||
setcookie('msz_tmp_key', $_SESSION['chat_key'], time() + 604800, '/', '.flashii.net');
|
||||
setcookie('msz_tmp_id', $user->user_id, time() + 604800, '/', '.flashii.net');
|
||||
setcookie('msz_tmp_key', $user->user_chat_key, time() + 604800, '/', '.flashii.net');
|
||||
|
||||
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
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
|
@ -137,7 +164,6 @@ class AuthController extends Controller
|
|||
$user->register_ip = IP::unpack(IP::remote());
|
||||
$user->last_ip = IP::unpack(IP::remote());
|
||||
$user->user_country = get_country_code(IP::remote());
|
||||
$user->user_registered = time();
|
||||
$user->save();
|
||||
|
||||
return ['error' => 'Welcome to Flashii! You may now log in.', 'next' => '/auth/login'];
|
||||
|
@ -145,8 +171,19 @@ class AuthController extends Controller
|
|||
|
||||
public function logout()
|
||||
{
|
||||
session_destroy();
|
||||
return 'Logged out.<meta http-equiv="refresh" content="0; url=/">';
|
||||
$app = Application::getInstance();
|
||||
|
||||
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
|
||||
|
|
|
@ -113,6 +113,16 @@ class TemplateEngine
|
|||
$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.
|
||||
* @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
|
||||
{
|
||||
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">
|
||||
<a class="header__entry fa-home" href="/">home</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>
|
||||
<a class="header__user" href="/auth/{{ tsession is defined and tsession.username is defined ? 'logout' : 'login' }}">
|
||||
<div class="header__username">{{ tsession is defined and tsession.username is defined ? tsession.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>
|
||||
<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__avatar" style="background-image: url('https://static.flash.moe/images/{{ app.session is not null ? 'discord-logo' : 'nova-none' }}.png')"></div>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
'text': 'Contributor',
|
||||
},
|
||||
'premium': {
|
||||
'display': false,
|
||||
'icon': 'fa-heart',
|
||||
'text': 'Contributor',
|
||||
'display': app.session is not null and app.session.user_id == profile.user_id,
|
||||
'icon': 'fa-user',
|
||||
'text': 'You!',
|
||||
},
|
||||
'banned': {
|
||||
'display': false,
|
||||
|
@ -45,7 +45,7 @@
|
|||
<div class="profile__username">{{ profile.username }}</div>
|
||||
</div>
|
||||
<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>
|
||||
{% endspaceless %}
|
||||
|
|
Loading…
Add table
Reference in a new issue