Create template based on SSO code

master
Skylar Ittner před 7 roky
revize 16cbf2a5f1

4
.gitignore vendorováno

@ -0,0 +1,4 @@
vendor
settings.php
nbproject/private
database_model.mwb.bak

@ -0,0 +1,62 @@
<?php
/**
* Make things happen when buttons are pressed and forms submitted.
*/
use LdapTools\LdapManager;
use LdapTools\Object\LdapObjectType;
require_once __DIR__ . "/required.php";
dieifnotloggedin();
require_once __DIR__ . "/lib/login.php";
require_once __DIR__ . "/lib/worst_passwords.php";
function returnToSender($msg, $arg = "") {
global $VARS;
if ($arg == "") {
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg);
} else {
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=$arg");
}
die();
}
switch ($VARS['action']) {
case "signout":
session_destroy();
header('Location: index.php');
die("Logged out.");
case "chpasswd":
if ($_SESSION['password'] == $VARS['oldpass']) {
if ($VARS['newpass'] == $VARS['conpass']) {
$passrank = checkWorst500List($VARS['newpass']);
if ($passrank !== FALSE) {
returnToSender("password_500", $passrank);
}
if (strlen($VARS['newpass']) < MIN_PASSWORD_LENGTH) {
returnToSender("weak_password");
}
$database->update('accounts', ['password' => encryptPassword($VARS['newpass'])], ['uid' => $_SESSION['uid']]);
$_SESSION['password'] = $VARS['newpass'];
returnToSender("password_updated");
} else {
returnToSender("new_password_mismatch");
}
} else {
returnToSender("old_password_mismatch");
}
break;
case "add2fa":
if (is_empty($VARS['secret'])) {
returnToSender("invalid_parameters");
}
$database->update('accounts', ['authsecret' => $VARS['secret']], ['uid' => $_SESSION['uid']]);
returnToSender("2fa_enabled");
case "rm2fa":
$database->update('accounts', ['authsecret' => ""], ['uid' => $_SESSION['uid']]);
returnToSender("2fa_removed");
break;
}

@ -0,0 +1,128 @@
<?php
require_once __DIR__ . "/required.php";
if ($_SESSION['loggedin'] != true) {
header('Location: index.php');
die("Session expired. Log in again to continue.");
}
require_once __DIR__ . "/pages.php";
$pageid = "home";
if (!is_empty($_GET['page'])) {
$pg = strtolower($_GET['page']);
$pg = preg_replace('/[^0-9a-z_]/', "", $pg);
if (array_key_exists($pg, PAGES) && file_exists(__DIR__ . "/pages/" . $pg . ".php")) {
$pageid = $pg;
} else {
$pageid = "404";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" contgreent="width=device-width, initial-scale=1">
<title><?php echo SITE_TITLE; ?></title>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/font-awesome.min.css" rel="stylesheet">
<link href="static/css/app.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-sm-offset-3 col-md-offset-4 col-lg-offset-4">
<img class="img-responsive banner-image" src="static/img/banner.png" />
</div>
</div>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="app.php?page=home">
<?php
// add breadcrumb-y thing
//lang("home");
//echo " <i class=\"fa fa-caret-right\"></i> ";
lang(PAGES[$pageid]['title']);
?>
</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-gears fa-fw"></i> <?php lang("options") ?> <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="app.php?page=security"><i class="fa fa-lock fa-fw"></i> <?php lang("account security") ?></a></li>
<li class="divider"></li>
<li><a href="action.php?action=signout"><i class="fa fa-sign-out fa-fw"></i> <?php lang("sign out") ?></a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<?php
// Alert messages
if (!is_empty($_GET['msg']) && array_key_exists($_GET['msg'], MESSAGES)) {
// optional string generation argument
if (is_empty($_GET['arg'])) {
$alertmsg = lang(MESSAGES[$_GET['msg']]['string'], false);
} else {
$alertmsg = lang2(MESSAGES[$_GET['msg']]['string'], ["arg" => $_GET['arg']], false);
}
$alerttype = MESSAGES[$_GET['msg']]['type'];
$alerticon = "square-o";
switch (MESSAGES[$_GET['msg']]['type']) {
case "danger":
$alerticon = "times";
break;
case "warning":
$alerticon = "exclamation-triangle";
break;
case "info":
$alerticon = "info-circle";
break;
case "success":
$alerticon = "check";
break;
}
echo <<<END
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-sm-offset-3 col-md-offset-4 col-lg-offset-4">
<div class="alert alert-dismissible alert-$alerttype">
<button type="button" class="close">&times;</button>
<i class="fa fa-$alerticon"></i> $alertmsg
</div>
</div>
</div>
END;
}
?>
<div>
<?php
include_once __DIR__ . '/pages/' . $pageid . ".php";
?>
</div>
<div class="footer">
<?php echo LICENSE_TEXT; ?><br />
Copyright &copy; <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
</div>
</div>
<script src="static/js/jquery-3.2.1.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<script src="static/js/app.js"></script>
</body>
</html>

@ -0,0 +1,16 @@
{
"name": "netsyms/web-app-template",
"description": "Simple framework for rapid webapp development",
"type": "project",
"require": {
"catfan/medoo": "^1.2",
"spomky-labs/otphp": "^8.3"
},
"license": "MIT",
"authors": [
{
"name": "Skylar Ittner",
"email": "admin@netsyms.com"
}
]
}

461
composer.lock vygenerováno

@ -0,0 +1,461 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "51c1672b4dea32e60865b4c0cd9ff8e1",
"packages": [
{
"name": "beberlei/assert",
"version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/beberlei/assert.git",
"reference": "3ee3bc468a3ce4bbfc3d74f53c6cdb5242d39d1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/beberlei/assert/zipball/3ee3bc468a3ce4bbfc3d74f53c6cdb5242d39d1a",
"reference": "3ee3bc468a3ce4bbfc3d74f53c6cdb5242d39d1a",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.1.1",
"phpunit/phpunit": "^4|^5"
},
"type": "library",
"autoload": {
"psr-4": {
"Assert\\": "lib/Assert"
},
"files": [
"lib/Assert/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de",
"role": "Lead Developer"
},
{
"name": "Richard Quadling",
"email": "rquadling@gmail.com",
"role": "Collaborator"
}
],
"description": "Thin assertion library for input validation in business models.",
"keywords": [
"assert",
"assertion",
"validation"
],
"time": "2017-03-14T18:06:52+00:00"
},
{
"name": "catfan/medoo",
"version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/catfan/Medoo.git",
"reference": "b5a788c90c44db0f978512c890cb6962af4685e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/catfan/Medoo/zipball/b5a788c90c44db0f978512c890cb6962af4685e8",
"reference": "b5a788c90c44db0f978512c890cb6962af4685e8",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": ">=5.4"
},
"suggest": {
"ext-pdo_dblib": "For MSSQL or Sybase databases on Linux/UNIX platform",
"ext-pdo_mysql": "For MySQL or MariaDB databases",
"ext-pdo_oci": "For Oracle databases",
"ext-pdo_pqsql": "For PostgreSQL databases",
"ext-pdo_sqlite": "For SQLite databases",
"ext-pdo_sqlsrv": "For MSSQL databases on Windows platform"
},
"type": "framework",
"autoload": {
"psr-4": {
"Medoo\\": "/src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Angel Lai",
"email": "angel@catfan.me"
}
],
"description": "The Lightest PHP database framework to accelerate development",
"homepage": "http://medoo.in",
"keywords": [
"database",
"lightweight",
"mssql",
"mysql",
"php framework",
"sql",
"sqlite"
],
"time": "2017-02-17T16:05:35+00:00"
},
{
"name": "christian-riesen/base32",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/ChristianRiesen/base32.git",
"reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa",
"reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.*",
"satooshi/php-coveralls": "0.*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Base32\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Riesen",
"email": "chris.riesen@gmail.com",
"homepage": "http://christianriesen.com",
"role": "Developer"
}
],
"description": "Base32 encoder/decoder according to RFC 4648",
"homepage": "https://github.com/ChristianRiesen/base32",
"keywords": [
"base32",
"decode",
"encode",
"rfc4648"
],
"time": "2016-05-05T11:49:03+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v2.0.10",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"pseudorandom",
"random"
],
"time": "2017-03-13T16:27:32+00:00"
},
{
"name": "spomky-labs/otphp",
"version": "v8.3.0",
"source": {
"type": "git",
"url": "https://github.com/Spomky-Labs/otphp.git",
"reference": "8c90e16ba48fe7c306832611e22c5bad2d663a98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/8c90e16ba48fe7c306832611e22c5bad2d663a98",
"reference": "8c90e16ba48fe7c306832611e22c5bad2d663a98",
"shasum": ""
},
"require": {
"beberlei/assert": "^2.4",
"christian-riesen/base32": "^1.1",
"paragonie/random_compat": "^2.0",
"php": "^5.5|^7.0",
"symfony/polyfill-mbstring": "^1.1",
"symfony/polyfill-php56": "^1.1"
},
"require-dev": {
"phpunit/phpunit": "~4.0|^5.0",
"satooshi/php-coveralls": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "8.2.x-dev"
}
},
"autoload": {
"psr-4": {
"OTPHP\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky"
},
{
"name": "All contributors",
"homepage": "https://github.com/Spomky-Labs/otphp/contributors"
}
],
"description": "A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator",
"homepage": "https://github.com/Spomky-Labs/otphp",
"keywords": [
"FreeOTP",
"RFC 4226",
"RFC 6238",
"google authenticator",
"hotp",
"otp",
"totp"
],
"time": "2016-12-08T10:46:02+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "e79d363049d1c2128f133a2667e4f4190904f7f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4",
"reference": "e79d363049d1c2128f133a2667e4f4190904f7f4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
},
{
"name": "symfony/polyfill-php56",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php56.git",
"reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/1dd42b9b89556f18092f3d1ada22cb05ac85383c",
"reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/polyfill-util": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php56\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
},
{
"name": "symfony/polyfill-util",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-util.git",
"reference": "746bce0fca664ac0a575e465f65c6643faddf7fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-util/zipball/746bce0fca664ac0a575e465f65c6643faddf7fb",
"reference": "746bce0fca664ac0a575e465f65c6643faddf7fb",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Util\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony utilities for portability of PHP codes",
"homepage": "https://symfony.com",
"keywords": [
"compat",
"compatibility",
"polyfill",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

Binární soubor nebyl zobrazen.

@ -0,0 +1,117 @@
<?php
require_once __DIR__ . "/required.php";
require_once __DIR__ . "/lib/login.php";
/* Authenticate user */
$userpass_ok = false;
$multiauth = false;
if ($VARS['progress'] == "1") {
if (authenticate_user($VARS['username'], $VARS['password'])) {
switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED":
$alert = lang("account locked", false);
break;
case "TERMINATED":
$alert = lang("account terminated", false);
break;
case "CHANGE_PASSWORD":
$alert = lang("password expired", false);
case "NORMAL":
$userpass_ok = true;
break;
case "ALERT_ON_ACCESS":
sendLoginAlertEmail($VARS['username']);
$userpass_ok = true;
break;
}
if ($userpass_ok) {
if (userHasTOTP($VARS['username'])) {
$multiauth = true;
} else {
doLoginUser($VARS['username'], $VARS['password']);
header('Location: app.php');
die("Logged in, go to app.php");
}
}
} else {
$alert = lang("login incorrect", false);
}
} else if ($VARS['progress'] == "2") {
if (verifyTOTP($VARS['username'], $VARS['authcode'])) {
doLoginUser($VARS['username'], $VARS['password']);
header('Location: app.php');
die("Logged in, go to app.php");
} else {
$alert = lang("2fa incorrect", false);
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" contgreent="width=device-width, initial-scale=1">
<title><?php echo SITE_TITLE; ?></title>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/app.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-sm-offset-3 col-md-offset-4 col-lg-offset-4">
<div>
<img class="img-responsive banner-image" src="static/img/banner.png" />
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php lang("sign in"); ?></h3>
</div>
<div class="panel-body">
<form action="" method="POST">
<?php
if (!is_empty($alert)) {
?>
<div class="alert alert-danger">
<?php echo $alert; ?>
</div>
<?php
}
if ($multiauth != true) {
?>
<input type="text" class="form-control" name="username" placeholder="<?php lang("username"); ?>" required="required" autofocus /><br />
<input type="password" class="form-control" name="password" placeholder="<?php lang("password"); ?>" required="required" /><br />
<input type="hidden" name="progress" value="1" />
<?php
} else if ($multiauth) {
?>
<div class="alert alert-info">
<?php lang("2fa prompt"); ?>
</div>
<input type="text" class="form-control" name="authcode" placeholder="<?php lang("authcode"); ?>" required="required" autofocus /><br />
<input type="hidden" name="progress" value="2" />
<input type="hidden" name="username" value="<?php echo $VARS['username']; ?>" />
<?php
}
?>
<button type="submit" class="btn btn-primary">
<?php lang("continue"); ?>
</button>
</form>
</div>
</div>
</div>
</div>
<div class="footer">
<?php echo LICENSE_TEXT; ?><br />
Copyright &copy; <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
</div>
</div>
<script src="static/js/jquery-3.2.1.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
</body>
</html>

@ -0,0 +1,42 @@
<?php
define("STRINGS", [
"sign in" => "Sign In",
"username" => "Username",
"password" => "Password",
"continue" => "Continue",
"authcode" => "Authentication code",
"2fa prompt" => "Enter the six-digit code from your mobile authenticator app.",
"2fa incorrect" => "Authentication code incorrect.",
"login incorrect" => "Login incorrect.",
"account locked" => "This account has been disabled. Contact technical support.",
"password expired" => "You must change your password before continuing.",
"account terminated" => "Account terminated. Access denied.",
"account state error" => "Your account state is not stable. Log out, restart your browser, and try again.",
"password on 500 list" => "The given password is ranked number {arg} out of the 500 most common passwords. Try a different one.",
"welcome user" => "Welcome, {user}!",
"change password" => "Change password",
"security options" => "Security options",
"account security" => "Account security",
"sign out" => "Sign out",
"settings" => "Settings",
"options" => "Options",
"404 error" => "404 Error",
"page not found" => "Page not found.",
"current password incorrect" => "The current password is incorrect. Try again.",
"new password mismatch" => "The new passwords did not match. Try again.",
"weak password" => "Password does not meet requirements.",
"password updated" => "Password updated successfully.",
"setup 2fa" => "Setup 2-factor authentication",
"2fa removed" => "2-factor authentication disabled.",
"2fa enabled" => "2-factor authentication activated.",
"remove 2fa" => "Disable 2-factor authentication",
"2fa explained" => "2-factor authentication adds more security to your account. You'll need an app such as Google Authenticator on your smartphone. When you have the app installed, you can enable 2-factor authentication by clicking the button below and scanning a QR code with the app. Whenever you sign in in the future, you'll need to input a six-digit code from your phone into the login page when prompted. You can disable 2-factor authentication from this page if you change your mind.",
"2fa active" => "2-factor authentication is active on your account. To remove 2fa, reset your authentication secret, or change to a new security device, click the button below.",
"enable 2fa" => "Enable 2-factor authentication",
"scan 2fa qrcode" => "Scan the QR Code with the authenticator app, or enter the secret key manually.",
"confirm 2fa" => "Finish setup",
"invalid parameters" => "Invalid request parameters.",
"ldap server error" => "The LDAP server returned an error: {arg}",
"home" => "Home",
]);

@ -0,0 +1,44 @@
<?php
define("MESSAGES", [
"old_password_mismatch" => [
"string" => "current password incorrect",
"type" => "danger"
],
"new_password_mismatch" => [
"string" => "new password mismatch",
"type" => "danger"
],
"weak_password" => [
"string" => "weak password",
"type" => "danger"
],
"password_updated" => [
"string" => "password updated",
"type" => "success"
],
"2fa_removed" => [
"string" => "2fa removed",
"type" => "success"
],
"2fa_enabled" => [
"string" => "2fa enabled",
"type" => "success"
],
"invalid_parameters" => [
"string" => "invalid parameters",
"type" => "danger"
],
"password_500" => [
"string" => "password on 500 list",
"type" => "danger"
],
"account_state_error" => [
"string" => "account state error",
"type" => "danger"
],
"404_error" => [
"string" => "page not found",
"type" => "info"
]
]);

@ -0,0 +1,302 @@
<?php
/**
* Authentication and account functions
*/
use Base32\Base32;
use OTPHP\TOTP;
use LdapTools\LdapManager;
use LdapTools\Connection\ADResponseCodes;
////////////////////////////////////////////////////////////////////////////////
// Account handling //
////////////////////////////////////////////////////////////////////////////////
/**
* Add a user to the system. /!\ Assumes input is OK /!\
* @param string $username Username, saved in lowercase.
* @param string $password Password, will be hashed before saving.
* @param string $realname User's real legal name
* @param string $email User's email address.
* @return int The new user's ID number in the database.
*/
function adduser($username, $password, $realname, $email = null, $phone1 = "", $phone2 = "") {
global $database;
$database->debug()->insert('accounts', [
'username' => strtolower($username),
'password' => (is_null($password) ? null : encryptPassword($password)),
'realname' => $realname,
'email' => $email,
'phone1' => $phone1,
'phone2' => $phone2,
'acctstatus' => 1
]);
return $database->id();
}
/**
* Get where a user's account actually is.
* @param string $username
* @return string "LDAP", "LOCAL", "LDAP_ONLY", or "NONE".
*/
function account_location($username, $password) {
global $database;
$user_exists = user_exists($username);
if (!$user_exists && !LDAP_ENABLED) {
return false;
}
if ($user_exists) {
$userinfo = $database->select('accounts', ['password'], ['username' => $username])[0];
// if password empty, it's an LDAP user
if (is_empty($userinfo['password']) && LDAP_ENABLED) {
return "LDAP";
} else if (is_empty($userinfo['password']) && !LDAP_ENABLED) {
return "NONE";
} else {
return "LOCAL";
}
} else {
if (user_exists_ldap($username, $password)) {
return "LDAP_ONLY";
} else {
return "NONE";
}
}
}
/**
* Checks the given credentials against the database.
* @param string $username
* @param string $password
* @return boolean True if OK, else false
*/
function authenticate_user($username, $password) {
global $database;
global $ldap_config;
if (is_empty($username) || is_empty($password)) {
return false;
}
$loc = account_location($username, $password);
if ($loc == "NONE") {
return false;
} else if ($loc == "LOCAL") {
$hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password'];
return (comparePassword($password, $hash));
} else if ($loc == "LDAP") {
return authenticate_user_ldap($username, $password);
} else if ($loc == "LDAP_ONLY") {
if (authenticate_user_ldap($username, $password) === TRUE) {
try {
$user = (new LdapManager($ldap_config))->getRepository('user')->findOneByUsername($username);
var_dump($user);
adduser($user->getUsername(), null, $user->getName(), ($user->hasEmailAddress() ? $user->getEmailAddress() : null));
return true;
} catch (Exception $e) {
sendError("LDAP error: " . $e->getMessage());
}
} else {
return false;
}
} else {
return false;
}
}
/**
* Check if a username exists in the local database.
* @param String $username
*/
function user_exists($username) {
global $database;
return $database->has('accounts', ['username' => $username, "LIMIT" => QUERY_LIMIT]);
}
/**
* Get the account status: NORMAL, TERMINATED, LOCKED_OR_DISABLED,
* CHANGE_PASSWORD, or ALERT_ON_ACCESS
* @global $database $database
* @param string $username
* @return string
*/
function get_account_status($username) {
global $database;
$loc = account_location($username);
if ($loc == "LOCAL") {
$statuscode = $database->select('accounts', [
'[>]acctstatus' => [
'acctstatus' => 'statusid'
]
], [
'accounts.acctstatus',
'acctstatus.statuscode'
], [
'username' => $username,
"LIMIT" => 1
]
)[0]['statuscode'];
return $statuscode;
} else if ($loc == "LDAP") {
// TODO: Read actual account status from AD servers
return "NORMAL";
} else {
// account isn't setup properly
return "LOCKED_OR_DISABLED";
}
}
////////////////////////////////////////////////////////////////////////////////
// Login handling //
////////////////////////////////////////////////////////////////////////////////
/**
* Setup $_SESSION values to log in a user
* @param string $username
*/
function doLoginUser($username, $password) {
global $database;
$userinfo = $database->select('accounts', ['email', 'uid', 'realname'], ['username' => $username])[0];
$_SESSION['username'] = $username;
$_SESSION['uid'] = $userinfo['uid'];
$_SESSION['email'] = $userinfo['email'];
$_SESSION['realname'] = $userinfo['realname'];
$_SESSION['password'] = $password; // needed for things like EWS
$_SESSION['loggedin'] = true;
}
/**
* Send an alert email to the system admin
*
* Used when an account with the status ALERT_ON_ACCESS logs in
* @param String $username the account username
*/
function sendLoginAlertEmail($username) {
// TODO: add email code
}
////////////////////////////////////////////////////////////////////////////////
// LDAP handling //
////////////////////////////////////////////////////////////////////////////////
/**
* Checks the given credentials against the LDAP server.
* @param string $username
* @param string $password
* @return mixed True if OK, else false or the error code from the server
*/
function authenticate_user_ldap($username, $password) {
global $ldap_config;
if (is_empty($username) || is_empty($password)) {
return false;
}
$ldapManager = new LdapManager($ldap_config);
$msg = "";
$code = 0;
if ($ldapManager->authenticate($username, $password, $msg, $code)) {
return true;
} else {
return $code;
}
}
/**
* Check if a username exists on the LDAP server.
* @global type $ldap_config
* @param type $username
* @return boolean true if yes, else false
*/
function user_exists_ldap($username, $password) {
global $ldap_config;
$ldap = new LdapManager($ldap_config);
if (!$ldap->authenticate($username, $password, $message, $code)) {
switch ($code) {
case ADResponseCodes::ACCOUNT_INVALID:
return false;
case ADResponseCodes::ACCOUNT_CREDENTIALS_INVALID:
return true;
case ADResponseCodes::ACCOUNT_RESTRICTIONS:
return true;
case ADResponseCodes::ACCOUNT_RESTRICTIONS_TIME:
return true;
case ADResponseCodes::ACCOUNT_RESTRICTIONS_DEVICE:
return true;
case ADResponseCodes::ACCOUNT_PASSWORD_EXPIRED:
return true;
case ADResponseCodes::ACCOUNT_DISABLED:
return true;
case ADResponseCodes::ACCOUNT_CONTEXT_IDS:
return true;
case ADResponseCodes::ACCOUNT_EXPIRED:
return false;
case ADResponseCodes::ACCOUNT_PASSWORD_MUST_CHANGE:
return true;
case ADResponseCodes::ACCOUNT_LOCKED:
return true;
default:
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// 2-factor authentication //
////////////////////////////////////////////////////////////////////////////////
/**
* Check if a user has TOTP setup
* @global $database $database
* @param string $username
* @return boolean true if TOTP secret exists, else false
*/
function userHasTOTP($username) {
global $database;
$secret = $database->select('accounts', 'authsecret', ['username' => $username])[0];
if (is_empty($secret)) {
return false;
}
return true;
}
/**
* Generate a TOTP secret for the given user.
* @param string $username
* @return string OTP provisioning URI (for generating a QR code)
*/
function newTOTP($username) {
global $database;
$secret = random_bytes(20);
$encoded_secret = Base32::encode($secret);
$userdata = $database->select('accounts', ['email', 'authsecret', 'realname'], ['username' => $username])[0];
$totp = new TOTP((is_null($userdata['email']) ? $userdata['realname'] : $userdata['email']), $encoded_secret);
$totp->setIssuer(SYSTEM_NAME);
return $totp->getProvisioningUri();
}
/**
* Save a TOTP secret for the user.
* @global $database $database
* @param string $username
* @param string $secret
*/
function saveTOTP($username, $secret) {
global $database;
$database->update('accounts', ['authsecret' => $secret], ['username' => $username]);
}
/**
* Verify a TOTP multiauth code
* @global $database
* @param string $username
* @param int $code
* @return boolean true if it's legit, else false
*/
function verifyTOTP($username, $code) {
global $database;
$userdata = $database->select('accounts', ['email', 'authsecret'], ['username' => $username])[0];
if (is_empty($userdata['authsecret'])) {
return false;
}
$totp = new TOTP(null, $userdata['authsecret']);
return $totp->verify($code);
}

@ -0,0 +1,522 @@
<?php
/*
* 500 most common passwords, to be used in stopping idiots from having really bad passwords.
* Source: https://github.com/danielmiessler/SecLists/blob/master/Passwords/500-worst-passwords.txt
*/
/**
* Checks a given password against the list of the 500 most common passwords.
* @param string $search the password to check
* @return false if not found, the password ranking if found
*/
function checkWorst500List($search) {
$worst_password_list = [
"123456",
"password",
"12345678",
"1234",
"pussy",
"12345",
"dragon",
"qwerty",
"696969",
"mustang",
"letmein",
"baseball",
"master",
"michael",
"football",
"shadow",
"monkey",
"abc123",
"pass",
"fuckme",
"6969",
"jordan",
"harley",
"ranger",
"iwantu",
"jennifer",
"hunter",
"fuck",
"2000",
"test",
"batman",
"trustno1",
"thomas",
"tigger",
"robert",
"access",
"love",
"buster",
"1234567",
"soccer",
"hockey",
"killer",
"george",
"sexy",
"andrew",
"charlie",
"superman",
"asshole",
"fuckyou",
"dallas",
"jessica",
"panties",
"pepper",
"1111",
"austin",
"william",
"daniel",
"golfer",
"summer",
"heather",
"hammer",
"yankees",
"joshua",
"maggie",
"biteme",
"enter",
"ashley",
"thunder",
"cowboy",
"silver",
"richard",
"fucker",
"orange",
"merlin",
"michelle",
"corvette",
"bigdog",
"cheese",
"matthew",
"121212",
"patrick",
"martin",
"freedom",
"ginger",
"blowjob",
"nicole",
"sparky",
"yellow",
"camaro",
"secret",
"dick",
"falcon",
"taylor",
"111111",
"131313",
"123123",
"bitch",
"hello",
"scooter",
"please",
"porsche",
"guitar",
"chelsea",
"black",
"diamond",
"nascar",
"jackson",
"cameron",
"654321",
"computer",
"amanda",
"wizard",
"xxxxxxxx",
"money",
"phoenix",
"mickey",
"bailey",
"knight",
"iceman",
"tigers",
"purple",
"andrea",
"horny",
"dakota",
"aaaaaa",
"player",
"sunshine",
"morgan",
"starwars",
"boomer",
"cowboys",
"edward",
"charles",
"girls",
"booboo",
"coffee",
"xxxxxx",
"bulldog",
"ncc1701",
"rabbit",
"peanut",
"john",
"johnny",
"gandalf",
"spanky",
"winter",
"brandy",
"compaq",
"carlos",
"tennis",
"james",
"mike",
"brandon",
"fender",
"anthony",
"blowme",
"ferrari",
"cookie",
"chicken",
"maverick",
"chicago",
"joseph",
"diablo",
"sexsex",
"hardcore",
"666666",
"willie",
"welcome",
"chris",
"panther",
"yamaha",
"justin",
"banana",
"driver",
"marine",
"angels",
"fishing",
"david",
"maddog",
"hooters",
"wilson",
"butthead",
"dennis",
"fucking",
"captain",
"bigdick",
"chester",
"smokey",
"xavier",
"steven",
"viking",
"snoopy",
"blue",
"eagles",
"winner",
"samantha",
"house",
"miller",
"flower",
"jack",
"firebird",
"butter",
"united",
"turtle",
"steelers",
"tiffany",
"zxcvbn",
"tomcat",
"golf",
"bond007",
"bear",
"tiger",
"doctor",
"gateway",
"gators",
"angel",
"junior",
"thx1138",
"porno",
"badboy",
"debbie",
"spider",
"melissa",
"booger",
"1212",
"flyers",
"fish",
"porn",
"matrix",
"teens",
"scooby",
"jason",
"walter",
"cumshot",
"boston",
"braves",
"yankee",
"lover",
"barney",
"victor",
"tucker",
"princess",
"mercedes",
"5150",
"doggie",
"zzzzzz",
"gunner",
"horney",
"bubba",
"2112",
"fred",
"johnson",
"xxxxx",
"tits",
"member",
"boobs",
"donald",
"bigdaddy",
"bronco",
"penis",
"voyager",
"rangers",
"birdie",
"trouble",
"white",
"topgun",
"bigtits",
"bitches",
"green",
"super",
"qazwsx",
"magic",
"lakers",
"rachel",
"slayer",
"scott",
"2222",
"asdf",
"video",
"london",
"7777",
"marlboro",
"srinivas",
"internet",
"action",
"carter",
"jasper",
"monster",
"teresa",
"jeremy",
"11111111",
"bill",
"crystal",
"peter",
"pussies",
"cock",
"beer",
"rocket",
"theman",
"oliver",
"prince",
"beach",
"amateur",
"7777777",
"muffin",
"redsox",
"star",
"testing",
"shannon",
"murphy",
"frank",
"hannah",
"dave",
"eagle1",
"11111",
"mother",
"nathan",
"raiders",
"steve",
"forever",
"angela",
"viper",
"ou812",
"jake",
"lovers",
"suckit",
"gregory",
"buddy",
"whatever",
"young",
"nicholas",
"lucky",
"helpme",
"jackie",
"monica",
"midnight",
"college",
"baby",
"cunt",
"brian",
"mark",
"startrek",
"sierra",
"leather",
"232323",
"4444",
"beavis",
"bigcock",
"happy",
"sophie",
"ladies",
"naughty",
"giants",
"booty",
"blonde",
"fucked",
"golden",
"0",
"fire",
"sandra",
"pookie",
"packers",
"einstein",
"dolphins",
"chevy",
"winston",
"warrior",
"sammy",
"slut",
"8675309",
"zxcvbnm",
"nipples",
"power",
"victoria",
"asdfgh",
"vagina",
"toyota",
"travis",
"hotdog",
"paris",
"rock",
"xxxx",
"extreme",
"redskins",
"erotic",
"dirty",
"ford",
"freddy",
"arsenal",
"access14",
"wolf",
"nipple",
"iloveyou",
"alex",
"florida",
"eric",
"legend",
"movie",
"success",
"rosebud",
"jaguar",
"great",
"cool",
"cooper",
"1313",
"scorpio",
"mountain",
"madison",
"987654",
"brazil",
"lauren",
"japan",
"naked",
"squirt",
"stars",
"apple",
"alexis",
"aaaa",
"bonnie",
"peaches",
"jasmine",
"kevin",
"matt",
"qwertyui",
"danielle",
"beaver",
"4321",
"4128",
"runner",
"swimming",
"dolphin",
"gordon",
"casper",
"stupid",
"shit",
"saturn",
"gemini",
"apples",
"august",
"3333",
"canada",
"blazer",
"cumming",
"hunting",
"kitty",
"rainbow",
"112233",
"arthur",
"cream",
"calvin",
"shaved",
"surfer",
"samson",
"kelly",
"paul",
"mine",
"king",
"racing",
"5555",
"eagle",
"hentai",
"newyork",
"little",
"redwings",
"smith",
"sticky",
"cocacola",
"animal",
"broncos",
"private",
"skippy",
"marvin",
"blondes",
"enjoy",
"girl",
"apollo",
"parker",
"qwert",
"time",
"sydney",
"women",
"voodoo",
"magnum",
"juice",
"abgrtyu",
"777777",
"dreams",
"maxwell",
"music",
"rush2112",
"russia",
"scorpion",
"rebecca",
"tester",
"mistress",
"phantom",
"billy",
"6666",
"albert"
];
$index = array_search($search, $worst_password_list);
if ($index === FALSE) {
return false;
} else {
return $index + 1;
}
}

@ -0,0 +1,8 @@
auxiliary.org-netbeans-modules-html-editor-lib.default-html-public-id=-//W3C//DTD HTML 4.01 Transitional//EN
include.path=${php.global.include.path}
php.version=PHP_70
source.encoding=UTF-8
src.dir=.
tags.asp=false
tags.short=false
web.root=.

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.php.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>WebAppTemplate</name>
</data>
</configuration>
</project>

@ -0,0 +1,11 @@
<?php
// List of pages and metadata
define("PAGES", [
"home" => [
"title" => "home"
],
"404" => [
"title" => "404 error"
]
]);

@ -0,0 +1,5 @@
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-sm-offset-3 col-md-offset-4 col-lg-offset-4">
<div class="alert alert-warning"><b><?php lang("404 error");?></b><br /> <?php lang("page not found"); ?></div>
</div>
</div>

@ -0,0 +1,226 @@
<?php
/**
* This file contains global settings and utility functions.
*/
ob_start(); // allow sending headers after content
// Unicode, solves almost all stupid encoding problems
header('Content-Type: text/html; charset=utf-8');
// l33t $ecurity h4x
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');
header('X-Powered-By: Late-night coding frenzies (plz send caffeine, thx)');
$session_length = 60 * 60; // 1 hour
session_set_cookie_params($session_length, "/", null, false, true);
session_start(); // stick some cookies in it
//
// Composer
require __DIR__ . '/vendor/autoload.php';
// Settings file
require __DIR__ . '/settings.php';
// List of alert messages
require __DIR__ . '/lang/messages.php';
// text strings (i18n)
require __DIR__ . '/lang/' . LANGUAGE . ".php";
/**
* Kill off the running process and spit out an error message
* @param string $error error message
*/
function sendError($error) {
die("<!DOCTYPE html><html><head><title>Error</title></head><body><h1 style='color: red; font-family: sans-serif; font-size:100%;'>" . htmlspecialchars($error) . "</h1></body></html>");
}
date_default_timezone_set(TIMEZONE);
// Database settings
// Also inits database and stuff
use Medoo\Medoo;
$database;
try {
$database = new Medoo([
'database_type' => DB_TYPE,
'database_name' => DB_NAME,
'server' => DB_SERVER,
'username' => DB_USER,
'password' => DB_PASS,
'charset' => DB_CHARSET
]);
} catch (Exception $ex) {
//header('HTTP/1.1 500 Internal Server Error');
sendError("Database error. Try again later. $ex");
}
if (!DEBUG) {
error_reporting(0);
} else {
error_reporting(E_ALL);
ini_set('display_errors', 'On');
}
$VARS;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$VARS = $_POST;
define("GET", false);
} else {
$VARS = $_GET;
define("GET", true);
}
/**
* Checks if a string or whatever is empty.
* @param $str The thingy to check
* @return boolean True if it's empty or whatever.
*/
function is_empty($str) {
return (is_null($str) || !isset($str) || $str == '');
}
/**
* I18N string getter. If the key doesn't exist, outputs the key itself.
* @param string $key I18N string key
* @param boolean $echo whether to echo the result or return it (default echo)
*/
function lang($key, $echo = true) {
if (array_key_exists($key, STRINGS)) {
$str = STRINGS[$key];
} else {
$str = $key;
}
if ($echo) {
echo $str;
} else {
return $str;
}
}
/**
* I18N string getter (with builder). If the key doesn't exist, outputs the key itself.
* @param string $key I18N string key
* @param array $replace key-value array of replacements.
* If the string value is "hello {abc}" and you give ["abc" => "123"], the
* result will be "hello 123".
* @param boolean $echo whether to echo the result or return it (default echo)
*/
function lang2($key, $replace, $echo = true) {
if (array_key_exists($key, STRINGS)) {
$str = STRINGS[$key];
} else {
$str = $key;
}
foreach ($replace as $find => $repl) {
$str = str_replace("{" . $find . "}", $repl, $str);
}
if ($echo) {
echo $str;
} else {
return $str;
}
}
/**
* Checks if an email address is valid.
* @param string $email Email to check
* @return boolean True if email passes validation, else false.
*/
function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
/**
* Hashes the given plaintext password
* @param String $password
* @return String the hash, using bcrypt
*/
function encryptPassword($password) {
return password_hash($password, PASSWORD_BCRYPT);
}
/**
* Securely verify a password and its hash
* @param String $password
* @param String $hash the hash to compare to
* @return boolean True if password OK, else false
*/
function comparePassword($password, $hash) {
return password_verify($password, $hash);
}
function dieifnotloggedin() {
if ($_SESSION['loggedin'] != true) {
sendError("Session expired. Please log out and log in again.");
}
}
/**
* Check if the previous database action had a problem.
* @param array $specials int=>string array with special response messages for SQL errors
*/
function checkDBError($specials = []) {
global $database;
$errors = $database->error();
if (!is_null($errors[1])) {
foreach ($specials as $code => $text) {
if ($errors[1] == $code) {
sendError($text);
}
}
sendError("A database error occurred:<br /><code>" . $errors[2] . "</code>");
}
}
/*
* http://stackoverflow.com/a/20075147/2534036
*/
if (!function_exists('base_url')) {
function base_url($atRoot = FALSE, $atCore = FALSE, $parse = FALSE) {
if (isset($_SERVER['HTTP_HOST'])) {
$http = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http';
$hostname = $_SERVER['HTTP_HOST'];
$dir = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
$core = preg_split('@/@', str_replace($_SERVER['DOCUMENT_ROOT'], '', realpath(dirname(__FILE__))), NULL, PREG_SPLIT_NO_EMPTY);
$core = $core[0];
$tmplt = $atRoot ? ($atCore ? "%s://%s/%s/" : "%s://%s/") : ($atCore ? "%s://%s/%s/" : "%s://%s%s");
$end = $atRoot ? ($atCore ? $core : $hostname) : ($atCore ? $core : $dir);
$base_url = sprintf($tmplt, $http, $hostname, $end);
} else
$base_url = 'http://localhost/';
if ($parse) {
$base_url = parse_url($base_url);
if (isset($base_url['path']))
if ($base_url['path'] == '/')
$base_url['path'] = '';
}
return $base_url;
}
}
function redirectToPageId($id, $args, $dontdie) {
header('Location: ' . URL . '?id=' . $id . $args);
if (is_null($dontdie)) {
die("Please go to " . URL . '?id=' . $id . $args);
}
}
function redirectIfNotLoggedIn() {
if ($_SESSION['loggedin'] !== TRUE) {
header('Location: ' . URL . '/login.php');
die();
}
}

@ -0,0 +1,47 @@
<?php
// Whether to show debugging data in output.
// DO NOT SET TO TRUE IN PRODUCTION!!!
define("DEBUG", false);
// Database connection settings
// See http://medoo.in/api/new for info
define("DB_TYPE", "mysql");
define("DB_NAME", "app");
define("DB_SERVER", "localhost");
define("DB_USER", "app");
define("DB_PASS", "");
define("DB_CHARSET", "utf8");
define("SITE_TITLE", "Web App Template");
// Used to identify the system in OTP and other places
define("SYSTEM_NAME", "Web App Template");
// For supported values, see http://php.net/manual/en/timezones.php
define("TIMEZONE", "America/Denver");
// Base URL for site links.
define('URL', 'http://localhost:8000/');
// See lang folder for language options
define('LANGUAGE', "en_us");
// Minimum length for new passwords
// The system checks new passwords against the 500 worst passwords and rejects
// any matches.
// If you want to have additional password requirements, go edit action.php.
// However, all that does is encourage people to use the infamous
// "post-it password manager". See also https://xkcd.com/936/ and
// http://stackoverflow.com/a/34166252/2534036 for reasons why forcing passwords
// like CaPs45$% is not actually a great idea.
// Encourage users to use 2-factor auth whenever possible.
define("MIN_PASSWORD_LENGTH", 8);
//////////////////////////////////////////////////////////////
// /!\ Warning: Changing these values may /!\ //
// /!\ violate the terms of your license agreement! /!\ //
//////////////////////////////////////////////////////////////
define("LICENSE_TEXT", "<b>Free Software: MIT License</b>");
define("COPYRIGHT_NAME", "Netsyms Technologies");
//////////////////////////////////////////////////////////////

@ -0,0 +1,13 @@
.banner-image {
max-height: 100px;
margin: 2em auto;
}
.navbar-brand {
font-size: 110%;
}
.footer {
margin-top: 10em;
text-align: center;
}

Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá

2337
static/css/font-awesome.css vendorováno

Rozdílový obsah nebyl zobrazen, protože je příliš veliký Načíst rozdílové porovnání

Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá

Binární soubor nebyl zobrazen.

Binární soubor nebyl zobrazen.

Rozdílový obsah nebyl zobrazen, protože je příliš veliký Načíst rozdílové porovnání

Za

Šířka:  |  Výška:  |  Velikost: 434 KiB

Binární soubor nebyl zobrazen.

Binární soubor nebyl zobrazen.

Binární soubor nebyl zobrazen.

@ -0,0 +1,7 @@
$(document).ready(function () {
/* Fade out alerts */
$(".alert .close").click(function (e) {
$(this).parent().fadeOut('slow');
});
});

Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá

Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá
Načítá se…
Zrušit
Uložit