Migrated IDE platform; accepting nonexisting diffs to make IDE happy

This commit is contained in:
2019-02-27 21:39:31 +01:00
parent 1ffa164160
commit c8fe81d222
17 changed files with 1701 additions and 1665 deletions

View File

@ -1,147 +1,150 @@
<?php
$configurationFile = '../lucidAuth.config.php';
if (!file_exists($configurationFile)) {
throw new Exception(sprintf('Missing config file. Please rename \'%1$s.example\' to \'%1$s\' and edit it to reflect your setup.', explode('../', $configurationFile)[1]));
}
$settings = include_once($configurationFile);
try {
# switch ($settings->Database['Driver']) {
# case 'sqlite':
# $database = new PDO('sqlite:' . $settings->Database['Path']);
if (is_writable($settings->Sqlite['Path'])) {
$pdoDB = new PDO('sqlite:' . $settings->Sqlite['Path']);
} else {
throw new Exception(sprintf('Database file \'%1$s\' is not writable', $settings->Sqlite['Path']));
}
# }
}
catch (Exception $e) {
throw new Exception(sprintf('Unable to connect to database \'%1$s\'', $settings->Sqlite['Path']));
}
function authenticateLDAP (string $username, string $password) {
global $settings;
if (!empty($username) && !empty($password)) {
// Handle login requests
$ds = ldap_connect($settings->LDAP['Server'], $settings->LDAP['Port']);
// Strict namingconvention: only allow alphabetic characters
$sanitizedUsername = preg_replace('([^a-zA-Z]*)', '', $_POST['username']);
$qualifiedUsername = $settings->LDAP['Domain'] . '\\' . $sanitizedUsername;
if (@ldap_bind($ds, $qualifiedUsername, utf8_encode($_POST['password']))) {
// Successful authentication; get additional userdetails from authenticationsource
$ldapSearchResults = ldap_search($ds, $settings->LDAP['BaseDN'], "sAMAccountName=$sanitizedUsername");
$commonName = ldap_get_entries($ds, $ldapSearchResults)[0]['cn'][0];
// Create JWT-payload
$jwtPayload = [
'iat' => time(), // Issued at: time when the token was generated
'iss' => $_SERVER['SERVER_NAME'], // Issuer
'sub' => $qualifiedUsername, // Subject (ie. username)
'name' => $commonName // Common name (as retrieved from AD)
];
$secureToken = JWT::encode($jwtPayload, base64_decode($settings->JWT['PrivateKey_base64']));
return ['status' => 'Success', 'token' => $secureToken];
} else {
// LDAP authentication failed!
return ['status' => 'Fail', 'reason' => '1'];
}
} else {
// Empty username or passwords not allowed!
return ['status' => 'Fail', 'reason' => '1'];
}
}
function storeToken (string $secureToken, string $qualifiedUsername, string $httpHost) {
global $settings, $pdoDB;
// Save authentication token in database serverside
try {
$pdoQuery = $pdoDB->prepare('
INSERT INTO SecureToken (UserId, Value)
SELECT User.Id, :securetoken
FROM User
WHERE User.Username = :qualifiedusername
');
$pdoQuery->execute([
':securetoken' => $secureToken,
':qualifiedusername' => $qualifiedUsername
]);
}
catch (Exception $e) {
return ['status' => 'Fail', 'reason' => $e];
}
// Save authentication token in cookie clientside
$cookieDomain = array_values(array_filter($settings->Session['CookieDomains'], function ($value) use ($httpHost) {
// Check if $_SERVER['HTTP_HOST'] matches any of the configured domains (either explicitly or as a subdomain)
// This might seem backwards, but relying on $_SERVER directly allows spoofed values with potential security risks
return (strlen($value) > strlen($httpHost)) ? false : (0 === substr_compare($httpHost, $value, -strlen($value)));
}))[0];
if ($cookieDomain && setcookie('JWT', $secureToken, (time() + $settings->Session['Duration']), '/', '.' . $cookieDomain)) {
return ['status' => 'Success'];
} else {
return ['status' => 'Fail', 'reason' => 'Unable to store cookie(s)'];
}
}
function validateToken (string $secureToken) {
global $settings, $pdoDB;
// Decode provided authentication token
try {
$jwtPayload = JWT::decode($secureToken, base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
} catch (Exception $e) {
// Invalid token
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Provided token could not be decoded' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '1'];
}
if ((int)$jwtPayload->iat < (time() - (int)$settings->Session['Duration'])) {
// Expired token
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Provided token has expired' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '3'];
}
// Retrieve all authentication tokens from database matching username
$pdoQuery = $pdoDB->prepare('
SELECT SecureToken.Value
FROM SecureToken
LEFT JOIN User
ON (User.Id=SecureToken.UserId)
WHERE User.Username = :username
');
$pdoQuery->execute([
':username' => (string)$jwtPayload->sub
]);
foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
try {
$storedTokens[] = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
} catch (Exception $e) {
continue;
}
}
// Compare provided authentication token to all stored tokens in database
if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) {
return $value->iat === $jwtPayload->iat;
})) === 1) {
return ['status' => 'Success'];
} else {
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- No matching token in database' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '2'];
}
}
<?php
$configurationFile = '../lucidAuth.config.php';
if (!file_exists($configurationFile)) {
throw new Exception(sprintf('Missing config file. Please rename \'%1$s.example\' to \'%1$s\' and edit it to reflect your setup.', explode('../', $configurationFile)[1]));
}
$settings = include_once($configurationFile);
try {
# switch ($settings->Database['Driver']) {
# case 'sqlite':
# $database = new PDO('sqlite:' . $settings->Database['Path']);
if (is_writable($settings->Sqlite['Path'])) {
$pdoDB = new PDO('sqlite:' . $settings->Sqlite['Path']);
} else {
throw new Exception(sprintf('Database file \'%1$s\' is not writable', $settings->Sqlite['Path']));
}
# }
}
catch (Exception $e) {
throw new Exception(sprintf('Unable to connect to database \'%1$s\'', $settings->Sqlite['Path']));
}
function authenticateLDAP (string $username, string $password) {
global $settings;
if (!empty($username) && !empty($password)) {
// Handle login requests
$ds = ldap_connect($settings->LDAP['Server'], $settings->LDAP['Port']);
// Strict namingconvention: only allow alphabetic characters
$sanitizedUsername = preg_replace('([^a-zA-Z]*)', '', $_POST['username']);
$qualifiedUsername = $settings->LDAP['Domain'] . '\\' . $sanitizedUsername;
if (@ldap_bind($ds, $qualifiedUsername, utf8_encode($_POST['password']))) {
// Successful authentication; get additional userdetails from authenticationsource
$ldapSearchResults = ldap_search($ds, $settings->LDAP['BaseDN'], "sAMAccountName=$sanitizedUsername");
$commonName = ldap_get_entries($ds, $ldapSearchResults)[0]['cn'][0];
// Create JWT-payload
$jwtPayload = [
'iat' => time(), // Issued at: time when the token was generated
'iss' => $_SERVER['SERVER_NAME'], // Issuer
'sub' => $qualifiedUsername, // Subject (ie. username)
'name' => $commonName // Common name (as retrieved from AD)
];
$secureToken = JWT::encode($jwtPayload, base64_decode($settings->JWT['PrivateKey_base64']));
return ['status' => 'Success', 'token' => $secureToken];
} else {
// LDAP authentication failed!
return ['status' => 'Fail', 'reason' => '1'];
}
} else {
// Empty username or passwords not allowed!
return ['status' => 'Fail', 'reason' => '1'];
}
}
function storeToken (string $secureToken, string $qualifiedUsername, string $httpHost) {
global $settings, $pdoDB;
// Save authentication token in database serverside
try {
$pdoQuery = $pdoDB->prepare('
INSERT INTO SecureToken (UserId, Value)
SELECT User.Id, :securetoken
FROM User
WHERE User.Username = :qualifiedusername
');
$pdoQuery->execute([
':securetoken' => $secureToken,
':qualifiedusername' => $qualifiedUsername
]);
}
catch (Exception $e) {
return ['status' => 'Fail', 'reason' => $e];
}
// Save authentication token in cookie clientside
$cookieDomain = array_values(array_filter($settings->Session['CookieDomains'], function ($value) use ($httpHost) {
// Check if $_SERVER['HTTP_HOST'] matches any of the configured domains (either explicitly or as a subdomain)
// This might seem backwards, but relying on $_SERVER directly allows spoofed values with potential security risks
return (strlen($value) > strlen($httpHost)) ? false : (0 === substr_compare($httpHost, $value, -strlen($value)));
}))[0];
if ($cookieDomain && setcookie('JWT', $secureToken, (time() + $settings->Session['Duration']), '/', '.' . $cookieDomain)) {
return ['status' => 'Success'];
} else {
return ['status' => 'Fail', 'reason' => 'Unable to store cookie(s)'];
}
}
function validateToken (string $secureToken) {
global $settings, $pdoDB;
// Decode provided authentication token
try {
$jwtPayload = JWT::decode($secureToken, base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
} catch (Exception $e) {
// Invalid token
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Provided token could not be decoded' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '1'];
}
if ((int)$jwtPayload->iat < (time() - (int)$settings->Session['Duration'])) {
// Expired token
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Provided token has expired' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '3'];
}
// Retrieve all authentication tokens from database matching username
$pdoQuery = $pdoDB->prepare('
SELECT SecureToken.Value
FROM SecureToken
LEFT JOIN User
ON (User.Id=SecureToken.UserId)
WHERE User.Username = :username
');
$pdoQuery->execute([
':username' => (string)$jwtPayload->sub
]);
foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
try {
$storedTokens[] = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
} catch (Exception $e) {
continue;
}
}
// Compare provided authentication token to all stored tokens in database
if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) {
return $value->iat === $jwtPayload->iat;
})) === 1) {
return [
'status' => 'Success',
'name' => $jwtPayload->name
];
} else {
if ($settings->Debug['LogToFile']) {
file_put_contents('../validateToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Either no matching token or multiple matching tokens found in database' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => '2'];
}
}
?>

View File

@ -1,122 +1,110 @@
<?php
error_reporting(E_ALL & ~E_NOTICE);
$pageLayout['full'] = <<<'FULL'
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<title>lucidAuth</title>
<meta name="application-name" content="lucidAuth" />
<meta name="theme-color" content="#B50000" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<link href="misc/style.css" rel="stylesheet" />
<link href="misc/style.theme.css" rel="stylesheet" />
<link href="misc/style.button.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="misc/script.theme.js"></script>
<script src="misc/script.translation.js"></script>
</head>
<body>
<div id="horizon">
<div id="content">
<div class="logo">
<div class="left"><div class="middle">lucidAuth</div></div><div class="right"></div>
<div class="sub"><em>Respect</em> the unexpected; mitigate your risks</div>
</div>
<div class="main">
%1$s
</div>
</div>
</div>
</body>
</html>
FULL;
$pageLayout['bare'] = <<<'BARE'
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<title>lucidAuth</title>
<meta name="application-name" content="lucidAuth" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="misc/script.iframe.js"></script>
</head>
<body>
%1$s
</body>
</html>
BARE;
$contentLayout['login'] = <<<LOGIN
<script src="misc/script.index.js"></script>
<fieldset>
<legend>Login Details</legend>
<ul>
<li>
<label class="pre" for="username" data-translation="label_username">Gebruikersnaam:</label>
<input type="text" id="username" name="username" tabindex="100" />
<label for="username">@lucidAuth</label>
</li>
<li>
<label class="pre" for="password" data-translation="label_password">Wachtwoord:</label>
<input type="password" id="password" name="password" tabindex="200" />
</li>
<li>
<input type="hidden" id="ref" name="ref" value="{$_GET['ref']}" />
<button id="btnlogin" class="bttn-simple bttn-xs bttn-primary" tabindex="300" data-translation="button_login">login</button>
</li>
<li class="misc">
<span class="indent">&nbsp;</span>
</li>
<li class="misc">
<span class="indent" data-translation="span_credentialsavailable">Inloggegevens verkrijgbaar op aanvraag!</span>
</li>
</ul>
</fieldset>
<img src="/images/tag_lock.png" style="position: absolute; top: 175px; left: 20px;" alt="Secure!" />
LOGIN;
$contentLayout['manage'] = <<<'MANAGE'
<script src="misc/script.manage.js"></script>
<span id="user"><span data-translation="span_loggedinas">Ingelogd als</span>&nbsp;{$_SESSION['fullname']}&nbsp;---&nbsp;[<a id="linkplugindialog" tabindex="600" data-translation="link_plugin">Browser plugin</a><div id="pluginlogos"><span data-translation="label_selectbrowser" style="float: left; margin-left: 5px;">Select browser:</span><span style="font-size: 8px; float: right; margin-right: 5px; margin-top: 2px;">[v0.2.122.4]</span><br /><img id="linkpluginchrome" src="images/chrome_256x256.png" /><img id="linkpluginfirefox" src="images/firefox_256x256.png" /><img id="linkpluginopera" src="images/opera_256x256.png" /></div>]&nbsp;[<a id="linklanguage-en" href="#" tabindex="700">EN</a>&nbsp;<a id="linklanguage-nl" class="current" href="#" tabindex="700">NL</a>]&nbsp;[<a href="index.php?do=logout" tabindex="800" data-translation="link_logout">Log uit</a>]</span>
<!-- <fieldset style="clear: both;">
<legend>Beheer Account</legend>
<ul>
<li>
</li>
<li>
<button id="btnaliasadd" class="bttn-simple bttn-xs bttn-primary" tabindex="200" data-translation="button_add">voeg toe</button>
</li>
<li>
<label id="labelallaliases" class="pre" for="allaliases" data-translation="label_allaliases">Alle aliassen:</label><output id="aliasstats">[--]</output>
<select id="allaliases" size="10" multiple="multiple" tabindex="300">
</select>
</li>
<li>
<button id="btnaliasdelete" class="bttn-simple bttn-xs bttn-primary" tabindex="400" data-translation="button_delete">verwijder</button>
</li>
<li>
<button id="btnsync" class="bttn-simple bttn-xs bttn-primary" style="background-position: center;" tabindex="500" data-translation="button_sync">synchroniseer</button>
</li>
</ul>
</fieldset>
-->
MANAGE;
$contentLayout['dialog'] = <<<DIALOG
<ul class="dialog">
<li>
<!--REPL_DIALOGDESC-->
</li>
<li>
<button id="btnhome" class="bttn-simple bttn-xs bttn-primary" tabindex="400" data-translation="button_home">ga naar startpagina</button>
</li>
</ul>
DIALOG;
<?php
error_reporting(E_ALL & ~E_NOTICE);
$pageLayout['full'] = <<<'FULL'
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<title>lucidAuth</title>
<meta name="application-name" content="lucidAuth" />
<meta name="theme-color" content="#003399" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<link href="misc/style.css" rel="stylesheet" />
<link href="misc/style.theme.css" rel="stylesheet" />
<link href="misc/style.button.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="misc/script.theme.js"></script>
<script src="misc/script.translation.js"></script>
</head>
<body>
<div id="horizon">
<div id="content">
<div class="logo">
<div class="left"><div class="middle">lucidAuth</div></div><div class="right"></div>
<div class="sub"><em>Respect</em> the unexpected; mitigate your risks</div>
</div>
<div class="main">
%1$s
</div>
</div>
</div>
</body>
</html>
FULL;
$pageLayout['bare'] = <<<'BARE'
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<title>lucidAuth</title>
<meta name="application-name" content="lucidAuth" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script src="misc/script.iframe.js"></script>
</head>
<body>
%1$s
</body>
</html>
BARE;
$contentLayout['login'] = <<<'LOGIN'
<script src="misc/script.index.js"></script>
<fieldset>
<legend>Login Details</legend>
<ul>
<li>
<label class="pre" for="username" data-translation="label_username">Gebruikersnaam:</label>
<input type="text" id="username" name="username" tabindex="100" />
<label for="username">@lucidAuth</label>
</li>
<li>
<label class="pre" for="password" data-translation="label_password">Wachtwoord:</label>
<input type="password" id="password" name="password" tabindex="200" />
</li>
<li>
<input type="hidden" id="ref" name="ref" value="%1$s" />
<button id="btnlogin" class="bttn-simple bttn-xs bttn-primary" tabindex="300" data-translation="button_login">login</button>
</li>
<li class="misc">
<span class="indent">&nbsp;</span>
</li>
<li class="misc">
<span class="indent" data-translation="span_credentialsavailable">Inloggegevens verkrijgbaar op aanvraag!</span>
</li>
</ul>
</fieldset>
<img src="/images/tag_lock.png" style="position: absolute; top: 175px; left: 20px;" alt="Secure!" />
LOGIN;
$contentLayout['manage'] = <<<'MANAGE'
<script src="misc/script.manage.js"></script>
<span id="user"><span data-translation="span_loggedinas">Ingelogd als</span>&nbsp;%1$s&nbsp;---&nbsp;[<a id="linklanguage-en" href="#" tabindex="700">EN</a>&nbsp;<a id="linklanguage-nl" class="current" href="#" tabindex="700">NL</a>]&nbsp;[<a href="#" tabindex="800" data-translation="link_logout">Log uit</a>]</span>
<fieldset style="clear: both;">
<legend>Beheer Gebruikers</legend>
<ul>
<li>
</li>
<li>
<button id="btnaliasadd" class="bttn-simple bttn-xs bttn-primary" tabindex="200" data-translation="button_add">voeg toe</button>
</li>
<li>
<label id="labelallaliases" class="pre" for="allaliases" data-translation="label_allaliases">Alle aliassen:</label><output id="aliasstats">[--]</output>
<select id="allaliases" size="10" multiple="multiple" tabindex="300">
</select>
</li>
<li>
<button id="btnaliasdelete" class="bttn-simple bttn-xs bttn-primary" tabindex="400" data-translation="button_delete">verwijder</button>
</li>
<li>
<button id="btnsync" class="bttn-simple bttn-xs bttn-primary" style="background-position: center;" tabindex="500" data-translation="button_sync">synchroniseer</button>
</li>
</ul>
</fieldset>
MANAGE;
?>