Revise session handling to prepare API authentication via token

This commit is contained in:
Bernd Bestel 2018-04-19 20:44:49 +02:00
parent 0c85342404
commit eae5b8bad9
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
9 changed files with 101 additions and 70 deletions

26
app.php
View File

@ -3,17 +3,14 @@
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use \Grocy\Middleware\SessionAuthMiddleware;
use \Grocy\Helpers\UrlManager;
use \Grocy\Services\ApplicationService;
use \Grocy\Controllers\LoginController;
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/data/config.php';
// Setup base application
if (PHP_SAPI !== 'cli')
{
$appContainer = new \Slim\Container([
$appContainer = new \Slim\Container([
'settings' => [
'displayErrorDetails' => true,
'determineRouteBeforeAppMiddleware' => true
@ -22,27 +19,22 @@ if (PHP_SAPI !== 'cli')
{
return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache');
},
'LoginControllerInstance' => function($container)
{
return new LoginController($container, 'grocy_session');
},
'UrlManager' => function($container)
{
return new UrlManager(BASE_URL);
}
]);
]);
$app = new \Slim\App($appContainer);
$app = new \Slim\App($appContainer);
}
else
if (PHP_SAPI === 'cli')
{
$app = new \Slim\App();
$app->add(\pavlakis\cli\CliRequest::class);
}
// Add session handling if this is not a demo installation
$applicationService = new ApplicationService();
if (!$applicationService->IsDemoInstallation())
{
$app->add(SessionAuthMiddleware::class);
}
require_once __DIR__ . '/routes.php';
$app->run();

View File

@ -9,13 +9,15 @@ use \Grocy\Services\DemoDataGeneratorService;
class LoginController extends BaseController
{
public function __construct(\Slim\Container $container)
public function __construct(\Slim\Container $container, string $sessionCookieName)
{
parent::__construct($container);
$this->SessionService = new SessionService();
$this->SessionCookieName = $sessionCookieName;
}
protected $SessionService;
protected $SessionCookieName;
public function ProcessLogin(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
@ -25,7 +27,7 @@ class LoginController extends BaseController
if ($postParams['username'] === HTTP_USER && $postParams['password'] === HTTP_PASSWORD)
{
$sessionKey = $this->SessionService->CreateSession();
setcookie('grocy_session', $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
setcookie($this->SessionCookieName, $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
}
@ -47,7 +49,7 @@ class LoginController extends BaseController
public function Logout(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
$this->SessionService->RemoveSession($_COOKIE['grocy_session']);
$this->SessionService->RemoveSession($_COOKIE[$this->SessionCookieName]);
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
}
@ -66,4 +68,9 @@ class LoginController extends BaseController
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/stockoverview'));
}
public function GetSessionCookieName()
{
return $this->SessionCookieName;
}
}

View File

@ -61,3 +61,14 @@ function GetClassConstants($className)
$r = new ReflectionClass($className);
return $r->getConstants();
}
function RandomString($length, $allowedChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$randomString = '';
for ($i = 0; $i < $length; $i++)
{
$randomString .= $allowedChars[rand(0, strlen($allowedChars) - 1)];
}
return $randomString;
}

View File

@ -2,11 +2,16 @@
namespace Grocy\Middleware;
use \Grocy\Services\ApplicationService;
class BaseMiddleware
{
public function __construct(\Slim\Container $container) {
public function __construct(\Slim\Container $container)
{
$this->AppContainer = $container;
$this->ApplicationService = new ApplicationService();
}
protected $AppContainer;
protected $ApplicationService;
}

View File

@ -13,7 +13,7 @@ class CliMiddleware extends BaseMiddleware
}
else
{
$response = $next($request, $response, $next);
$response = $next($request, $response);
return $response->withHeader('Content-Type', 'text/plain');
}
}

View File

@ -6,7 +6,7 @@ class JsonMiddleware extends BaseMiddleware
{
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next)
{
$response = $next($request, $response, $next);
$response = $next($request, $response);
return $response->withHeader('Content-Type', 'application/json');
}
}

View File

@ -6,19 +6,27 @@ use \Grocy\Services\SessionService;
class SessionAuthMiddleware extends BaseMiddleware
{
public function __construct(\Slim\Container $container, string $sessionCookieName)
{
parent::__construct($container);
$this->SessionCookieName = $sessionCookieName;
}
protected $SessionCookieName;
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next)
{
$route = $request->getAttribute('route');
$routeName = $route->getName();
if ($routeName === 'root')
if ($routeName === 'root' || $this->ApplicationService->IsDemoInstallation())
{
$response = $next($request, $response);
}
else
{
$sessionService = new SessionService();
if ((!isset($_COOKIE['grocy_session']) || !$sessionService->IsValidSession($_COOKIE['grocy_session'])) && $routeName !== 'login')
if ((!isset($_COOKIE[$this->SessionCookieName]) || !$sessionService->IsValidSession($_COOKIE[$this->SessionCookieName])) && $routeName !== 'login')
{
$response = $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login'));
}

View File

@ -2,48 +2,51 @@
use \Grocy\Middleware\JsonMiddleware;
use \Grocy\Middleware\CliMiddleware;
use \Grocy\Middleware\SessionAuthMiddleware;
// Base route
$app->get('/', 'Grocy\Controllers\LoginController:Root')->setName('root');
$app->group('', function()
{
// Base route
$this->get('/', 'LoginControllerInstance:Root')->setName('root');
// Login routes
$app->get('/login', 'Grocy\Controllers\LoginController:LoginPage')->setName('login');
$app->post('/login', 'Grocy\Controllers\LoginController:ProcessLogin')->setName('login');
$app->get('/logout', 'Grocy\Controllers\LoginController:Logout');
// Login routes
$this->get('/login', 'LoginControllerInstance:LoginPage')->setName('login');
$this->post('/login', 'LoginControllerInstance:ProcessLogin')->setName('login');
$this->get('/logout', 'LoginControllerInstance:Logout');
// Stock routes
$app->get('/stockoverview', 'Grocy\Controllers\StockController:Overview');
$app->get('/purchase', 'Grocy\Controllers\StockController:Purchase');
$app->get('/consume', 'Grocy\Controllers\StockController:Consume');
$app->get('/inventory', 'Grocy\Controllers\StockController:Inventory');
// Stock routes
$this->get('/stockoverview', 'Grocy\Controllers\StockController:Overview');
$this->get('/purchase', 'Grocy\Controllers\StockController:Purchase');
$this->get('/consume', 'Grocy\Controllers\StockController:Consume');
$this->get('/inventory', 'Grocy\Controllers\StockController:Inventory');
$app->get('/products', 'Grocy\Controllers\StockController:ProductsList');
$app->get('/product/{productId}', 'Grocy\Controllers\StockController:ProductEditForm');
$this->get('/products', 'Grocy\Controllers\StockController:ProductsList');
$this->get('/product/{productId}', 'Grocy\Controllers\StockController:ProductEditForm');
$app->get('/locations', 'Grocy\Controllers\StockController:LocationsList');
$app->get('/location/{locationId}', 'Grocy\Controllers\StockController:LocationEditForm');
$this->get('/locations', 'Grocy\Controllers\StockController:LocationsList');
$this->get('/location/{locationId}', 'Grocy\Controllers\StockController:LocationEditForm');
$app->get('/quantityunits', 'Grocy\Controllers\StockController:QuantityUnitsList');
$app->get('/quantityunit/{quantityunitId}', 'Grocy\Controllers\StockController:QuantityUnitEditForm');
$this->get('/quantityunits', 'Grocy\Controllers\StockController:QuantityUnitsList');
$this->get('/quantityunit/{quantityunitId}', 'Grocy\Controllers\StockController:QuantityUnitEditForm');
$app->get('/shoppinglist', 'Grocy\Controllers\StockController:ShoppingList');
$app->get('/shoppinglistitem/{itemId}', 'Grocy\Controllers\StockController:ShoppingListItemEditForm');
$this->get('/shoppinglist', 'Grocy\Controllers\StockController:ShoppingList');
$this->get('/shoppinglistitem/{itemId}', 'Grocy\Controllers\StockController:ShoppingListItemEditForm');
// Habit routes
$app->get('/habitsoverview', 'Grocy\Controllers\HabitsController:Overview');
$app->get('/habittracking', 'Grocy\Controllers\HabitsController:TrackHabitExecution');
// Habit routes
$this->get('/habitsoverview', 'Grocy\Controllers\HabitsController:Overview');
$this->get('/habittracking', 'Grocy\Controllers\HabitsController:TrackHabitExecution');
$app->get('/habits', 'Grocy\Controllers\HabitsController:HabitsList');
$app->get('/habit/{habitId}', 'Grocy\Controllers\HabitsController:HabitEditForm');
$this->get('/habits', 'Grocy\Controllers\HabitsController:HabitsList');
$this->get('/habit/{habitId}', 'Grocy\Controllers\HabitsController:HabitEditForm');
// Batterry routes
$app->get('/batteriesoverview', 'Grocy\Controllers\BatteriesController:Overview');
$app->get('/batterytracking', 'Grocy\Controllers\BatteriesController:TrackChargeCycle');
$app->get('/batteries', 'Grocy\Controllers\BatteriesController:BatteriesList');
$app->get('/battery/{batteryId}', 'Grocy\Controllers\BatteriesController:BatteryEditForm');
// Batterry routes
$this->get('/batteriesoverview', 'Grocy\Controllers\BatteriesController:Overview');
$this->get('/batterytracking', 'Grocy\Controllers\BatteriesController:TrackChargeCycle');
$this->get('/batteries', 'Grocy\Controllers\BatteriesController:BatteriesList');
$this->get('/battery/{batteryId}', 'Grocy\Controllers\BatteriesController:BatteryEditForm');
})->add(new SessionAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName()));
$app->group('/api', function()
{
@ -65,7 +68,7 @@ $app->group('/api', function()
$this->get('/batteries/track-charge-cycle/{batteryId}', 'Grocy\Controllers\BatteriesApiController:TrackChargeCycle');
$this->get('/batteries/get-battery-details/{batteryId}', 'Grocy\Controllers\BatteriesApiController:BatteryDetails');
})->add(JsonMiddleware::class);
})->add(new SessionAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName()))->add(JsonMiddleware::class);
$app->group('/cli', function()
{

View File

@ -24,11 +24,11 @@ class SessionService extends BaseService
*/
public function CreateSession()
{
$newSessionKey = uniqid() . uniqid() . uniqid();
$newSessionKey = $this->GenerateSessionKey();
$sessionRow = $this->Database->sessions()->createRow(array(
'session_key' => $newSessionKey,
'expires' => time() + 2592000 //30 days
'expires' => time() + 2592000 // 30 days
));
$sessionRow->save();
@ -39,4 +39,9 @@ class SessionService extends BaseService
{
$this->Database->sessions()->where('session_key', $sessionKey)->delete();
}
private function GenerateSessionKey()
{
return RandomString(50);
}
}