@@ -0,0 +1,15 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
try { | |||
$uid = LoginKey::getuid($VARS['code']); | |||
exitWithJson(["status" => "OK", "uid" => $uid]); | |||
} catch (Exception $ex) { | |||
sendJsonResp("", "ERROR"); | |||
} |
@@ -0,0 +1,17 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
$code = LoginKey::generate($VARS['appname']); | |||
if (strpos($SETTINGS['url'], "http") === 0) { | |||
$url = $SETTINGS['url'] . "login/"; | |||
} else { | |||
$url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . $SETTINGS['url'] . "login/"; | |||
} | |||
exitWithJson(["status" => "OK", "code" => $code, "loginurl" => $url]); |
@@ -212,4 +212,16 @@ $APIS = [ | |||
"id" => "numeric" | |||
] | |||
], | |||
"getloginkey" => [ | |||
"load" => "getloginkey.php", | |||
"vars" => [ | |||
"appname" => "string" | |||
] | |||
], | |||
"checkloginkey" => [ | |||
"load" => "checkloginkey.php", | |||
"vars" => [ | |||
"code" => "string" | |||
] | |||
] | |||
]; |
@@ -0,0 +1,28 @@ | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
DROP TABLE IF EXISTS `available_apps`; | |||
DROP TABLE IF EXISTS `apps`; | |||
CREATE TABLE IF NOT EXISTS `userloginkeys` ( | |||
`id` INT(11) NOT NULL AUTO_INCREMENT, | |||
`key` VARCHAR(255) NOT NULL, | |||
`expires` DATETIME NULL DEFAULT NULL, | |||
`uid` INT(11) NULL DEFAULT NULL, | |||
PRIMARY KEY (`id`, `key`), | |||
UNIQUE INDEX `id_UNIQUE` (`id` ASC), | |||
UNIQUE INDEX `key_UNIQUE` (`key` ASC), | |||
INDEX `fk_userloginkeys_accounts1_idx` (`uid` ASC), | |||
CONSTRAINT `fk_userloginkeys_accounts1` | |||
FOREIGN KEY (`uid`) | |||
REFERENCES `accounts` (`uid`) | |||
ON DELETE NO ACTION | |||
ON UPDATE NO ACTION) | |||
ENGINE = InnoDB | |||
DEFAULT CHARACTER SET = utf8; | |||
ALTER TABLE `userloginkeys` | |||
ADD COLUMN `appname` VARCHAR(255) NOT NULL AFTER `uid`; |
@@ -1,238 +1,84 @@ | |||
<?php | |||
/* This Source Code Form is subject to the terms of the Mozilla Public | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
require_once __DIR__ . "/required.php"; | |||
// If we're logged in, we don't need to be here. | |||
if (!empty($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) { | |||
header('Location: home.php'); | |||
die(); | |||
// This branch will likely run if the user signed in from a different app. | |||
// if we're logged in, we don't need to be here. | |||
if (!empty($_SESSION['loggedin']) && $_SESSION['loggedin'] === true && !isset($_GET['permissionerror'])) { | |||
header('Location: app.php'); | |||
} | |||
/* Authenticate user */ | |||
$username_ok = false; | |||
$multiauth = false; | |||
$change_password = false; | |||
if (empty($VARS['progress'])) { | |||
// Easy way to remove "undefined" warnings. | |||
} else if ($VARS['progress'] == "1") { | |||
engageRateLimit(); | |||
if (!$SETTINGS['captcha']['enabled'] || ($SETTINGS['captcha']['enabled'] && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], $SETTINGS['captcha']['server'] . "/api.php"))) { | |||
$autherror = ""; | |||
$user = User::byUsername($VARS['username']); | |||
if ($user->exists()) { | |||
$status = $user->getStatus()->getString(); | |||
switch ($status) { | |||
case "LOCKED_OR_DISABLED": | |||
$alert = $Strings->get("account locked", false); | |||
break; | |||
case "TERMINATED": | |||
$alert = $Strings->get("account terminated", false); | |||
break; | |||
case "CHANGE_PASSWORD": | |||
$alert = $Strings->get("password expired", false); | |||
$alerttype = "info"; | |||
$_SESSION['username'] = $user->getUsername(); | |||
$_SESSION['uid'] = $user->getUID(); | |||
$change_password = true; | |||
break; | |||
case "NORMAL": | |||
$username_ok = true; | |||
break; | |||
case "ALERT_ON_ACCESS": | |||
$mail_resp = $user->sendAlertEmail(); | |||
if ($SETTINGS['debug']) { | |||
var_dump($mail_resp); | |||
} | |||
$username_ok = true; | |||
break; | |||
default: | |||
if (!empty($error)) { | |||
$alert = $error; | |||
} else { | |||
$alert = $Strings->get("login error", false); | |||
} | |||
break; | |||
} | |||
if ($username_ok) { | |||
if ($user->checkPassword($VARS['password'])) { | |||
$_SESSION['passok'] = true; // stop logins using only username and authcode | |||
if ($user->has2fa()) { | |||
$multiauth = true; | |||
} else { | |||
Session::start($user); | |||
Log::insert(LogType::LOGIN_OK, $user->getUID()); | |||
header('Location: app.php'); | |||
die("Logged in, go to app.php"); | |||
} | |||
} else { | |||
$alert = $Strings->get("login incorrect", false); | |||
Log::insert(LogType::LOGIN_FAILED, null, "Username: " . $VARS['username']); | |||
} | |||
} | |||
} else { // User does not exist anywhere | |||
$alert = $Strings->get("login incorrect", false); | |||
Log::insert(LogType::LOGIN_FAILED, null, "Username: " . $VARS['username']); | |||
} | |||
} else { | |||
$alert = $Strings->get("captcha error", false); | |||
Log::insert(LogType::BAD_CAPTCHA, null, "Username: " . $VARS['username']); | |||
} | |||
} else if ($VARS['progress'] == "2") { | |||
engageRateLimit(); | |||
$user = User::byUsername($VARS['username']); | |||
if ($_SESSION['passok'] !== true) { | |||
// stop logins using only username and authcode | |||
sendError("Password integrity check failed!"); | |||
} | |||
if ($user->check2fa($VARS['authcode'])) { | |||
Session::start($user); | |||
Log::insert(LogType::LOGIN_OK, $user->getUID()); | |||
header('Location: app.php'); | |||
die("Logged in, go to app.php"); | |||
} else { | |||
$alert = $Strings->get("2fa incorrect", false); | |||
Log::insert(LogType::BAD_2FA, null, "Username: " . $VARS['username']); | |||
} | |||
} else if ($VARS['progress'] == "chpasswd") { | |||
engageRateLimit(); | |||
if (!empty($_SESSION['username'])) { | |||
$user = User::byUsername($_SESSION['username']); | |||
try { | |||
$result = $user->changePassword($VARS['oldpass'], $VARS['newpass'], $VARS['conpass']); | |||
if ($result === TRUE) { | |||
$alert = $Strings->get(MESSAGES["password_updated"]["string"], false); | |||
$alerttype = MESSAGES["password_updated"]["type"]; | |||
} | |||
} catch (PasswordMatchException $e) { | |||
$alert = $Strings->get(MESSAGES["passwords_same"]["string"], false); | |||
$alerttype = "danger"; | |||
} catch (PasswordMismatchException $e) { | |||
$alert = $Strings->get(MESSAGES["new_password_mismatch"]["string"], false); | |||
$alerttype = "danger"; | |||
} catch (IncorrectPasswordException $e) { | |||
$alert = $Strings->get(MESSAGES["old_password_mismatch"]["string"], false); | |||
$alerttype = "danger"; | |||
} catch (WeakPasswordException $e) { | |||
$alert = $Strings->get(MESSAGES["weak_password"]["string"], false); | |||
$alerttype = "danger"; | |||
} | |||
} else { | |||
session_destroy(); | |||
header('Location: index.php'); | |||
die(); | |||
} | |||
} | |||
if (!empty($_GET['logout'])) { | |||
// Show a logout message instead of immediately redirecting to login flow | |||
?> | |||
<!DOCTYPE html> | |||
<meta charset="UTF-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
header("Link: <static/fonts/Roboto.css>; rel=preload; as=style", false); | |||
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false); | |||
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false); | |||
header("Link: <static/css/index.css>; rel=preload; as=style", false); | |||
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false); | |||
header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", false); | |||
?> | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title><?php echo $SETTINGS['site_title']; ?></title> | |||
<title><?php echo $SETTINGS['site_title']; ?></title> | |||
<link rel="icon" href="static/img/logo.svg"> | |||
<link rel="icon" href="static/img/logo.svg"> | |||
<link href="static/css/bootstrap.min.css" rel="stylesheet"> | |||
<link href="static/css/svg-with-js.min.css" rel="stylesheet"> | |||
<style nonce="<?php echo $SECURE_NONCE; ?>"> | |||
.display-5 { | |||
font-size: 3rem; | |||
font-weight: 300; | |||
line-height: 1.2; | |||
} | |||
</style> | |||
<link href="static/css/bootstrap.min.css" rel="stylesheet"> | |||
<link href="static/css/material-color/material-color.min.css" rel="stylesheet"> | |||
<link href="static/css/index.css" rel="stylesheet"> | |||
<?php if ($SETTINGS['captcha']['enabled']) { ?> | |||
<script src="<?php echo $SETTINGS['captcha']['server'] ?>/captcheck.dist.js"></script> | |||
<?php } ?> | |||
</head> | |||
<body> | |||
<div class="container mt-4"> | |||
<div class="row justify-content-center"> | |||
<div class="col-auto"> | |||
<img class="banner-image" src="static/img/logo.svg" /> | |||
<div class="col-12 text-center"> | |||
<h1 class="display-5 mb-4"><?php $Strings->get("You have been logged out.") ?></h1> | |||
</div> | |||
</div> | |||
<div class="row justify-content-center"> | |||
<div class="card col-11 col-xs-11 col-sm-8 col-md-6 col-lg-4"> | |||
<div class="card-body"> | |||
<h5 class="card-title"><?php $Strings->get("sign in"); ?></h5> | |||
<form action="" method="POST"> | |||
<?php | |||
if (!empty($alert)) { | |||
$alerttype = isset($alerttype) ? $alerttype : "danger"; | |||
?> | |||
<div class="alert alert-<?php echo $alerttype ?>"> | |||
<?php | |||
switch ($alerttype) { | |||
case "danger": | |||
$alerticon = "fas fa-times"; | |||
break; | |||
case "warning": | |||
$alerticon = "fas fa-exclamation-triangle"; | |||
break; | |||
case "info": | |||
$alerticon = "fas fa-info-circle"; | |||
break; | |||
case "success": | |||
$alerticon = "fas fa-check"; | |||
break; | |||
default: | |||
$alerticon = "far fa-square"; | |||
} | |||
?> | |||
<i class="<?php echo $alerticon ?> fa-fw"></i> <?php echo $alert ?> | |||
</div> | |||
<?php | |||
} | |||
if (!$multiauth && !$change_password) { | |||
?> | |||
<input type="text" class="form-control" name="username" placeholder="<?php $Strings->get("username"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br /> | |||
<input type="password" class="form-control" name="password" placeholder="<?php $Strings->get("password"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /><br /> | |||
<?php if ($SETTINGS['captcha']['enabled']) { ?> | |||
<div class="captcheck_container" data-stylenonce="<?php echo $SECURE_NONCE; ?>"></div> | |||
<br /> | |||
<?php } ?> | |||
<input type="hidden" name="progress" value="1" /> | |||
<?php | |||
} else if ($multiauth) { | |||
?> | |||
<div class="alert alert-info"> | |||
<?php $Strings->get("2fa prompt"); ?> | |||
</div> | |||
<input type="text" class="form-control" name="authcode" placeholder="<?php $Strings->get("authcode"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br /> | |||
<input type="hidden" name="progress" value="2" /> | |||
<input type="hidden" name="username" value="<?php echo $VARS['username']; ?>" /> | |||
<?php | |||
} else if ($change_password) { | |||
?> | |||
<input type="password" class="form-control" name="oldpass" placeholder="Current password" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br /> | |||
<input type="password" class="form-control" name="newpass" placeholder="New password" required="required" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" /><br /> | |||
<input type="password" class="form-control" name="conpass" placeholder="New password (again)" required="required" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" /><br /> | |||
<input type="hidden" name="progress" value="chpasswd" /> | |||
<?php | |||
} | |||
?> | |||
<button type="submit" class="btn btn-primary"> | |||
<?php $Strings->get("continue"); ?> | |||
</button> | |||
</form> | |||
<div class="col-12 col-sm-8 col-lg-6"> | |||
<div class="card mt-4"> | |||
<div class="card-body"> | |||
<a href="./index.php" class="btn btn-primary btn-block"><?php $Strings->get("Log in again"); ?></a> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="footer"> | |||
<?php echo $SETTINGS['footer_text']; ?><br /> | |||
Copyright © <?php echo date('Y'); ?> <?php echo $SETTINGS['copyright']; ?> | |||
</div> | |||
<script src="static/js/jquery-3.3.1.min.js"></script> | |||
<script src="static/js/bootstrap.bundle.min.js"></script> | |||
</body> | |||
</html> | |||
</div> | |||
<script src="static/js/fontawesome-all.min.js"></script> | |||
<?php | |||
die(); | |||
} | |||
if (empty($_SESSION["login_code"])) { | |||
$redirecttologin = true; | |||
} else { | |||
try { | |||
$uid = LoginKey::getuid($_SESSION["login_code"]); | |||
$user = new User($uid); | |||
Session::start($user); | |||
$_SESSION["login_code"] = null; | |||
header('Location: app.php'); | |||
die("Logged in, go to app.php"); | |||
} catch (Exception $ex) { | |||
$redirecttologin = true; | |||
} | |||
} | |||
if ($redirecttologin) { | |||
try { | |||
$code = LoginKey::generate($SETTINGS["site_title"]); | |||
$_SESSION["login_code"] = $code; | |||
header("Location: ./login/?code=" . htmlentities($code) . "&redirect=" . htmlentities($_SERVER["REQUEST_URI"])); | |||
} catch (Exception $ex) { | |||
sendError($ex->getMessage()); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
{ | |||
"Login to {app}": "Login to {app}", | |||
"Username not found.": "Username not found.", | |||
"Password for {user}": "Password for {user}", | |||
"Password incorrect.": "Password incorrect.", | |||
"Two-factor code": "Two-factor code", | |||
"Code incorrect.": "Code incorrect.", | |||
"Current password for {user}": "Current password for {user}", | |||
"New password": "New password", | |||
"New password (again)": "New password (again)", | |||
"Fill in all three boxes.": "Fill in all three boxes." | |||
} |
@@ -4,5 +4,6 @@ | |||
"account options": "Account options", | |||
"sync": "Sync settings", | |||
"settings": "Settings", | |||
"account": "Account" | |||
"account": "Account", | |||
"Home": "Home" | |||
} |
@@ -0,0 +1,33 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
class LoginKey { | |||
public static function generate(string $appname): string { | |||
global $database; | |||
do { | |||
$code = base64_encode(random_bytes(32)); | |||
} while ($database->has('userloginkeys', ['key' => $code])); | |||
$database->insert('userloginkeys', ['key' => $code, 'expires' => date("Y-m-d H:i:s", time() + 600), 'appname' => $appname]); | |||
return $code; | |||
} | |||
public static function getuid(string $code): int { | |||
global $database; | |||
if (!$database->has('userloginkeys', ["AND" => ['key' => $code, 'uid[!]' => null]])) { | |||
throw new Exception(); | |||
} | |||
$uid = $database->get('userloginkeys', 'uid', ['key' => $code]); | |||
return $uid; | |||
} | |||
} |
@@ -0,0 +1,150 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
require_once __DIR__ . "/../required.php"; | |||
if (empty($_GET['code']) || empty($_GET['redirect'])) { | |||
die("Bad request."); | |||
} | |||
// Delete old keys to keep the table small and tidy | |||
$database->delete("userloginkeys", ["expires[<]" => date("Y-m-d H:i:s")]); | |||
if (!$database->has("userloginkeys", ["AND" => ["key" => $_GET["code"]], "expires[>]" => date("Y-m-d H:i:s"), "uid" => null])) { | |||
header("Location: $_GET[redirect]"); | |||
die("Invalid auth code."); | |||
} | |||
$APPNAME = $database->get("userloginkeys", "appname", ["key" => $_GET["code"]]); | |||
if (empty($_SESSION['thisstep'])) { | |||
$_SESSION['thisstep'] = "username"; | |||
} | |||
$error = ""; | |||
function sendUserBack($code, $url, $uid) { | |||
global $database; | |||
$database->update("userloginkeys", ["uid" => $uid], ["key" => $code]); | |||
header("Location: $url"); | |||
die("<a href=\"" . htmlspecialchars($url) . "\">Click here</a>"); | |||
} | |||
if (!empty($_SESSION['check'])) { | |||
switch ($_SESSION['check']) { | |||
case "username": | |||
if (empty($_POST['username'])) { | |||
$_SESSION['thisstep'] = "username"; | |||
break; | |||
} | |||
$user = User::byUsername($_POST['username']); | |||
if ($user->exists()) { | |||
$_SESSION['login_uid'] = $user->getUID(); | |||
switch ($user->getStatus()->get()) { | |||
case AccountStatus::LOCKED_OR_DISABLED: | |||
$error = $Strings->get("account locked", false); | |||
break; | |||
case AccountStatus::TERMINATED: | |||
$error = $Strings->get("account terminated", false); | |||
break; | |||
case AccountStatus::ALERT_ON_ACCESS: | |||
$mail_resp = $user->sendAlertEmail(); | |||
case AccountStatus::NORMAL: | |||
$_SESSION['thisstep'] = "password"; | |||
break; | |||
case AccountStatus::CHANGE_PASSWORD: | |||
$_SESSION['thisstep'] = "change_password"; | |||
break; | |||
} | |||
} else { | |||
$error = $Strings->get("Username not found.", false); | |||
} | |||
break; | |||
case "password": | |||
if (empty($_POST['password'])) { | |||
$_SESSION['thisstep'] = "password"; | |||
break; | |||
} | |||
if (empty($_SESSION['login_uid'])) { | |||
$_SESSION['thisstep'] = "username"; | |||
break; | |||
} | |||
$user = new User($_SESSION['login_uid']); | |||
if ($user->checkPassword($_POST['password'])) { | |||
$_SESSION['login_pwd'] = true; | |||
if ($user->has2fa()) { | |||
$_SESSION['thisstep'] = "totp"; | |||
} else { | |||
sendUserBack($_GET['code'], $_GET['redirect'], $_SESSION['login_uid']); | |||
} | |||
} else { | |||
$error = $Strings->get("Password incorrect.", false); | |||
} | |||
break; | |||
case "change_password": | |||
if (empty($_POST['oldpassword']) || empty($_POST['newpassword']) || empty($_POST['newpassword2'])) { | |||
$_SESSION['thisstep'] = "change_password"; | |||
$error = $Strings->get("Fill in all three boxes.", false); | |||
break; | |||
} | |||
$user = new User($_SESSION['login_uid']); | |||
try { | |||
$result = $user->changePassword($_POST['oldpassword'], $_POST['newpassword'], $_POST['newpassword2']); | |||
if ($result === TRUE) { | |||
if ($user->has2fa()) { | |||
$_SESSION['thisstep'] = "totp"; | |||
} else { | |||
sendUserBack($_GET['code'], $_GET['redirect'], $_SESSION['login_uid']); | |||
} | |||
} | |||
} catch (PasswordMatchException $e) { | |||
$error = $Strings->get(MESSAGES["passwords_same"]["string"], false); | |||
} catch (PasswordMismatchException $e) { | |||
$error = $Strings->get(MESSAGES["new_password_mismatch"]["string"], false); | |||
} catch (IncorrectPasswordException $e) { | |||
$error = $Strings->get(MESSAGES["old_password_mismatch"]["string"], false); | |||
} catch (WeakPasswordException $e) { | |||
$error = $Strings->get(MESSAGES["weak_password"]["string"], false); | |||
} | |||
break; | |||
case "totp": | |||
if (empty($_POST['totp']) || empty($_SESSION['login_uid'])) { | |||
$_SESSION['thisstep'] = "username"; | |||
break; | |||
} | |||
$user = new User($_SESSION['login_uid']); | |||
if ($user->check2fa($_POST['totp'])) { | |||
sendUserBack($_GET['code'], $_GET['redirect'], $_SESSION['login_uid']); | |||
} else { | |||
$error = $Strings->get("Code incorrect.", false); | |||
} | |||
break; | |||
} | |||
} | |||
include __DIR__ . "/parts/header.php"; | |||
switch ($_SESSION['thisstep']) { | |||
case "username": | |||
require __DIR__ . "/parts/username.php"; | |||
break; | |||
case "password": | |||
require __DIR__ . "/parts/password.php"; | |||
break; | |||
case "change_password": | |||
require __DIR__ . "/parts/change_password.php"; | |||
break; | |||
case "totp": | |||
require __DIR__ . "/parts/totp.php"; | |||
break; | |||
} | |||
include __DIR__ . "/parts/footer.php"; |
@@ -0,0 +1,51 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
$_SESSION['check'] = "change_password"; | |||
$username = (new User($_SESSION['login_uid']))->getUsername(); | |||
?> | |||
<form action="" method="POST"> | |||
<div> | |||
<?php $Strings->get("password expired"); ?> | |||
</div> | |||
<div class="form-group"> | |||
<label for="oldpassword"><?php $Strings->build("Current password for {user}", ["user" => htmlentities($username)]); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-lock"></i></span> | |||
</div> | |||
<input type="password" class="form-control" id="oldpassword" name="oldpassword" placeholder="" required autofocus> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label for="newpassword"><?php $Strings->get("New password"); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-lock"></i></span> | |||
</div> | |||
<input type="password" class="form-control" id="newpassword" name="newpassword" placeholder="" required> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label for="newpassword2"><?php $Strings->get("New password (again)"); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-lock"></i></span> | |||
</div> | |||
<input type="password" class="form-control" id="newpassword2" name="newpassword2" placeholder="" required> | |||
</div> | |||
</div> | |||
<div class="d-flex"> | |||
<button type="submit" class="btn btn-primary ml-auto"> | |||
<i class="fas fa-chevron-right"></i> <?php $Strings->get("continue"); ?> | |||
</button> | |||
</div> | |||
</form> |
@@ -0,0 +1,15 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
?> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<script src="../static/js/fontawesome-all.min.js"></script> |
@@ -0,0 +1,44 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
header("Link: <../static/fonts/Roboto.css>; rel=preload; as=style", false); | |||
header("Link: <../static/css/bootstrap.min.css>; rel=preload; as=style", false); | |||
header("Link: <../static/css/login.css>; rel=preload; as=style", false); | |||
header("Link: <../static/css/svg-with-js.min.css>; rel=preload; as=style", false); | |||
header("Link: <../static/js/fontawesome-all.min.js>; rel=preload; as=script", false); | |||
?> | |||
<!DOCTYPE html> | |||
<meta charset="UTF-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title><?php echo $SETTINGS['site_title']; ?></title> | |||
<link rel="icon" href="../static/img/logo.svg"> | |||
<link href="../static/css/bootstrap.min.css" rel="stylesheet"> | |||
<link href="../static/css/login.css" rel="stylesheet"> | |||
<link href="../static/css/svg-with-js.min.css" rel="stylesheet"> | |||
<div class="container mt-4"> | |||
<div class="row justify-content-center"> | |||
<div class="col-12 text-center"> | |||
<h1 class="display-5 mb-4"><?php $Strings->build("Login to {app}", ["app" => htmlentities($APPNAME)]); ?></h1> | |||
</div> | |||
<div class="col-12 col-sm-8 col-lg-6"> | |||
<div class="card mt-4"> | |||
<div class="card-body"> | |||
<?php | |||
if (!empty($error)) { | |||
?> | |||
<div class="text-danger"> | |||
<?php echo $error; ?> | |||
</div> | |||
<?php | |||
} | |||
?> |
@@ -0,0 +1,29 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
$_SESSION['check'] = "password"; | |||
$username = (new User($_SESSION['login_uid']))->getUsername(); | |||
?> | |||
<form action="" method="POST"> | |||
<div class="form-group"> | |||
<label for="password"><?php $Strings->build("Password for {user}", ["user" => htmlentities($username)]); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-lock"></i></span> | |||
</div> | |||
<input type="password" class="form-control" id="password" name="password" aria-describedby="passwordHelp" placeholder="" required autofocus> | |||
</div> | |||
<small id="passwordHelp" class="form-text text-muted">Enter your password.</small> | |||
</div> | |||
<div class="d-flex"> | |||
<button type="submit" class="btn btn-primary ml-auto"> | |||
<i class="fas fa-chevron-right"></i> <?php $Strings->get("continue"); ?> | |||
</button> | |||
</div> | |||
</form> |
@@ -0,0 +1,28 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
$_SESSION['check'] = "totp"; | |||
?> | |||
<form action="" method="POST"> | |||
<div class="form-group"> | |||
<label for="totp"><?php $Strings->get("Two-factor code"); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-mobile-alt"></i></span> | |||
</div> | |||
<input type="text" class="form-control" id="totp" name="totp" aria-describedby="totpHelp" placeholder="" required autofocus> | |||
</div> | |||
<small id="passwordHelp" class="form-text text-muted">Enter the two-factor code from your mobile device.</small> | |||
</div> | |||
<div class="d-flex"> | |||
<button type="submit" class="btn btn-primary ml-auto"> | |||
<i class="fas fa-chevron-right"></i> <?php $Strings->get("continue"); ?> | |||
</button> | |||
</div> | |||
</form> |
@@ -0,0 +1,28 @@ | |||
<?php | |||
/* | |||
* This Source Code Form is subject to the terms of the Mozilla Public | |||
* License, v. 2.0. If a copy of the MPL was not distributed with this | |||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
$_SESSION['check'] = "username"; | |||
?> | |||
<form action="" method="POST"> | |||
<div class="form-group"> | |||
<label for="username"><?php $Strings->get("username"); ?></label> | |||
<div class="input-group"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text"><i class="fas fa-user"></i></span> | |||
</div> | |||
<input type="text" class="form-control" id="username" name="username" aria-describedby="usernameHelp" placeholder="" required autofocus> | |||
</div> | |||
<small id="usernameHelp" class="form-text text-muted">Enter your username.</small> | |||
</div> | |||
<div class="d-flex"> | |||
<button type="submit" class="btn btn-primary ml-auto"> | |||
<i class="fas fa-chevron-right"></i> <?php $Strings->get("continue"); ?> | |||
</button> | |||
</div> | |||
</form> |
@@ -0,0 +1,11 @@ | |||
/* | |||
This Source Code Form is subject to the terms of the Mozilla Public | |||
License, v. 2.0. If a copy of the MPL was not distributed with this | |||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
*/ | |||
.display-5 { | |||
font-size: 3rem; | |||
font-weight: 300; | |||
line-height: 1.2; | |||
} |