Periodic merge upstream #2
@ -29,4 +29,4 @@ Forward Authentication for use with proxies (caddy, nginx, traefik, etc)
|
||||
The domainname of the website made in step 3, needs to match the domainname (*ignoring subdomains, if any*) of the resource utilizing this authentication proxy.
|
||||
|
||||
## Questions or bugs
|
||||
Feel free to open issues in this repository (or in its mirror on [GitHub](#)).
|
||||
Feel free to open issues in this repository.
|
@ -66,11 +66,11 @@ function storeToken (string $secureToken, string $qualifiedUsername, string $htt
|
||||
INSERT INTO SecureToken (UserId, Value)
|
||||
SELECT User.Id, :securetoken
|
||||
FROM User
|
||||
WHERE User.Username = :qualifiedusername
|
||||
WHERE LOWER(User.Username) = :qualifiedusername
|
||||
');
|
||||
$pdoQuery->execute([
|
||||
':securetoken' => $secureToken,
|
||||
':qualifiedusername' => $qualifiedUsername
|
||||
':qualifiedusername' => strtolower($qualifiedUsername)
|
||||
]);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
@ -114,18 +114,19 @@ function validateToken (string $secureToken) {
|
||||
|
||||
// Retrieve all authentication tokens from database matching username
|
||||
$pdoQuery = $pdoDB->prepare('
|
||||
SELECT SecureToken.Value
|
||||
SELECT User.Id, SecureToken.Value
|
||||
FROM SecureToken
|
||||
LEFT JOIN User
|
||||
ON (User.Id=SecureToken.UserId)
|
||||
WHERE User.Username = :username
|
||||
WHERE LOWER(User.Username) = :username
|
||||
');
|
||||
$pdoQuery->execute([
|
||||
':username' => (string)$jwtPayload->sub
|
||||
':username' => (string) strtolower($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']);
|
||||
$currentUserId = $row['Id'];
|
||||
} catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
@ -135,10 +136,14 @@ function validateToken (string $secureToken) {
|
||||
if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) {
|
||||
return $value->iat === $jwtPayload->iat;
|
||||
})) === 1) {
|
||||
return ['status' => 'Success'];
|
||||
return [
|
||||
'status' => 'Success',
|
||||
'name' => $jwtPayload->name,
|
||||
'uid' => $currentUserId
|
||||
];
|
||||
} 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);
|
||||
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'];
|
||||
}
|
||||
|
@ -9,15 +9,11 @@ $pageLayout['full'] = <<<'FULL'
|
||||
<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" />
|
||||
<meta name="theme-color" content="#003399" />
|
||||
<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>
|
||||
@ -36,6 +32,46 @@ $pageLayout['full'] = <<<'FULL'
|
||||
</html>
|
||||
FULL;
|
||||
|
||||
$pageLayout['full_alt'] = <<<'FULL_ALT'
|
||||
<!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 href="misc/style.css" rel="stylesheet" />
|
||||
<link href="misc/style.panes.css" rel="stylesheet" />
|
||||
<link href="misc/style.button.css" rel="stylesheet" />
|
||||
<link href="misc/style.theme.css" rel="stylesheet" />
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
|
||||
<script src="misc/script.translation.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<section id="theme" class="content">
|
||||
<div class="columns">
|
||||
<main class="main">
|
||||
<header class="header">
|
||||
<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>
|
||||
%1$s
|
||||
</header>
|
||||
<section>
|
||||
%2$s
|
||||
</section>
|
||||
</main>
|
||||
<aside class="sidebar-first"></aside>
|
||||
<aside class="sidebar-second"></aside>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
FULL_ALT;
|
||||
|
||||
$pageLayout['bare'] = <<<'BARE'
|
||||
<!DOCTYPE html>
|
||||
<html lang="nl">
|
||||
@ -52,72 +88,64 @@ $pageLayout['bare'] = <<<'BARE'
|
||||
</html>
|
||||
BARE;
|
||||
|
||||
$contentLayout['login'] = <<<LOGIN
|
||||
$contentLayout['login'] = <<<'LOGIN'
|
||||
<script src="misc/script.index.js"></script>
|
||||
<fieldset>
|
||||
<legend>Login Details</legend>
|
||||
<section>
|
||||
<ul>
|
||||
<li class="misc">
|
||||
<span class="indent"> </span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="pre" for="username" data-translation="label_username">Gebruikersnaam:</label>
|
||||
<label class="pre" for="username" data-translation="label_username">Username:</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>
|
||||
<label class="pre" for="password" data-translation="label_password">Password:</label>
|
||||
<input type="password" id="password" name="password" tabindex="200" />
|
||||
</li>
|
||||
<li>
|
||||
<input type="hidden" id="ref" name="ref" value="{$_GET['ref']}" />
|
||||
<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"> </span>
|
||||
</li>
|
||||
<li class="misc">
|
||||
<span class="indent" data-translation="span_credentialsavailable">Inloggegevens verkrijgbaar op aanvraag!</span>
|
||||
<span class="indent" data-translation="span_credentialsavailable">Login credentials available upon request!</span>
|
||||
</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
</section>
|
||||
<img src="/images/tag_lock.png" style="position: absolute; top: 175px; left: 20px;" alt="Secure!" />
|
||||
LOGIN;
|
||||
|
||||
$contentLayout['manage'] = <<<MANAGE
|
||||
$contentLayout['manage']['header'] = <<<'MANAGE_HEADER'
|
||||
<script src="misc/script.editable.table.js"></script>
|
||||
<script src="misc/script.manage.js"></script>
|
||||
<span id="user"><span data-translation="span_loggedinas">Ingelogd als</span> {$_SESSION['fullname']} --- [<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>] [<a id="linklanguage-en" href="#" tabindex="700">EN</a> <a id="linklanguage-nl" class="current" href="#" tabindex="700">NL</a>] [<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>
|
||||
<span id="user"><span data-translation="span_loggedinas">Logged in as</span> %1$s --- [<a id="linklanguage-en" href="#" tabindex="700">EN</a> <a id="linklanguage-nl" class="current" href="#" tabindex="700">NL</a>] [<a href="#" tabindex="800" data-translation="link_logout">Logout</a>]</span>
|
||||
<ul style="clear: both; margin-top: 20px;">
|
||||
<li class="buttons">
|
||||
<button id="btnnewuser" class="bttn-simple bttn-xs bttn-primary" data-translation="button_new">new</button>
|
||||
|
||||
<button id="btnsave" class="bttn-simple bttn-xs bttn-primary" data-translation="button_save">save</button>
|
||||
<button id="btncancel" class="bttn-simple bttn-xs bttn-primary" data-translation="button_cancel">cancel</button>
|
||||
</li>
|
||||
</ul>
|
||||
DIALOG;
|
||||
|
||||
MANAGE_HEADER;
|
||||
$contentLayout['manage']['section'] = <<<'MANAGE_SECTION'
|
||||
<ul>
|
||||
<li>
|
||||
<table id="usertable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="immutable" data-translation="th_username">Username</th>
|
||||
<th class="immutable" data-translation="th_role">Role</th>
|
||||
<th class="immutable" data-translation="th_manage">Manage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%1$s
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
MANAGE_SECTION;
|
||||
|
||||
?>
|
@ -3,15 +3,18 @@
|
||||
|
||||
include_once('../include/lucidAuth.functions.php');
|
||||
|
||||
echo $settings->Debug['Verbose'];
|
||||
|
||||
if ($_POST['do'] == 'login') {
|
||||
$result = authenticateLDAP($_POST['username'], $_POST['password']);
|
||||
if ($result['status'] == 'Success') {
|
||||
if ($result['status'] === 'Success') {
|
||||
// Store authentication token; in database serverside & in cookie clientside
|
||||
if (storeToken($result['token'], $settings->LDAP['Domain'] . '\\' . $_POST['username'], $_SERVER['HTTP_HOST'])['status'] !== 'Success') {
|
||||
// Since this action is only ever called through an AJAX-request; return JSON object
|
||||
echo '{"Result":"Fail","Reason":"Failed storing authentication token in database and/or cookie"}' . PHP_EOL;
|
||||
// Return JSON object
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"Result" => "Failure",
|
||||
"Reason" => "Failed storing authentication token in database and/or cookie"
|
||||
]);
|
||||
# echo '{"Result":"Fail","Reason":"Failed storing authentication token in database and/or cookie"}' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -21,33 +24,50 @@
|
||||
$proxyHeaders = json_decode(base64_decode($_POST['ref']), JSON_OBJECT_AS_ARRAY);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// Since this action is only ever called through an AJAX-request; return JSON object
|
||||
echo '{"Result":"Fail","Reason":"Original request URI lost in transition"}' . PHP_EOL;
|
||||
// Return JSON object
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"Result" => "Failure",
|
||||
"Reason" => "Original request-URI lost in transition"
|
||||
]);
|
||||
# echo '{"Result":"Fail","Reason":"Original request URI lost in transition"}' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
$originalUri = !empty($proxyHeaders) ? $proxyHeaders['XForwardedProto'] . '://' . $proxyHeaders['XForwardedHost'] . $proxyHeaders['XForwardedUri'] : 'lucidAuth.manage.php';
|
||||
|
||||
// Since this request is only ever called through an AJAX-request; return JSON object
|
||||
// Return JSON object
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"Result" => "Success",
|
||||
"Location" => $originalUri,
|
||||
"CrossDomainLogin" => $settings->Session['CrossDomainLogin']
|
||||
]);
|
||||
# echo sprintf('{"Result":"Success","Location":"%1$s","CrossDomainLogin":%2$s}', $originalUri, $settings->Session['CrossDomainLogin'] ? 'True' : 'False') . PHP_EOL;
|
||||
} else {
|
||||
switch ($result['reason']) {
|
||||
case '1':
|
||||
echo '{"Result":"Fail","Reason":"Invalid username and/or password"}' . PHP_EOL;
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"Result" => "Failure",
|
||||
"Reason" => "Invalid username and/or password"
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
echo '{"Result":"Fail","Reason":"Uncaught error"}' . PHP_EOL;
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"Result" => "Failure",
|
||||
"Reason" => "Uncaught error"
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
include_once('../include/lucidAuth.template.php');
|
||||
|
||||
echo sprintf($pageLayout['full'], $contentLayout['login']);
|
||||
echo sprintf($pageLayout['full'],
|
||||
sprintf($contentLayout['login'],
|
||||
$_GET['ref']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
51
public/lucidAuth.manage.php
Normal file
51
public/lucidAuth.manage.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
error_reporting(E_ALL ^ E_NOTICE);
|
||||
|
||||
include_once('../include/lucidAuth.functions.php');
|
||||
|
||||
if (!empty($_COOKIE['JWT'])) {
|
||||
$validateTokenResult = validateToken($_COOKIE['JWT']);
|
||||
}
|
||||
|
||||
if ($validateTokenResult['status'] === "Success") {
|
||||
include_once('../include/lucidAuth.template.php');
|
||||
|
||||
try {
|
||||
$allUsers = $pdoDB->query('
|
||||
SELECT User.Id, User.Username, Role.Rolename
|
||||
FROM User
|
||||
LEFT JOIN Role
|
||||
ON (Role.Id = User.RoleId)
|
||||
')->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Exception $e) {
|
||||
// Should really do some actual errorhandling here
|
||||
throw new Exception($e);
|
||||
}
|
||||
foreach($allUsers as $row) {
|
||||
$tableRows[] = sprintf('<tr%1$s><td data-userid="%2$s">%3$s</td><td>%4$s</td><td class="immutable">%5$s</td></tr>',
|
||||
$validateTokenResult['uid'] === $row['Id'] ? ' class="currentuser"': null,
|
||||
$row['Id'],
|
||||
explode('\\', $row['Username'])[1],
|
||||
$row['Rolename'],
|
||||
'<button class="bttn-simple bttn-xs bttn-primary" data-translation="button_sessions">Sessions</button>' . ($validateTokenResult['uid'] === $row['Id'] ? null : ' <button class="bttn-simple bttn-xs bttn-primary delete" data-translation="button_delete">Delete</button>')
|
||||
);
|
||||
}
|
||||
|
||||
echo sprintf($pageLayout['full_alt'],
|
||||
sprintf($contentLayout['manage']['header'],
|
||||
$validateTokenResult['name']
|
||||
),
|
||||
sprintf($contentLayout['manage']['section'],
|
||||
implode($tableRows)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// No cookie containing valid authentication token found;
|
||||
// explicitly deleting any remaining cookie, then redirecting to loginpage
|
||||
setcookie('JWT', FALSE);
|
||||
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
header("Location: lucidAuth.login.php");
|
||||
}
|
||||
|
||||
?>
|
@ -18,7 +18,7 @@
|
||||
|
||||
include_once('../include/lucidAuth.template.php');
|
||||
|
||||
echo sprintf($pageLayout['bare',
|
||||
echo sprintf($pageLayout['bare'],
|
||||
'// iFrames go here'
|
||||
);
|
||||
?>
|
131
public/misc/script.editable.table.js
Normal file
131
public/misc/script.editable.table.js
Normal file
@ -0,0 +1,131 @@
|
||||
/*global $, window*/
|
||||
$.fn.editableTableWidget = function (options) {
|
||||
'use strict';
|
||||
return $(this).each(function () {
|
||||
var buildDefaultOptions = function () {
|
||||
var opts = $.extend({}, $.fn.editableTableWidget.defaultOptions);
|
||||
opts.editor = opts.editor.clone();
|
||||
return opts;
|
||||
},
|
||||
activeOptions = $.extend(buildDefaultOptions(), options),
|
||||
ARROW_LEFT = 37, ARROW_UP = 38, ARROW_RIGHT = 39, ARROW_DOWN = 40, ENTER = 13, ESC = 27, TAB = 9,
|
||||
element = $(this),
|
||||
editor = activeOptions.editor.css('position', 'absolute').hide().appendTo(element.parent()),
|
||||
active,
|
||||
showEditor = function (select) {
|
||||
active = element.find('td:focus:not(".immutable")');
|
||||
if (active.length) {
|
||||
editor.val(active.text())
|
||||
.removeClass('error')
|
||||
.show()
|
||||
.offset(active.offset())
|
||||
.css(active.css(activeOptions.cloneProperties))
|
||||
.width(active.width())
|
||||
.height(active.height())
|
||||
.focus();
|
||||
if (select) {
|
||||
editor.select();
|
||||
}
|
||||
}
|
||||
},
|
||||
setActiveText = function () {
|
||||
var text = editor.val(),
|
||||
evt = $.Event('change'),
|
||||
originalContent;
|
||||
if (active.text() === text || editor.hasClass('error')) {
|
||||
return true;
|
||||
}
|
||||
originalContent = active.html();
|
||||
active.text(text).trigger(evt, text);
|
||||
if (evt.result === false) {
|
||||
active.html(originalContent);
|
||||
}
|
||||
},
|
||||
movement = function (element, keycode) {
|
||||
if (keycode === ARROW_RIGHT) {
|
||||
return element.next('td');
|
||||
} else if (keycode === ARROW_LEFT) {
|
||||
return element.prev('td');
|
||||
} else if (keycode === ARROW_UP) {
|
||||
return element.parent().prev().children().eq(element.index());
|
||||
} else if (keycode === ARROW_DOWN) {
|
||||
return element.parent().next().children().eq(element.index());
|
||||
}
|
||||
return [];
|
||||
};
|
||||
editor.blur(function () {
|
||||
setActiveText();
|
||||
editor.hide();
|
||||
}).keydown(function (e) {
|
||||
if (e.which === ENTER) {
|
||||
setActiveText();
|
||||
editor.hide();
|
||||
active.focus();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else if (e.which === ESC) {
|
||||
editor.val(active.text());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
editor.hide();
|
||||
active.focus();
|
||||
} else if (e.which === TAB) {
|
||||
active.focus();
|
||||
} else if (this.selectionEnd - this.selectionStart === this.value.length) {
|
||||
var possibleMove = movement(active, e.which);
|
||||
if (possibleMove.length > 0) {
|
||||
possibleMove.focus();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('input paste', function () {
|
||||
var evt = $.Event('validate');
|
||||
active.trigger(evt, editor.val());
|
||||
if (evt.result === false) {
|
||||
editor.addClass('error');
|
||||
} else {
|
||||
editor.removeClass('error');
|
||||
}
|
||||
});
|
||||
element.on('click keypress dblclick', showEditor)
|
||||
.css('cursor', 'pointer')
|
||||
.keydown(function (e) {
|
||||
var prevent = true,
|
||||
possibleMove = movement($(e.target), e.which);
|
||||
if (possibleMove.length > 0) {
|
||||
possibleMove.focus();
|
||||
} else if (e.which === ENTER) {
|
||||
showEditor(false);
|
||||
} else if (e.which === 17 || e.which === 91 || e.which === 93) {
|
||||
showEditor(true);
|
||||
prevent = false;
|
||||
} else {
|
||||
prevent = false;
|
||||
}
|
||||
if (prevent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.find('td').prop('tabindex', 1);
|
||||
|
||||
$(window).on('resize', function () {
|
||||
if (editor.is(':visible')) {
|
||||
editor.offset(active.offset())
|
||||
.width(active.width())
|
||||
.height(active.height());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
$.fn.editableTableWidget.defaultOptions = {
|
||||
cloneProperties: ['padding', 'padding-top', 'padding-bottom', 'padding-left', 'padding-right',
|
||||
'text-align', 'font', 'font-size', 'font-family', 'font-weight',
|
||||
'border', 'border-top', 'border-bottom', 'border-left', 'border-right'],
|
||||
editor: $('<input id="editor">')
|
||||
};
|
||||
|
@ -19,7 +19,7 @@ $(document).ready(function(){
|
||||
password: $('#password').val(),
|
||||
ref: $('#ref').val()
|
||||
})
|
||||
.done(function(data,status) {
|
||||
.done(function(data,_status) {
|
||||
if (data.Result === 'Success') {
|
||||
$('#btnlogin').css({
|
||||
'background': 'green url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAaklEQVQ4jeXOMQ5AQBBG4T2BC4i76EWich7ncAKbqCRuodTqnMNTkFgJs3ZU4tXz/Rlj/hUQv8EpMAClFk9sjUAiHVcCnoFMwhZYgPYG575Xe46aIOyMdJx7ji9GwrEzUgOFCu8DkRp/qxU2BKCUyZR6ygAAAABJRU5ErkJggg==) no-repeat center',
|
||||
|
91
public/misc/script.manage.js
Normal file
91
public/misc/script.manage.js
Normal file
@ -0,0 +1,91 @@
|
||||
$(document).ready(function(){
|
||||
// Initialize the editable-table functionality
|
||||
$('#usertable').editableTableWidget();
|
||||
|
||||
$('#usertable button.delete').click(function() {
|
||||
$(this).closest('tr').addClass('removed');
|
||||
});
|
||||
|
||||
$('#btnnewuser').click(function() {
|
||||
// Create a new user; generate pseudo-random username
|
||||
var newUser = 'User' + String(Math.floor(Math.random() * Math.floor(9999))).padStart(4, '0');
|
||||
|
||||
// Add new user to the interface
|
||||
// (new `<tr>` in `<table id="usertable">`)
|
||||
$('#usertable tbody').append($('<tr>', {class: 'new'})
|
||||
.append($('<td>', {
|
||||
text: newUser
|
||||
}))
|
||||
.append($('<td>', {
|
||||
text: 'User'
|
||||
}))
|
||||
.append($('<td>', {
|
||||
class: 'immutable',
|
||||
html: '<button class="bttn-simple bttn-xs bttn-primary disabled" data-translation="button_sessions" disabled="true">' +
|
||||
locales[(localStorage.getItem('language') !== null ? localStorage.getItem('language') : 'nl')]['button_sessions'] + '</button> ' +
|
||||
'<button class="bttn-simple bttn-xs bttn-primary delete" data-translation="button_delete">' +
|
||||
locales[(localStorage.getItem('language') !== null ? localStorage.getItem('language') : 'nl')]['button_delete'] +
|
||||
'</button>'
|
||||
}))
|
||||
);
|
||||
// Call `editableTableWidget()` again to include the newly added `<tr>`
|
||||
// To prevent recreating multiple new editors; reference the already existing `<input>`
|
||||
$('#usertable').editableTableWidget({editor: $('#editor')});
|
||||
// Add eventhandlers to buttons of newly added `<tr>`
|
||||
$('#usertable .new button.delete').unbind().click(function() {
|
||||
$(this).closest('tr').remove();
|
||||
});
|
||||
});
|
||||
|
||||
$('#btnsave').click(function() {
|
||||
var newEntries = [];
|
||||
$('#usertable .new').each(function() {
|
||||
newEntries.push({
|
||||
'userName': $(this).find('td:nth-child(1)').text(),
|
||||
'roleName': $(this).find('td:nth-child(2)').text()
|
||||
});
|
||||
});
|
||||
var removedEntries = [];
|
||||
$('#usertable .removed').each(function() {
|
||||
removedEntries.push({
|
||||
'userId': $(this).find('td:nth-child(1)').data('userid'),
|
||||
'userName': $(this).find('td:nth-child(1)').text(),
|
||||
'roleName': $(this).find('td:nth-child(2)').text()
|
||||
});
|
||||
});
|
||||
|
||||
console.log({'new': newEntries, 'removed': removedEntries});
|
||||
|
||||
/* $.get("psworker.php", {
|
||||
do: "mutate",
|
||||
mutations: {
|
||||
new: newEntries,
|
||||
removed: removedEntries
|
||||
}
|
||||
})*/
|
||||
});
|
||||
|
||||
$('#btncancel').click(function() {
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
if (localStorage.getItem('theme') !== null) {
|
||||
$('#theme').addClass(localStorage.getItem('theme'));
|
||||
}
|
||||
|
||||
$('.logo .sub').click(function(event) {
|
||||
var classes = ["tablecloth", "weave", "madras", "tartan", "seigaiha"];
|
||||
|
||||
if (event.ctrlKey) {
|
||||
var selectedTheme = classes[~~(Math.random()*classes.length)];
|
||||
|
||||
$('#theme').removeClass(classes.join(' ')).addClass(selectedTheme);
|
||||
localStorage.setItem('theme', selectedTheme);
|
||||
}
|
||||
if (event.altKey) {
|
||||
$('#theme').removeClass(classes.join(' '));
|
||||
localStorage.removeItem('theme');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
$(document).ready(function() {
|
||||
if (localStorage.getItem('theme') != '') {
|
||||
$('html').addClass(localStorage.getItem('theme'));
|
||||
}
|
||||
|
||||
$('.logo .sub').on('click', function(event) {
|
||||
if (event.ctrlKey) {
|
||||
var classes = ["tablecloth", "weave", "madras", "tartan", "seigaiha"];
|
||||
var selectedTheme = classes[~~(Math.random()*classes.length)];
|
||||
|
||||
$('html').removeClass().addClass(selectedTheme);
|
||||
localStorage.setItem('theme', selectedTheme);
|
||||
}
|
||||
if (event.altKey) {
|
||||
$('html').removeClass();
|
||||
localStorage.removeItem('theme');
|
||||
}
|
||||
});
|
||||
});
|
@ -1,37 +1,37 @@
|
||||
var locales = {
|
||||
en: {
|
||||
button_add: "add",
|
||||
button_new: "new",
|
||||
button_save: "save",
|
||||
button_cancel: "cancel",
|
||||
button_continue: "continue",
|
||||
button_sessions: "sessions",
|
||||
button_delete: "delete",
|
||||
button_login: "login",
|
||||
heading_error: "ERROR!",
|
||||
label_password: "Password:",
|
||||
label_username: "Username:",
|
||||
label_selectbrowser: "Select browser:",
|
||||
link_install: "Install!",
|
||||
link_logout: "Logout",
|
||||
link_plugin: "Browser plugin",
|
||||
span_credentialsavailable: "Login credentials available upon request!",
|
||||
span_loggedinas: "Logged in as",
|
||||
span_plugin: "Browser plugin?"
|
||||
th_username: "Username",
|
||||
th_role: "Role",
|
||||
th_manage: "Manage"
|
||||
},
|
||||
nl: {
|
||||
button_add: "voeg toe",
|
||||
button_cancel: "annuleer",
|
||||
button_continue: "doorgaan",
|
||||
button_new: "nieuw",
|
||||
button_save: "opslaan",
|
||||
button_cancel: "annuleren",
|
||||
button_sessions: "sessies",
|
||||
button_delete: "verwijder",
|
||||
button_login: "log in",
|
||||
heading_error: "FOUT!",
|
||||
label_password: "Wachtwoord:",
|
||||
label_username: "Gebruikersnaam:",
|
||||
label_selectbrowser: "Selecteer browser:",
|
||||
link_install: "Installeer!",
|
||||
link_logout: "Log uit",
|
||||
link_plugin: "Browser plugin",
|
||||
span_credentialsavailable: "Inloggegevens verkrijgbaar op aanvraag!",
|
||||
span_loggedinas: "Ingelogd als",
|
||||
span_plugin: "Browser plugin?"
|
||||
th_username: "Gebruikersnaam",
|
||||
th_role: "Rol",
|
||||
th_manage: "Beheer"
|
||||
} // ... etc.
|
||||
};
|
||||
|
||||
@ -39,7 +39,7 @@ $(document).ready(function(){
|
||||
$('[id^=linklanguage-]').click(function() {
|
||||
var selectedlang = $(this).attr('id').split('-')[1];
|
||||
// Replace text of each element with translated value
|
||||
$('[data-translation]').each(function(index) {
|
||||
$('[data-translation]').each(function() {
|
||||
$(this).text(locales[selectedlang][$(this).data('translation')]);
|
||||
});
|
||||
// Enable/disable (toggle) anchors
|
||||
@ -51,7 +51,7 @@ $(document).ready(function(){
|
||||
});
|
||||
|
||||
if (localStorage.getItem('language') !== null) {
|
||||
$('[data-translation]').each(function(index) {
|
||||
$('[data-translation]').each(function() {
|
||||
$(this).text(locales[localStorage.getItem('language')][$(this).data('translation')]);
|
||||
});
|
||||
$('span#user a.current').removeClass('current');
|
||||
|
@ -96,64 +96,60 @@ body {
|
||||
.main {
|
||||
clear: both;
|
||||
}
|
||||
.main fieldset {
|
||||
border: 0;
|
||||
}
|
||||
.main fieldset legend {
|
||||
visibility: hidden;
|
||||
}
|
||||
.main fieldset label.pre {
|
||||
.main section {
|
||||
clear: both;
|
||||
}
|
||||
.main section label.pre {
|
||||
display: inline-block;
|
||||
width: 155px;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.main fieldset label#labelallaliases {
|
||||
float: left;
|
||||
}
|
||||
.main fieldset output#aliasstats {
|
||||
float: left;
|
||||
clear: left;
|
||||
width: 160px;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
.main fieldset output#aliasstats:after {
|
||||
margin-left: 7px;
|
||||
content: ' ';
|
||||
}
|
||||
.main fieldset input {
|
||||
.main section input {
|
||||
border: 1px solid #003399;
|
||||
padding: 2px;
|
||||
width: 100px;
|
||||
}
|
||||
.main fieldset input[type=radio] {
|
||||
.main section input[type=radio] {
|
||||
border: none;
|
||||
margin-top: 7px;
|
||||
width: 20px;
|
||||
}
|
||||
.main fieldset button {
|
||||
.main header button,
|
||||
.main section button {
|
||||
margin-left: 160px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.main fieldset select {
|
||||
border: 1px solid #003399;
|
||||
padding: 2px;
|
||||
width: 375px;
|
||||
}
|
||||
.main fieldset select .new {
|
||||
font-weight: bold;
|
||||
}
|
||||
.main fieldset select .deleted {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.main fieldset#signup label.pre {
|
||||
width: 205px;
|
||||
}
|
||||
.main fieldset#signup span.indent, .main fieldset#signup input.button {
|
||||
margin-left: 210px;
|
||||
}
|
||||
.main header .buttons,
|
||||
.main section .buttons {
|
||||
text-align: center;
|
||||
}
|
||||
.main header .buttons button,
|
||||
.main section .buttons button {
|
||||
margin-left: inherit;
|
||||
}
|
||||
.main section table {
|
||||
width: 100%;
|
||||
}
|
||||
.main section table * {
|
||||
font-size: 14px;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
.main section table .new {
|
||||
font-weight: bold;
|
||||
}
|
||||
.main section table .removed td:nth-child(-n+2) {
|
||||
text-decoration: line-through;
|
||||
color: grey;
|
||||
}
|
||||
.main section table tbody tr:nth-child(odd) {
|
||||
background-color: rgb(215, 215, 215);
|
||||
}
|
||||
.main section table .immutable {
|
||||
cursor: default !important;
|
||||
}
|
||||
.main li {
|
||||
list-style: none;
|
||||
padding: 5px;
|
||||
@ -214,25 +210,3 @@ body {
|
||||
.main span#user nav {
|
||||
display: inline;
|
||||
}
|
||||
.main span#user #pluginlogos {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
right: 10px;
|
||||
height: 112px;
|
||||
width: 250px;
|
||||
border: 1px solid rgb(0, 51, 153);
|
||||
box-shadow: black 0px 0px 20px;
|
||||
box-sizing: border-box;
|
||||
padding-top: 5px;
|
||||
background: white;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
.main span#user #pluginlogos img {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
filter: saturate(500%) contrast(200%) grayscale(100%) opacity(50%);
|
||||
transition: all 375ms;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
50
public/misc/style.panes.css
Normal file
50
public/misc/style.panes.css
Normal file
@ -0,0 +1,50 @@
|
||||
body{
|
||||
margin: 0;
|
||||
}
|
||||
.wrapper{
|
||||
min-height: 100vh;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header {
|
||||
height: 100px;
|
||||
background: #FFFFFF;
|
||||
color: #000000;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
background: #333333;
|
||||
color: #000;
|
||||
min-height: 0px;
|
||||
max-height: 100vh;
|
||||
}
|
||||
.columns{
|
||||
display: flex;
|
||||
flex:1;
|
||||
}
|
||||
.main{
|
||||
flex: 1;
|
||||
order: 2;
|
||||
background: #fff;
|
||||
border: 3px solid #CCCCCC;
|
||||
border-radius: 5px;
|
||||
height: 400px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.main section {
|
||||
overflow-y: scroll;
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
.sidebar-first{
|
||||
width: 25%;
|
||||
background: transparent;
|
||||
order: 1;
|
||||
}
|
||||
.sidebar-second{
|
||||
width: 25%;
|
||||
order: 3;
|
||||
background: transparent;
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
html.tablecloth {
|
||||
#theme.tablecloth {
|
||||
height: 100%;
|
||||
background: repeating-linear-gradient(-45deg, transparent, transparent 1em, rgba(136, 136, 136, 0.4) 0, rgba(136, 136, 136, 0.1) 2em, transparent 0, transparent 1em, rgba(136, 136, 136, 0.3) 0, rgba(136, 136, 136, 0.2) 4em, transparent 0, transparent 1em, rgba(68, 68, 68, 0.6) 0, rgba(68, 68, 68, 0.2) 2em),
|
||||
repeating-linear-gradient(45deg, transparent, transparent 1em, rgba(136, 136, 136, 0.4) 0, rgba(136, 136, 136, 0.1) 2em, transparent 0, transparent 1em, rgba(136, 136, 136, 0.3) 0, rgba(136, 136, 136, 0.2) 4em, transparent 0, transparent 1em, rgba(68, 68, 68, 0.4) 0, rgba(68, 68, 68, 0.1) 2em), #666;
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
|
||||
html.weave {
|
||||
#theme.weave {
|
||||
background: linear-gradient(45deg, #666 12%, transparent 0, transparent 88%, #666 0),
|
||||
linear-gradient(135deg, transparent 37%, #888 0, #888 63%, transparent 0),
|
||||
linear-gradient(45deg, transparent 37%, #666 0, #666 63%, transparent 0),
|
||||
@ -13,14 +13,14 @@ html.weave {
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
|
||||
html.madras {
|
||||
#theme.madras {
|
||||
height: 100%;
|
||||
background-color: #e9d4b9;
|
||||
background-image: repeating-linear-gradient(45deg, transparent 5px, rgba(11, 36, 45, 0.5) 5px, rgba(11, 36, 45, 0.5) 10px, rgba(211, 119, 111, 0) 10px, rgba(211, 119, 111, 0) 35px, rgba(211, 119, 111, 0.5) 35px, rgba(211, 119, 111, 0.5) 40px, rgba(11, 36, 45, 0.5) 40px, rgba(11, 36, 45, 0.5) 50px, rgba(11, 36, 45, 0) 50px, rgba(11, 36, 45, 0) 60px, rgba(211, 119, 111, 0.5) 60px, rgba(211, 119, 111, 0.5) 70px, rgba(247, 179, 85, 0.5) 70px, rgba(247, 179, 85, 0.5) 80px, rgba(247, 179, 85, 0) 80px, rgba(247, 179, 85, 0) 90px, rgba(211, 119, 111, 0.5) 90px, rgba(211, 119, 111, 0.5) 110px, rgba(211, 119, 111, 0) 110px, rgba(211, 119, 111, 0) 120px, rgba(11, 36, 45, 0.5) 120px, rgba(11, 36, 45, 0.5) 140px),
|
||||
repeating-linear-gradient(135deg, transparent 5px, rgba(11, 36, 45, 0.5) 5px, rgba(11, 36, 45, 0.5) 10px, rgba(211, 119, 111, 0) 10px, rgba(211, 119, 111, 0) 35px, rgba(211, 119, 111, 0.5) 35px, rgba(211, 119, 111, 0.5) 40px, rgba(11, 36, 45, 0.5) 40px, rgba(11, 36, 45, 0.5) 50px, rgba(11, 36, 45, 0) 50px, rgba(11, 36, 45, 0) 60px, rgba(211, 119, 111, 0.5) 60px, rgba(211, 119, 111, 0.5) 70px, rgba(247, 179, 85, 0.5) 70px, rgba(247, 179, 85, 0.5) 80px, rgba(247, 179, 85, 0) 80px, rgba(247, 179, 85, 0) 90px, rgba(211, 119, 111, 0.5) 90px, rgba(211, 119, 111, 0.5) 110px, rgba(211, 119, 111, 0) 110px, rgba(211, 119, 111, 0) 140px, rgba(11, 36, 45, 0.5) 140px, rgba(11, 36, 45, 0.5) 160px);
|
||||
}
|
||||
|
||||
html.tartan {
|
||||
#theme.tartan {
|
||||
height: 100%;
|
||||
background-color: #a0302c;
|
||||
background-image: repeating-linear-gradient(20deg, transparent, transparent 50px, rgba(0, 0, 0, 0.4) 50px, rgba(0, 0, 0, 0.4) 53px, transparent 53px, transparent 63px, rgba(0, 0, 0, 0.4) 63px, rgba(0, 0, 0, 0.4) 66px, transparent 66px, transparent 116px, rgba(0, 0, 0, 0.5) 116px, rgba(0, 0, 0, 0.5) 166px, rgba(255, 255, 255, 0.2) 166px, rgba(255, 255, 255, 0.2) 169px, rgba(0, 0, 0, 0.5) 169px, rgba(0, 0, 0, 0.5) 179px, rgba(255, 255, 255, 0.2) 179px, rgba(255, 255, 255, 0.2) 182px, rgba(0, 0, 0, 0.5) 182px, rgba(0, 0, 0, 0.5) 232px, transparent 232px),
|
||||
@ -28,7 +28,7 @@ html.tartan {
|
||||
repeating-linear-gradient(145deg, transparent, transparent 2px, rgba(0, 0, 0, 0.2) 2px, rgba(0, 0, 0, 0.2) 3px, transparent 3px, transparent 5px, rgba(0, 0, 0, 0.2) 5px);
|
||||
}
|
||||
|
||||
html.seigaiha {
|
||||
#theme.seigaiha {
|
||||
background-color: grey;
|
||||
background-image: radial-gradient(circle at 100% 150%, grey 24%, silver 25%, silver 28%, grey 29%, grey 36%, silver 36%, silver 40%, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0)),
|
||||
radial-gradient(circle at 0 150%, grey 24%, silver 25%, silver 28%, grey 29%, grey 36%, silver 36%, silver 40%, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0)),
|
||||
|
Loading…
Reference in New Issue
Block a user