Browse Source

Rewrite a lot of code, close Business/CommonBugs#3, close Business/CommonBugs#4, close #9

V2_Rewrite
Skylar Ittner 4 years ago
parent
commit
b23e4bce30
  1. 43
      action.php
  2. 263
      api.php
  3. 197
      app.php
  4. 13
      apps/404_error.php
  5. 26
      apps/change_password.php
  6. 27
      apps/change_pin.php
  7. 79
      apps/setup_2fa.php
  8. BIN
      database.mwb
  9. 0
      database_upgrade/1.0.1_2.0.sql
  10. 270
      home.php
  11. 271
      index.php
  12. 110
      lang/en_us.php
  13. 4
      langs/en/api.json
  14. 3
      langs/en/sync.json
  15. 0
      langs/messages.php
  16. 35
      lib/Exceptions.lib.php
  17. 72
      lib/Log.lib.php
  18. 71
      lib/Login.lib.php
  19. 19
      lib/Session.lib.php
  20. 10
      lib/Strings.lib.php
  21. 307
      lib/User.lib.php
  22. 538
      lib/login.php
  23. 62
      mobile/index.php
  24. 32
      pages.php
  25. 10
      pages/404.php
  26. 32
      pages/home.php
  27. 127
      pages/security.php
  28. 128
      pages/sync.php
  29. 10
      required.php
  30. 130
      static/css/app.css
  31. 4
      static/css/app.min.css
  32. 15
      static/css/bootstrap.min.css
  33. 66
      static/css/dock.css
  34. 5
      static/css/fa-svg-with-js.css
  35. 4
      static/css/font-awesome.min.css
  36. 15
      static/css/index.css
  37. 14
      static/css/qrcode.css
  38. BIN
      static/fonts/FontAwesome.otf
  39. 72
      static/fonts/Roboto.css
  40. BIN
      static/fonts/Roboto_300.eot
  41. 312
      static/fonts/Roboto_300.svg
  42. BIN
      static/fonts/Roboto_300.ttf
  43. BIN
      static/fonts/Roboto_300.woff
  44. BIN
      static/fonts/Roboto_300.woff2
  45. BIN
      static/fonts/Roboto_400.eot
  46. 308
      static/fonts/Roboto_400.svg
  47. BIN
      static/fonts/Roboto_400.ttf
  48. BIN
      static/fonts/Roboto_400.woff
  49. BIN
      static/fonts/Roboto_400.woff2
  50. BIN
      static/fonts/Roboto_500.eot
  51. 305
      static/fonts/Roboto_500.svg
  52. BIN
      static/fonts/Roboto_500.ttf
  53. BIN
      static/fonts/Roboto_500.woff
  54. BIN
      static/fonts/Roboto_500.woff2
  55. BIN
      static/fonts/Roboto_700.eot
  56. 309
      static/fonts/Roboto_700.svg
  57. BIN
      static/fonts/Roboto_700.ttf
  58. BIN
      static/fonts/Roboto_700.woff
  59. BIN
      static/fonts/Roboto_700.woff2
  60. BIN
      static/fonts/fontawesome-webfont.eot
  61. 2671
      static/fonts/fontawesome-webfont.svg
  62. BIN
      static/fonts/fontawesome-webfont.ttf
  63. BIN
      static/fonts/fontawesome-webfont.woff
  64. BIN
      static/fonts/fontawesome-webfont.woff2
  65. BIN
      static/img/up-arrow-black.png
  66. 94
      static/img/up-arrow-black.svg
  67. BIN
      static/img/up-arrow-white.png
  68. 94
      static/img/up-arrow-white.svg
  69. 81
      static/js/app.js
  70. 1
      static/js/app.min.js
  71. 12
      static/js/bootstrap.min.js
  72. 5
      static/js/fontawesome-all.min.js
  73. 4
      static/js/jquery-3.2.1.min.js
  74. 2
      static/js/jquery-3.3.1.min.js

43
action.php

@ -23,37 +23,39 @@ dieifnotloggedin();
engageRateLimit();
require_once __DIR__ . "/lib/login.php";
function returnToSender($msg, $arg = "") {
global $VARS;
if ($arg == "") {
header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg");
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg");
} else {
header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=" . urlencode($arg));
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=" . urlencode($arg));
}
die();
}
switch ($VARS['action']) {
case "signout":
insertAuthLog(11, $_SESSION['uid']);
Log::insert(LogType::LOGOUT, $_SESSION['uid']);
session_destroy();
header('Location: index.php');
die("Logged out.");
case "chpasswd":
$error = [];
$result = change_password($VARS['oldpass'], $VARS['newpass'], $VARS['conpass'], $error);
if ($result === TRUE) {
returnToSender("password_updated");
}
switch (count($error)) {
case 1:
returnToSender($error[0]);
case 2:
returnToSender($error[0], $error[1]);
default:
returnToSender("generic_op_error");
$user = new User($_SESSION['uid']);
try {
$result = $user->changePassword($VARS['oldpass'], $VARS['newpass'], $VARS['conpass']);
if ($result === TRUE) {
returnToSender("password_updated");
}
} catch (PasswordMatchException $e) {
returnToSender("passwords_same");
} catch (PasswordMismatchException $e) {
returnToSender("new_password_mismatch");
} catch (IncorrectPasswordException $e) {
returnToSender("old_password_mismatch");
} catch (WeakPasswordException $e) {
returnToSender("weak_password");
}
break;
case "chpin":
@ -71,16 +73,17 @@ switch ($VARS['action']) {
if (is_empty($VARS['secret'])) {
returnToSender("invalid_parameters");
}
$user = new User($_SESSION['uid']);
$totp = new TOTP(null, $VARS['secret']);
if (!$totp->verify($VARS["totpcode"])) {
returnToSender("2fa_wrong_code");
}
$database->update('accounts', ['authsecret' => $VARS['secret']], ['uid' => $_SESSION['uid']]);
insertAuthLog(9, $_SESSION['uid']);
$user->save2fa($VARS['secret']);
Log::insert(LogType::ADDED_2FA, $user);
returnToSender("2fa_enabled");
case "rm2fa":
$database->update('accounts', ['authsecret' => ""], ['uid' => $_SESSION['uid']]);
insertAuthLog(10, $_SESSION['uid']);
(new User($_SESSION['uid']))->save2fa("");
Log::insert(LogType::REMOVED_2FA, $_SESSION['uid']);
returnToSender("2fa_removed");
break;
}

263
api.php

@ -12,16 +12,19 @@
* user passwords.
*/
require __DIR__ . '/required.php';
require_once __DIR__ . '/lib/login.php';
header("Content-Type: application/json");
//try {
$key = $VARS['key'];
if ($database->has('apikeys', ['key' => $key]) !== TRUE) {
engageRateLimit();
http_response_code(403);
insertAuthLog(14, null, "Key: " . $key);
if (empty($VARS['key'])) {
die("\"403 Unauthorized\"");
} else {
$key = $VARS['key'];
if ($database->has('apikeys', ['key' => $key]) !== TRUE) {
engageRateLimit();
http_response_code(403);
Log::insert(LogType::API_BAD_KEY, null, "Key: " . $key);
die("\"403 Unauthorized\"");
}
}
/**
@ -40,29 +43,31 @@ function getCensoredKey() {
return $resp;
}
if (empty($VARS['action'])) {
http_response_code(404);
die(json_encode("No action specified."));
}
switch ($VARS['action']) {
case "ping":
exit(json_encode(["status" => "OK"]));
break;
case "auth":
$errmsg = "";
if (authenticate_user($VARS['username'], $VARS['password'], $errmsg)) {
insertAuthLog(12, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
$user = User::byUsername($VARS['username']);
if ($user->checkPassword($VARS['password'])) {
Log::insert(LogType::API_AUTH_OK, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "OK", "msg" => $Strings->get("login successful", false)]));
} else {
insertAuthLog(13, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
if (!is_empty($errmsg)) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->build("ldap error", ['error' => $errmsg], false)]));
}
if (user_exists($VARS['username'])) {
switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED":
Log::insert(LogType::API_AUTH_FAILED, $user->getUID(), "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
if ($user->exists()) {
switch ($user->getStatus()->get()) {
case AccountStatus::LOCKED_OR_DISABLED:
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account locked", false)]));
case "TERMINATED":
case AccountStatus::TERMINATED:
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account terminated", false)]));
case "CHANGE_PASSWORD":
case AccountStatus::CHANGE_PASSWORD:
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("password expired", false)]));
case "NORMAL":
case AccountStatus::NORMAL:
break;
default:
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account state error", false)]));
@ -72,165 +77,132 @@ switch ($VARS['action']) {
}
break;
case "userinfo":
if (!is_empty($VARS['username'])) {
if (user_exists_local($VARS['username'])) {
$data = $database->select("accounts", ["uid", "username", "realname (name)", "email", "phone" => ["phone1 (1)", "phone2 (2)"], 'pin'], ["username" => strtolower($VARS['username'])])[0];
$data['pin'] = (is_null($data['pin']) || $data['pin'] == "" ? false : true);
exit(json_encode(["status" => "OK", "data" => $data]));
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
} else if (!is_empty($VARS['uid'])) {
if ($database->has('accounts', ['uid' => $VARS['uid']])) {
$data = $database->select("accounts", ["uid", "username", "realname (name)", "email", "phone" => ["phone1 (1)", "phone2 (2)"], 'pin'], ["uid" => $VARS['uid']])[0];
$data['pin'] = (is_null($data['pin']) || $data['pin'] == "" ? false : true);
exit(json_encode(["status" => "OK", "data" => $data]));
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
if (!empty($VARS['username'])) {
$user = User::byUsername($VARS['username']);
} else if (!empty($VARS['uid']) && is_numeric($VARS['uid'])) {
$user = new User($VARS['uid']);
} else {
http_response_code(400);
die("\"400 Bad Request\"");
}
if ($user->exists()) {
$data = $database->get("accounts", ["uid", "username", "realname (name)", "email", "phone" => ["phone1 (1)", "phone2 (2)"], 'pin'], ["uid" => $user->getUID()]);
$data['pin'] = (is_null($data['pin']) || $data['pin'] == "" ? false : true);
exit(json_encode(["status" => "OK", "data" => $data]));
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
break;
case "userexists":
if (!is_empty($VARS['uid'])) {
if ($database->has('accounts', ['uid' => $VARS['uid']])) {
exit(json_encode(["status" => "OK", "exists" => true]));
} else {
exit(json_encode(["status" => "OK", "exists" => false]));
}
}
if (user_exists_local($VARS['username'])) {
exit(json_encode(["status" => "OK", "exists" => true]));
if (!empty($VARS['uid']) && is_numeric($VARS['uid'])) {
$user = new User($VARS['uid']);
} else if (!empty($VARS['username'])) {
$user = User::byUsername($VARS['username']);
} else {
exit(json_encode(["status" => "OK", "exists" => false]));
http_response_code(400);
die("\"400 Bad Request\"");
}
exit(json_encode(["status" => "OK", "exists" => $user->exists()]));
break;
case "hastotp":
if (userHasTOTP($VARS['username'])) {
exit(json_encode(["status" => "OK", "otp" => true]));
} else {
exit(json_encode(["status" => "OK", "otp" => false]));
}
exit(json_encode(["status" => "OK", "otp" => User::byUsername($VARS['username'])->has2fa()]));
break;
case "verifytotp":
if (verifyTOTP($VARS['username'], $VARS['code'])) {
$user = User::byUsername($VARS['username']);
if ($user->check2fa($VARS['code'])) {
exit(json_encode(["status" => "OK", "valid" => true]));
} else {
insertAuthLog(7, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_BAD_2FA, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("2fa incorrect", false), "valid" => false]));
}
break;
case "acctstatus":
exit(json_encode(["status" => "OK", "account" => get_account_status($VARS['username'])]));
exit(json_encode(["status" => "OK", "account" => User::byUsername($VARS['username'])->getStatus()->getString()]));
case "login":
engageRateLimit();
// simulate a login, checking account status and alerts
$errmsg = "";
if (authenticate_user($VARS['username'], $VARS['password'], $errmsg)) {
$uid = $database->select('accounts', 'uid', ['username' => strtolower($VARS['username'])])[0];
switch (get_account_status($VARS['username'])) {
engageRateLimit();
$user = User::byUsername($VARS['username']);
if ($user->checkPassword($VARS['password'])) {
switch ($user->getStatus()->getString()) {
case "LOCKED_OR_DISABLED":
insertAuthLog(5, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_LOGIN_FAILED, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account locked", false)]));
case "TERMINATED":
insertAuthLog(5, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_LOGIN_FAILED, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account terminated", false)]));
case "CHANGE_PASSWORD":
insertAuthLog(5, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_LOGIN_FAILED, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("password expired", false)]));
case "NORMAL":
insertAuthLog(4, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_LOGIN_OK, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "OK"]));
case "ALERT_ON_ACCESS":
sendLoginAlertEmail($VARS['username']);
insertAuthLog(4, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
$user->sendAlertEmail();
Log::insert(LogType::API_LOGIN_OK, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "OK", "alert" => true]));
default:
insertAuthLog(5, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
Log::insert(LogType::API_LOGIN_FAILED, $uid, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("account state error", false)]));
}
} else {
insertAuthLog(5, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
if (!is_empty($errmsg)) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->build("ldap error", ['error' => $errmsg], false)]));
}
Log::insert(LogType::API_LOGIN_FAILED, null, "Username: " . strtolower($VARS['username']) . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
break;
case "ismanagerof":
if ($VARS['uid'] == "1") {
if ($database->has("accounts", ['uid' => $VARS['manager']])) {
if ($database->has("accounts", ['uid' => $VARS['employee']])) {
$managerid = $VARS['manager'];
$employeeid = $VARS['employee'];
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => $VARS['employee']]));
}
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => $VARS['manager']]));
}
$manager = new User($VARS['manager']);
$employee = new User($VARS['employee']);
} else {
if (user_exists_local($VARS['manager'])) {
if (user_exists_local($VARS['employee'])) {
$managerid = $database->select('accounts', 'uid', ['username' => strtolower($VARS['manager'])]);
$employeeid = $database->select('accounts', 'uid', ['username' => strtolower($VARS['employee'])]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => strtolower($VARS['employee'])]));
}
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => strtolower($VARS['manager'])]));
}
$manager = User::byUsername($VARS['manager']);
$employee = User::byUsername($VARS['employee']);
}
if (!$manager->exists()) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => $VARS['manager']]));
}
if ($database->has('managers', ['AND' => ['managerid' => $managerid, 'employeeid' => $employeeid]])) {
if (!$employee->exists()) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false), "user" => $VARS['employee']]));
}
if ($database->has('managers', ['AND' => ['managerid' => $manager->getUID(), 'employeeid' => $employee->getUID()]])) {
exit(json_encode(["status" => "OK", "managerof" => true]));
} else {
exit(json_encode(["status" => "OK", "managerof" => false]));
}
break;
case "getmanaged":
if ($VARS['uid']) {
if ($database->has("accounts", ['uid' => $VARS['uid']])) {
$managerid = $VARS['uid'];
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
} else if ($VARS['username']) {
if ($database->has("accounts", ['username' => strtolower($VARS['username'])])) {
$managerid = $database->select('accounts', 'uid', ['username' => strtolower($VARS['username'])]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
if (!empty($VARS['uid'])) {
$manager = new User($VARS['uid']);
} else if (!empty($VARS['username'])) {
$manager = User::byUsername($VARS['username']);
} else {
http_response_code(400);
die("\"400 Bad Request\"");
}
if (!$manager->exists()) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
if ($VARS['get'] == "username") {
$managed = $database->select('managers', ['[>]accounts' => ['employeeid' => 'uid']], 'username', ['managerid' => $managerid]);
$managed = $database->select('managers', ['[>]accounts' => ['employeeid' => 'uid']], 'username', ['managerid' => $manager->getUID()]);
} else {
$managed = $database->select('managers', 'employeeid', ['managerid' => $managerid]);
$managed = $database->select('managers', 'employeeid', ['managerid' => $manager->getUID()]);
}
exit(json_encode(["status" => "OK", "employees" => $managed]));
break;
case "getmanagers":
if ($VARS['uid']) {
if ($database->has("accounts", ['uid' => $VARS['uid']])) {
$empid = $VARS['uid'];
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
} else if ($VARS['username']) {
if ($database->has("accounts", ['username' => strtolower($VARS['username'])])) {
$empid = $database->select('accounts', 'uid', ['username' => strtolower($VARS['username'])]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
if (!empty($VARS['uid'])) {
$emp = new User($VARS['uid']);
} else if (!empty($VARS['username'])) {
$emp = User::byUsername($VARS['username']);
} else {
http_response_code(400);
die("\"400 Bad Request\"");
}
$managers = $database->select('managers', 'managerid', ['employeeid' => $empid]);
if (!$emp->exists()) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
$managers = $database->select('managers', 'managerid', ['employeeid' => $emp->getUID()]);
exit(json_encode(["status" => "OK", "managers" => $managers]));
break;
case "usersearch":
@ -241,29 +213,23 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "OK", "result" => $data]));
break;
case "permission":
if (is_empty($VARS['code'])) {
if (empty($VARS['code'])) {
http_response_code(400);
die("\"400 Bad Request\"");
}
$perm = $VARS['code'];
if ($VARS['uid']) {
if ($database->has("accounts", ['uid' => $VARS['uid']])) {
$user = $database->select('accounts', ['username'], ['uid' => $VARS['uid']])[0]['username'];
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
} else if ($VARS['username']) {
if ($database->has("accounts", ['username' => strtolower($VARS['username'])])) {
$user = $VARS['username'];
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
if (!empty($VARS['uid'])) {
$user = new User($VARS['uid']);
} else if (!empty($VARS['username'])) {
$user = User::byUsername($VARS['username']);
} else {
http_response_code(400);
die("\"400 Bad Request\"");
}
$hasperm = account_has_permission($user, $perm);
exit(json_encode(["status" => "OK", "has_permission" => $hasperm]));
if (!$user->exists()) {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("user does not exist", false)]));
}
exit(json_encode(["status" => "OK", "has_permission" => $user->hasPermission($perm)]));
break;
case "mobileenabled":
exit(json_encode(["status" => "OK", "mobile" => MOBILE_ENABLED]));
@ -277,7 +243,7 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "OK", "valid" => $user_key_valid]));
case "alertemail":
engageRateLimit();
if (is_empty($VARS['username']) || !user_exists($VARS['username'])) {
if (is_empty($VARS['username']) || !User::byUsername($VARS['username'])->exists()) {
http_response_code(400);
die("\"400 Bad Request\"");
}
@ -285,7 +251,7 @@ switch ($VARS['action']) {
if (!is_empty($VARS['appname'])) {
$appname = $VARS['appname'];
}
$result = sendLoginAlertEmail($VARS['username'], $appname);
$result = User::byUsername($VARS['username'])->sendAlertEmail($appname);
if ($result === TRUE) {
exit(json_encode(["status" => "OK"]));
}
@ -371,22 +337,19 @@ switch ($VARS['action']) {
http_response_code(400);
die("\"400 Bad Request\"");
}
if (!is_empty($VARS['username'])) {
if (user_exists_local($VARS['username'])) {
$pin = $database->get("accounts", "pin", ["username" => strtolower($VARS['username'])]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
} else if (!is_empty($VARS['uid'])) {
if ($database->has('accounts', ['uid' => $VARS['uid']])) {
$pin = $database->get("accounts", "pin", ["uid" => strtolower($VARS['uid'])]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
if (!empty($VARS['username'])) {
$user = User::byUsername($VARS['username']);
} else if (!empty($VARS['uid'])) {
$user = new User($VARS['uid']);
} else {
http_response_code(400);
die("\"400 Bad Request\"");
}
if ($user->exists()) {
$pin = $database->get("accounts", "pin", ["uid" => $user->getUID()]);
} else {
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
}
if (is_null($pin) || $pin == "") {
exit(json_encode(["status" => "ERROR", "pinvalid" => false, "nopinset" => true]));
}
@ -395,8 +358,4 @@ switch ($VARS['action']) {
default:
http_response_code(404);
die(json_encode("404 Not Found: the requested action is not available."));
}
/* } catch (Exception $e) {
header("HTTP/1.1 500 Internal Server Error");
die("\"500 Internal Server Error\"");
} */
}

197
app.php

@ -0,0 +1,197 @@
<?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 ($_SESSION['loggedin'] != true) {
header('Location: index.php');
die("Session expired. Log in again to continue.");
}
require_once __DIR__ . "/pages.php";
$pageid = "home";
if (isset($_GET['page']) && !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";
}
}
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/app.css>; rel=preload; as=style", false);
header("Link: <static/css/fa-svg-with-js.css>; rel=preload; as=style", false);
header("Link: <static/js/fontawesome-all.min.js>; rel=preload; as=script", false);
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
header("Link: <static/js/bootstrap.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 SITE_TITLE; ?></title>
<link rel="icon" href="static/img/logo.svg">
<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/app.css" rel="stylesheet">
<link href="static/css/fa-svg-with-js.css" rel="stylesheet">
<script nonce="<?php echo $SECURE_NONCE; ?>">
FontAwesomeConfig = {autoAddCss: false}
</script>
<script src="static/js/fontawesome-all.min.js"></script>
<?php
// custom page styles
if (isset(PAGES[$pageid]['styles'])) {
foreach (PAGES[$pageid]['styles'] as $style) {
echo "<link href=\"$style\" rel=\"stylesheet\">\n";
header("Link: <$style>; rel=preload; as=style", false);
}
}
?>
</head>
<body>
<?php
// Alert messages
if (isset($_GET['msg']) && !is_empty($_GET['msg']) && array_key_exists($_GET['msg'], MESSAGES)) {
// optional string generation argument
if (!isset($_GET['arg']) || is_empty($_GET['arg'])) {
$alertmsg = $Strings->get(MESSAGES[$_GET['msg']]['string'], false);
} else {
$alertmsg = $Strings->build(MESSAGES[$_GET['msg']]['string'], ["arg" => strip_tags($_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 justify-content-center" id="msg-alert-box">
<div class="col-11 col-sm-6 col-md-5 col-lg-4 col-xl-4">
<div class="alert alert-dismissible alert-$alerttype mt-2 p-0 border-0 shadow">
<div class="p-2 pl-3">
<button type="button" class="close">&times;</button>
<i class="fas fa-$alerticon"></i> $alertmsg
</div>
<div class="progress">
<div class="progress-bar bg-$alerttype w-0" id="msg-alert-timeout-bar"></div>
</div>
</div>
</div>
</div>
END;
}
?>
<?php
// Adjust as needed
$navbar_breakpoint = "md";
// For mobile app
echo "<script nonce=\"$SECURE_NONCE\">var navbar_breakpoint = \"$navbar_breakpoint\";</script>"
?>
<nav class="navbar navbar-expand-<?php echo $navbar_breakpoint; ?> navbar-light bg-orange fixed-top">
<button class="navbar-toggler my-0 py-0" type="button" data-toggle="collapse" data-target="#navbar-collapse" aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle navigation">
<!--<i class="fas fa-bars"></i>-->
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand py-0 mr-auto" href="app.php">
<img src="static/img/logo.svg" alt="" class="d-none d-<?php echo $navbar_breakpoint; ?>-inline brand-img py-0" />
<?php echo SITE_TITLE; ?>
</a>
<div class="collapse navbar-collapse py-0" id="navbar-collapse">
<div class="navbar-nav mr-auto py-0">
<?php
$curpagefound = false;
foreach (PAGES as $id => $pg) {
if (isset($pg['navbar']) && $pg['navbar'] === TRUE) {
if ($pageid == $id) {
$curpagefound = true;
?>
<span class="nav-item py-<?php echo $navbar_breakpoint; ?>-0 active">
<?php
} else {
?>
<span class="nav-item py-<?php echo $navbar_breakpoint; ?>-0">
<?php
}
?>
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="app.php?page=<?php echo $id; ?>">
<?php
if (isset($pg['icon'])) {
?><i class="<?php echo $pg['icon']; ?> fa-fw"></i> <?php
}
$Strings->get($pg['title']);
?>
</a>
</span>
<?php
}
}
?>
</div>
<div class="navbar-nav ml-auto py-0" id="navbar-right">
<span class="nav-item py-<?php echo $navbar_breakpoint; ?>-0">
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="app.php">
<i class="fas fa-user fa-fw"></i><span>&nbsp;<?php echo $_SESSION['realname'] ?></span>
</a>
</span>
<span class="nav-item mr-auto py-<?php echo $navbar_breakpoint; ?>-0">
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="action.php?action=signout">
<i class="fas fa-sign-out-alt fa-fw"></i><span>&nbsp;<?php $Strings->get("sign out") ?></span>
</a>
</span>
</div>
</div>
</nav>
<div class="container" id="main-content">
<div>
<?php
include_once __DIR__ . '/pages/' . $pageid . ".php";
?>
</div>
<div class="footer">
<?php echo FOOTER_TEXT; ?><br />
Copyright &copy; <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
</div>
</div>
<script src="static/js/jquery-3.3.1.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<script src="static/js/app.js"></script>
<?php
// custom page scripts
if (isset(PAGES[$pageid]['scripts'])) {
foreach (PAGES[$pageid]['scripts'] as $script) {
echo "<script src=\"$script\"></script>\n";
header("Link: <$script>; rel=preload; as=script", false);
}
}
?>
</body>
</html>

13
apps/404_error.php

@ -1,13 +0,0 @@
<?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/. */
dieifnotloggedin();
$APPS["404_error"]["title"] = $Strings->get("404 error", false);
$APPS["404_error"]["icon"] = "times";
$APPS["404_error"]["type"] = "warning";
$APPS["404_error"]["content"] = "<h4>" . $Strings->get("page not found", false) . "</h4>";
?>

26
apps/change_password.php

@ -1,26 +0,0 @@
<?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/. */
dieifnotloggedin();
$oldpass = $Strings->get("current password", false);
$newpass = $Strings->get("new password", false);
$conpass = $Strings->get("confirm password", false);
$change = $Strings->get("change password", false);
$APPS["change_password"]["title"] = $Strings->get("change password", false);
$APPS["change_password"]["icon"] = "key";
$APPS["change_password"]["content"] = <<<CONTENTEND
<form action="action.php" method="POST">
<input type="password" class="form-control" name="oldpass" placeholder="$oldpass" />
<input type="password" class="form-control" name="newpass" placeholder="$newpass" />
<input type="password" class="form-control" name="conpass" placeholder="$conpass" />
<input type="hidden" name="action" value="chpasswd" />
<input type="hidden" name="source" value="security" />
<br />
<button type="submit" class="btn btn-success btn-sm btn-block">$change</button>
</form>
CONTENTEND;

27
apps/change_pin.php

@ -1,27 +0,0 @@
<?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/. */
dieifnotloggedin();
$newpin = $Strings->get("new pin", false);
$conpin = $Strings->get("confirm pin", false);
$change = $Strings->get("change pin", false);
$pinexp = $Strings->get("pin explanation", false);
$APPS["change_pin"]["title"] = $Strings->get("change pin", false);
$APPS["change_pin"]["icon"] = "th";
$APPS["change_pin"]["content"] = <<<CONTENTEND
<div class="alert alert-info"><i class="fa fa-info-circle"></i> $pinexp</div>
<form action="action.php" method="POST">
<input type="password" class="form-control" name="newpin" placeholder="$newpin" maxlength="8" pattern="[0-9]*" inputmode="numeric" />
<input type="password" class="form-control" name="conpin" placeholder="$conpin" maxlength="8" pattern="[0-9]*" inputmode="numeric" />
<input type="hidden" name="action" value="chpin" />
<input type="hidden" name="source" value="security" />
<br />
<button type="submit" class="btn btn-success btn-sm btn-block">$change</button>
</form>
CONTENTEND;

79
apps/setup_2fa.php

@ -1,79 +0,0 @@
<?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/. */
dieifnotloggedin();
use OTPHP\Factory;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
// extra login utils
require_once __DIR__ . "/../lib/login.php";
$APPS["setup_2fa"]["title"] = $Strings->get("setup 2fa", false);
$APPS["setup_2fa"]["icon"] = "lock";
if (userHasTOTP($_SESSION['username'])) {
$APPS["setup_2fa"]["content"] = '<div class="alert alert-info"><i class="fa fa-info-circle"></i> ' . $Strings->get("2fa active", false) . '</div>'
. '<a href="action.php?action=rm2fa&source=security" class="btn btn-warning btn-sm btn-block">'
. $Strings->get("remove 2fa", false) . '</a>';
} else if ($_GET['2fa'] == "generate") {
$codeuri = newTOTP($_SESSION['username']);
$userdata = $database->select('accounts', ['email', 'authsecret', 'realname'], ['username' => $_SESSION['username']])[0];
$label = SYSTEM_NAME . ":" . is_null($userdata['email']) ? $userdata['realname'] : $userdata['email'];
$issuer = SYSTEM_NAME;
$qrCode = new QrCode($codeuri);
$qrCode->setWriterByName('svg');
$qrCode->setSize(550);
$qrCode->setErrorCorrectionLevel(ErrorCorrectionLevel::HIGH);
$qrcode = $qrCode->writeDataUri();
$totp = Factory::loadFromProvisioningUri($codeuri);
$codesecret = $totp->getSecret();
$chunk_secret = trim(chunk_split($codesecret, 4, ' '));
$lang_manualsetup = $Strings->get("manual setup", false);
$lang_secretkey = $Strings->get("secret key", false);
$lang_label = $Strings->get("label", false);
$lang_issuer = $Strings->get("issuer", false);
$lang_entercode = $Strings->get("enter otp code", false);
$APPS["setup_2fa"]["content"] = '<div class="alert alert-info"><i class="fa fa-info-circle"></i> ' . $Strings->get("scan 2fa qrcode", false) . '</div>' . <<<END
<style nonce="$SECURE_NONCE">
.margintop-15px {
margin-top: 15px;
}
.mono-chunk {
text-align: center;
font-size: 110%;
font-family: monospace;
}
</style>
<img src="$qrcode" class="img-responsive qrcode" />
<form action="action.php" method="POST" class="margintop-15px">
<input type="text" name="totpcode" class="form-control" placeholder="$lang_entercode" minlength=6 maxlength=6 required />
<br />
<input type="hidden" name="action" value="add2fa" />
<input type="hidden" name="source" value="security" />
<input type="hidden" name="secret" value="$codesecret" />
<button type="submit" class="btn btn-success btn-sm btn-block">
END
. $Strings->get("confirm 2fa", false) . <<<END
</button>
</form>
<div class="panel panel-default margintop-15px">
<div class="panel-body">
<b>$lang_manualsetup</b>
<br /><label>$lang_secretkey:</label>
<div class="well well-sm mono-chunk">$chunk_secret</div>
<br /><label>$lang_label:</label>
<div class="well well-sm mono-chunk">$label</div>
<br /><label>$lang_issuer:</label>
<div class="well well-sm mono-chunk">$issuer</div>
</div>
</div>
END;
} else {
$APPS["setup_2fa"]["content"] = '<div class="alert alert-info"><i class="fa fa-info-circle"></i> ' . $Strings->get("2fa explained", false) . '</div>'
. '<a class="btn btn-success btn-sm btn-block" href="home.php?page=security&2fa=generate">'
. $Strings->get("enable 2fa", false) . '</a>';
}

BIN
database.mwb

0
database_upgrade/1.0.1_1.1.sql → database_upgrade/1.0.1_2.0.sql

270
home.php

@ -3,272 +3,4 @@
* 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 ($_SESSION['loggedin'] != true) {
header('Location: index.php');
die("Session expired. Log in again to continue.");
} else if (is_empty($_SESSION['password'])) {
header('Location: index.php');
die("You need to log in again.");
}
require_once __DIR__ . "/pages.php";
$pageid = "home";
if (!is_empty($_GET['page'])) {
if (array_key_exists($_GET['page'], PAGES)) {
$pageid = $_GET['page'];
} else {
$pageid = "404";
}
}
?>
<!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 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/material-color/material-color.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">
<?php
if ((SHOW_ICON == "both" || SHOW_ICON == "app") && ICON_POSITION != "menu") {
if (MENU_BAR_STYLE != "fixed") {
?>
<img class="img-responsive banner-image" src="static/img/logo.svg" />
<?php
}
}
?>
</div>
</div>
<nav class="navbar navbar-default navbar-orange navbar-<?php echo MENU_BAR_STYLE; ?>-top">
<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>
<?php
if (SHOW_ICON == "both" || SHOW_ICON == "app") {
if (MENU_BAR_STYLE == "fixed" || ICON_POSITION == "menu") {
$src = "static/img/logo.svg";
if ($pageid != "home") {
$src = "static/img/up-arrow-black.png";
}
?>
<a class="navbar-brand" href="home.php">
<img src="<?php echo $src; ?>" />
</a>
<?php
}
}
?>
<a class="navbar-brand" href="home.php">
<?php
echo SITE_TITLE;
?>
</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
<?php
$counter = 0;
$more = "";
$curpagefound = false;
foreach (PAGES as $id => $pg) {
if ($pg['navbar'] === TRUE) {
$counter++;
if ($counter > ($curpagefound ? 4 : 3) && $pageid != $id) {
$item = '<a href="home.php?page=' . $id . '">';
if (isset($pg['icon'])) {
$item .= '<i class="fa fa-' . $pg['icon'] . ' fa-fw"></i>';
}
$item .= $Strings->get($pg['title'], false) . '</a>';
echo '<li class="hidden-sm hidden-md">' . $item . "</li>";
$more .= '<li>' . $item . "</li>";
} else {
if ($pageid == $id) {
$curpagefound = true;
?>
<li class="active">
<?php
} else {
?>
<li>
<?php } ?>
<a href="home.php?page=<?php echo $id; ?>">
<?php
if (isset($pg['icon'])) {
?>
<i class="fa fa-<?php echo $pg['icon']; ?> fa-fw"></i>
<?php } ?>
<?php $Strings->get($pg['title']) ?>
</a>
</li>
<?php
}
}
}
if ($counter > 4) {
?>
<li class="dropdown hidden-lg hidden-xs">
<a href="" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-ellipsis-v fa-fw"></i> <?php $Strings->get("more"); ?></a>
<ul class="dropdown-menu"><?php echo $more; ?></ul>
</li>
<?php } ?>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="home.php"><i class="fa fa-user fa-fw"></i> <span class=""><?php echo $_SESSION['realname'] ?></span></a></li>
<li><a href="action.php?action=signout"><i class="fa fa-sign-out fa-fw"></i> <span class=""><?php $Strings->get("sign out") ?></span></a></li>
</ul>
</div>
</nav>
<?php
if (MENU_BAR_STYLE == "fixed") {
?>
<div class="pad-75px"></div>
<?php
}
?>
<div class="app-dock-container mobile-app-hide">
<div class="app-dock">
<?php
foreach (EXTERNAL_APPS as $a) {
?>
<div class="app-dock-item">
<p>
<a href="<?php echo $a['url']; ?>">
<img class="img-responsive app-icon" src="<?php
if (strpos($a['icon'], "http") !== 0) {
echo $a['url'] . $a['icon'];
} else {
echo $a['icon'];
}
?>"/>
<span><?php echo $a['title']; ?></span>
</a>
</p>
</div>
<?php
}
?>
</div>
</div>
<?php
// Alert messages
if (!is_empty($_GET['msg']) && array_key_exists($_GET['msg'], MESSAGES)) {
// optional string generation argument
if (is_empty($_GET['arg'])) {
$alertmsg = $Strings->get(MESSAGES[$_GET['msg']]['string'], false);
} else {
$alertmsg = $Strings->build(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 class="row widget-box">
<?php
// Center the widgets horizontally on the screen
$appcount = 0;
foreach (APPS[$pageid] as $app) {
if (file_exists(__DIR__ . "/apps/" . $app . ".php")) {
include_once __DIR__ . "/apps/" . $app . ".php";
if (isset($APPS[$app])) {
$appcount++;
}
}
}
if ($appcount == 1) {
?>
<div class="hidden-xs col-sm-3 col-md-4 col-lg-4">
<!-- Empty placeholder column for nice center-align -->
</div>
<?php
} else if ($appcount == 2) {
?>
<div class="hidden-xs hidden-sm col-md-2 col-lg-2">
<!-- Empty placeholder column for nice center-align -->
</div>
<?php
}
// Load app widgets
foreach (APPS[$pageid] as $app) {
if (file_exists(__DIR__ . "/apps/" . $app . ".php")) {
include_once __DIR__ . "/apps/" . $app . ".php";
if (!isset($APPS[$app])) {
continue;
}
$apptitle = ($APPS[$app]['i18n'] === TRUE ? $Strings->get($APPS[$app]['title'], false) : $APPS[$app]['title']);
$appicon = (is_empty($APPS[$app]['icon']) ? "" : "fa fa-fw fa-" . $APPS[$app]['icon']);
$apptype = (is_empty($APPS[$app]['type']) ? "default" : $APPS[$app]['type']);
$appcontent = $APPS[$app]['content'];
echo <<<END
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
<div class="panel panel-$apptype apppanel">
<div class="panel-heading">
<h3 class="panel-title"><i class="$appicon"></i> $apptitle </h3>
</div>
<div class="panel-body">
$appcontent
</div>
</div>
</div>
END;
}
}
?>
</div>
<div class="footer">
<?php echo FOOTER_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>
header("Location: app.php");

271
index.php

@ -5,28 +5,26 @@
require_once __DIR__ . "/required.php";
require_once __DIR__ . "/lib/login.php";
// If we're logged in, we don't need to be here.
if ($_SESSION['loggedin'] && !is_empty($_SESSION['password'])) {
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.
} else if ($_SESSION['loggedin'] && is_empty($_SESSION['password'])) {
$alert = $Strings->get("sign in again", false);
$alerttype = "info";
}
/* Authenticate user */
$username_ok = false;
$multiauth = false;
$change_password = false;
if ($VARS['progress'] == "1") {
if (empty($VARS['progress'])) {
// Easy way to remove "undefined" warnings.
} else if ($VARS['progress'] == "1") {
engageRateLimit();
if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && verifyCaptcheck($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
$autherror = "";
if (user_exists($VARS['username'])) {
$status = get_account_status($VARS['username'], $error);
$user = User::byUsername($VARS['username']);
if ($user->exists()) {
$status = $user->getStatus()->getString();
switch ($status) {
case "LOCKED_OR_DISABLED":
$alert = $Strings->get("account locked", false);
@ -37,15 +35,15 @@ if ($VARS['progress'] == "1") {
case "CHANGE_PASSWORD":
$alert = $Strings->get("password expired", false);
$alerttype = "info";
$_SESSION['username'] = strtolower($VARS['username']);
$_SESSION['uid'] = $database->get('accounts', 'uid', ['username' => strtolower($VARS['username'])]);
$_SESSION['username'] = $user->getUsername();
$_SESSION['uid'] = $user->getUID();
$change_password = true;
break;
case "NORMAL":
$username_ok = true;
break;
case "ALERT_ON_ACCESS":
$mail_resp = sendLoginAlertEmail($VARS['username']);
$mail_resp = $user->sendAlertEmail();
if (DEBUG) {
var_dump($mail_resp);
}
@ -60,73 +58,69 @@ if ($VARS['progress'] == "1") {
break;
}
if ($username_ok) {
if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) {
if ($user->checkPassword($VARS['password'])) {
$_SESSION['passok'] = true; // stop logins using only username and authcode
if (userHasTOTP($VARS['username'])) {
if ($user->has2fa()) {
$multiauth = true;
$_SESSION['password'] = $VARS['password'];
} else {
doLoginUser($VARS['username'], $VARS['password']);
insertAuthLog(1, $_SESSION['uid']);
header('Location: home.php');
die("Logged in, go to home.php");
Session::start($user);
Log::insert(LogType::LOGIN_OK, $user->getUID());
header('Location: app.php');
die("Logged in, go to app.php");
}
} else {
if (!is_empty($autherror)) {
$alert = $autherror;
insertAuthLog(2, null, "Username: " . $VARS['username']);
} else {
$alert = $Strings->get("login incorrect", false);
insertAuthLog(2, null, "Username: " . $VARS['username']);
}
$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);
insertAuthLog(2, null, "Username: " . $VARS['username']);
Log::insert(LogType::LOGIN_FAILED, null, "Username: " . $VARS['username']);
}
} else {
$alert = $Strings->get("captcha error", false);
insertAuthLog(8, null, "Username: " . $VARS['username']);
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 (verifyTOTP($VARS['username'], $VARS['authcode'])) {
doLoginUser($VARS['username'], $VARS['password']);
insertAuthLog(1, $_SESSION['uid']);
header('Location: home.php');
die("Logged in, go to home.php");
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);
insertAuthLog(6, null, "Username: " . $VARS['username']);
Log::insert(LogType::BAD_2FA, null, "Username: " . $VARS['username']);
}
} else if ($VARS['progress'] == "chpasswd") {
engageRateLimit();
if (!is_empty($_SESSION['username'])) {
$error = [];
$result = change_password($VARS['oldpass'], $VARS['newpass'], $VARS['conpass'], $error);
if ($result === TRUE) {
$alert = $Strings->get(MESSAGES["password_updated"]["string"], false);
$alerttype = MESSAGES["password_updated"]["type"];
}
switch (count($error)) {
case 0:
break;
case 1:
$alert = $Strings->get(MESSAGES[$error[0]]["string"], false);
$alerttype = MESSAGES[$error[0]]["type"];
break;
case 2:
$alert = $Strings->build(MESSAGES[$error[0]]["string"], ["arg" => $error[1]], false);
$alerttype = MESSAGES[$error[0]]["type"];
break;
default:
$alert = $Strings->get(MESSAGES["generic_op_error"]["string"], false);
$alerttype = MESSAGES["generic_op_error"]["type"];
$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();
@ -134,6 +128,13 @@ if ($VARS['progress'] == "1") {
die();
}
}
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.min.js>; rel=preload; as=script", false);
?>
<!DOCTYPE html>
<html>