Use session/cookie based authentication with login form instead of basic auth

This commit is contained in:
Bernd Bestel 2017-06-04 18:28:08 +02:00
parent bd3155d39b
commit 23146417e6
8 changed files with 153 additions and 11 deletions

View File

@ -101,4 +101,48 @@ class Grocy
return self::$InstalledVersion; return self::$InstalledVersion;
} }
/**
* @return boolean
*/
public static function IsValidSession($sessionKey)
{
if ($sessionKey === null || empty($sessionKey))
{
return false;
}
else
{
return file_exists(__DIR__ . "/data/sessions/$sessionKey.txt");
}
}
/**
* @return string
*/
public static function CreateSession()
{
if (!file_exists(__DIR__ . '/data/sessions'))
{
mkdir(__DIR__ . '/data/sessions');
}
$now = time();
foreach (new FilesystemIterator(__DIR__ . '/data/sessions') as $file)
{
if ($now - $file->getCTime() >= 2678400) //31 days
{
unlink(__DIR__ . '/data/sessions/' . $file->getFilename());
}
}
$newSessionKey = uniqid() . uniqid() . uniqid();
file_put_contents(__DIR__ . "/data/sessions/$newSessionKey.txt", '');
return $newSessionKey;
}
public static function RemoveSession($sessionKey)
{
unlink(__DIR__ . "/data/sessions/$sessionKey.txt");
}
} }

View File

@ -2,7 +2,6 @@
"require": { "require": {
"slim/slim": "^3.8", "slim/slim": "^3.8",
"slim/php-view": "^2.2", "slim/php-view": "^2.2",
"morris/lessql": "^0.3.4", "morris/lessql": "^0.3.4"
"tuupola/slim-basic-auth": "^2.2"
} }
} }

View File

@ -30,6 +30,7 @@
<Compile Include="GrocyDbMigrator.php" /> <Compile Include="GrocyDbMigrator.php" />
<Compile Include="index.php" /> <Compile Include="index.php" />
<Compile Include="views\consume.php" /> <Compile Include="views\consume.php" />
<Compile Include="views\login.php" />
<Compile Include="views\inventory.php" /> <Compile Include="views\inventory.php" />
<Compile Include="views\shoppinglistform.php" /> <Compile Include="views\shoppinglistform.php" />
<Compile Include="views\shoppinglist.php" /> <Compile Include="views\shoppinglist.php" />
@ -60,6 +61,7 @@
<Content Include="views\consume.js" /> <Content Include="views\consume.js" />
<Content Include="views\dashboard.js" /> <Content Include="views\dashboard.js" />
<Content Include="views\inventory.js" /> <Content Include="views\inventory.js" />
<Content Include="views\login.js" />
<Content Include="views\shoppinglistform.js" /> <Content Include="views\shoppinglistform.js" />
<Content Include="views\shoppinglist.js" /> <Content Include="views\shoppinglist.js" />
<Content Include="views\purchase.js" /> <Content Include="views\purchase.js" />

View File

@ -15,6 +15,7 @@ require_once __DIR__ . '/GrocyPhpHelper.php';
$app = new \Slim\App(new \Slim\Container([ $app = new \Slim\App(new \Slim\Container([
'settings' => [ 'settings' => [
'displayErrorDetails' => true, 'displayErrorDetails' => true,
'determineRouteBeforeAppMiddleware' => true
], ],
])); ]));
$container = $app->getContainer(); $container = $app->getContainer();
@ -22,18 +23,65 @@ $container['renderer'] = new PhpRenderer('./views');
if (!Grocy::IsDemoInstallation()) if (!Grocy::IsDemoInstallation())
{ {
$isHttpsReverseProxied = !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'; $sessionMiddleware = function(Request $request, Response $response, callable $next)
$app->add(new \Slim\Middleware\HttpBasicAuthentication([ {
'realm' => 'grocy', $route = $request->getAttribute('route');
'secure' => !$isHttpsReverseProxied, $routeName = $route->getName();
'users' => [
HTTP_USER => HTTP_PASSWORD if (!Grocy::IsValidSession($_COOKIE['grocy_session']) && $routeName !== 'login')
] {
])); $response = $response->withRedirect('/login');
}
else
{
$response = $next($request, $response);
}
return $response;
};
$app->add($sessionMiddleware);
} }
$db = Grocy::GetDbConnection(); $db = Grocy::GetDbConnection();
$app->get('/login', function(Request $request, Response $response)
{
return $this->renderer->render($response, '/layout.php', [
'title' => 'Login',
'contentPage' => 'login.php'
]);
})->setName('login');
$app->post('/login', function(Request $request, Response $response)
{
$postParams = $request->getParsedBody();
if (isset($postParams['username']) && isset($postParams['password']))
{
if ($postParams['username'] === HTTP_USER && $postParams['password'] === HTTP_PASSWORD)
{
$sessionKey = Grocy::CreateSession();
setcookie('grocy_session', $sessionKey, time()+2592000); //30 days
return $response->withRedirect('/');
}
else
{
return $response->withRedirect('/login?invalid=true');
}
}
else
{
return $response->withRedirect('/login?invalid=true');
}
})->setName('login');
$app->get('/logout', function(Request $request, Response $response)
{
Grocy::RemoveSession($_COOKIE['grocy_session']);
return $response->withRedirect('/');
});
$app->get('/', function(Request $request, Response $response) use($db) $app->get('/', function(Request $request, Response $response) use($db)
{ {
$db = Grocy::GetDbConnection(true); //For database schema migration $db = Grocy::GetDbConnection(true); //For database schema migration

View File

@ -1 +1 @@
1.3.0 1.4.0

View File

@ -38,6 +38,14 @@
<a class="navbar-brand" href="/">grocy</a> <a class="navbar-brand" href="/">grocy</a>
</div> </div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;Logout</a>
</li>
</ul>
</div>
<div id="navbar-mobile" class="navbar-collapse collapse"> <div id="navbar-mobile" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
@ -71,6 +79,12 @@
</li> </li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;Logout</a>
</li>
</ul>
</div> </div>
</div> </div>
</nav> </nav>

12
views/login.js Normal file
View File

@ -0,0 +1,12 @@
$(function()
{
$('.logout-button').hide();
$('#username').focus();
if (Grocy.GetUriParam('invalid') === 'true')
{
$('#login-error').text('Invalid credentials, please try again.');
$('#login-error').show();
}
});

23
views/login.php Normal file
View File

@ -0,0 +1,23 @@
<div class="col-md-4 col-md-offset-5 main">
<h1 class="page-header text-center">Login</h1>
<form method="post" action="/login" id="login-form">
<div class="form-group">
<label for="name">Username</label>
<input type="text" class="form-control" required id="username" name="username" />
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="name">Password</label>
<input type="password" class="form-control" required id="password" name="password" />
<div id="login-error" class="help-block with-errors"></div>
</div>
<button id="login-button" type="submit" class="btn btn-default">Login</button>
</form>
</div>