Merge pull request #27 from cp6/dev

Dev
This commit is contained in:
corbpie 2022-02-19 00:01:17 +11:00 committed by GitHub
commit 955e3de89f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 402 additions and 96 deletions

4
AUTH.php Normal file
View File

@ -0,0 +1,4 @@
<?php
require_once('class.php');
$create_account = new auth();
$create_account->selfDestruct();

View File

@ -11,7 +11,8 @@ V2 build is going to be a complete overhaul of My idlers with the project being
Despite what the name infers this self hosted web app isn't just for storing idling server information.
By using a [YABs](https://github.com/masonr/yet-another-bench-script) output you can get disk & network speed values along with GeekBench 5 scores to do easier comparing and sorting.
[![Generic badge](https://img.shields.io/badge/version-1.4-blue.svg)](https://shields.io/)
[![Generic badge](https://img.shields.io/badge/version-1.5-blue.svg)](https://shields.io/)
## 1.4 changes:
**If you have version 1.3 already installed please run ```update1.3to1.4.sql```**

View File

@ -2,9 +2,11 @@
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
require_once('class.php');
$idle = new idlers();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$auth = new auth();
$auth->sessionStartIfNone();
if (isset($_SESSION['token'])) {
$idle = new idlers();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_GET['type'])) {
if ($_GET['type'] == 'server') {
echo $idle->serverData($_GET['id']);
@ -52,7 +54,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
echo $idle->compareTable($_GET['server1'], $_GET['server2']);
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['order_form'])) {
header('Content-Type: text/html; charset=utf-8');
echo $idle->orderTable($_POST['order_by']);//Returns order table
@ -97,4 +99,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
header('Location:index.php');
die();
}
}
} else {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(array(
'code' => 401,
'message' => 'Unauthorized'));
exit;
}

261
class.php
View File

@ -23,6 +23,11 @@ class idlersConfig
const SAVE_YABS_OUTPUT = true;//true or false
const GET_ASN_INFO = true;//Get ANS name and number
//Failed attempts before ip locked from attempting login
const FAIL_ATTEMPTS_ALLOWED = 4;
//Minutes to lock ip for
const IP_LOCK_MINUTES = 10;
}
class elementHelpers extends idlersConfig
@ -1403,7 +1408,7 @@ class idlers extends helperFunctions
$this->colOpen('col-12 col-md-6 mm-col');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Bandwidth');
$this->numberInput('me_bandwidth', '', 'form-control', false, 1, 9999);
$this->numberInput('me_bandwidth', '', 'form-control', false, 0, 9999);
$this->outputString('<div class="input-group-append"><span class="input-group-text">TB</span></div>');
$this->tagClose('div', 3);
@ -1431,7 +1436,7 @@ class idlers extends helperFunctions
$this->rowColOpen('form-row', 'col-6');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Swap');
$this->numberInput('me_swap', '', 'form-control', false, 0.5, 9999, 'any');
$this->numberInput('me_swap', '', 'form-control', false, 0, 9999, 'any');
$this->tagClose('div', 2);
$this->colOpen('col-6');
$this->tagOpen('div', 'input-group');
@ -1541,7 +1546,7 @@ class idlers extends helperFunctions
$this->rowColOpen('form-row', 'col-12');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Bandwidth');
$this->numberInput('sh_me_bandwidth', '', 'form-control', false, 1, 99999);
$this->numberInput('sh_me_bandwidth', '', 'form-control', false, 0, 99999);
$this->outputString('<div class="input-group-append"><span class="input-group-text">GB</span></div>');
$this->tagClose('div', 3);
@ -1928,7 +1933,7 @@ class idlers extends helperFunctions
$this->colOpen('col-12 col-md-3');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Swap');
$this->numberInput('swap', '100', 'form-control', true, 1, 62000, 'any');
$this->numberInput('swap', '100', 'form-control', true, 0, 62000, 'any');
$this->tagClose('div', 2);
$this->colOpen('col-12 col-md-3');
$this->tagOpen('div', 'input-group');
@ -3847,3 +3852,251 @@ class itemUpdate extends idlers
}
}
class auth extends idlers
{
public string $token;
private string $user;
private string $ip_address;
public function selfDestruct()
{
$select = $this->dbConnect()->prepare("SELECT `user` FROM `auth`;");
$select->execute();
$user_count = $select->rowCount();
if ($user_count == 0) {
//Begin create user
$this->createAccountForm();
} else {
//User already exists
//Delete AUTH.php
$this->deleteAuthFile();
}
}
protected function createAccountForm()
{
if (isset($_POST['pass']) && isset($_POST['user'])) {
//Form submitted
$this->insertAccount($_POST['user'], $_POST['pass']);
} else {
$this->pageHead();
$this->rowColOpen('row text-center', 'col-12');
$this->tagOpen('div', 'card');
$this->tagOpen('div', 'card-header');
$this->HTMLphrase('h1', '', 'Create account');
$this->HTMLphrase('p', '', 'If you are seeing this there are currently 0 accounts. Once 1 is created this file gets deleted.');
$this->tagClose('div');
$this->tagOpen('div', 'card-body');
$this->outputString('<form method="post">');
$this->rowColOpen('form-row', 'col-12 col-md-6 mm-col');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Username');
$this->textInput('user', '', 'form-control', true, 4, 64);
$this->tagClose('div', 2);
$this->colOpen('col-12 col-md-6 mm-col');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Password');
$this->outputString("<input type='password' id='pass' name='pass' class='form-control' min-length='12' max-length='124' required>");
$this->tagClose('div', 3);
$this->rowColOpen('form-row text-center', 'col-12');
$this->submitInput('Create', 'submitInput', 'btn btn-main');
$this->tagClose('div', 2);
$this->tagClose('form');
$this->tagClose('div', 4);
$this->pageFooter();
}
}
protected function insertAccount(string $user, string $pass): bool
{
$hashed_password = password_hash($pass, PASSWORD_DEFAULT);//Hash the submitted password
$insert = $this->dbConnect()->prepare("INSERT INTO `auth` (`user`, `pass`) VALUES (?,?)");
return $insert->execute([$user, $hashed_password]);
}
public function sessionStartIfNone()
{
if (session_status() == PHP_SESSION_NONE) {
session_start();//No session stated... so start one
}
}
public function isLoggedIn(): bool
{
$this->sessionStartIfNone();//Start session if none already started
if (isset($_SESSION['token']) && !empty($_SESSION['token'])) {
$this->token = $_SESSION['token'];
return true;//Logged in
} else {
return false;
}
}
public function loginForm()
{
if (isset($_POST['user']) && isset($_POST['pass'])) {
$this->attemptLogin($_POST['user'], $_POST['pass']);
} else {
$this->pageHead();
$this->rowColOpen('row text-center', 'col-12');
$this->tagOpen('div', 'card');
$this->tagOpen('div', 'card-header');
$this->HTMLphrase('h3', '', 'My idlers login');
$this->tagClose('div');
$this->tagOpen('div', 'card-body');
$this->outputString('<form method="post">');
$this->rowColOpen('form-row', 'col-12 col-md-3');
$this->tagClose('div', 1);
$this->colOpen('col-12 col-md-6 mm-col');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Username');
$this->textInput('user', '', 'form-control', true, 4, 64);
$this->tagClose('div', 2);
$this->colOpen('col-12 col-md-3');
$this->tagClose('div', 2);
$this->rowColOpen('form-row', 'col-12 col-md-3');
$this->tagClose('div', 1);
$this->colOpen('col-12 col-md-6 mm-col');
$this->tagOpen('div', 'input-group');
$this->inputPrepend('Password');
$this->outputString("<input type='password' id='pass' name='pass' class='form-control' min-length='12' max-length='124' required>");
$this->tagClose('div', 2);
$this->colOpen('col-12 col-md-3');
$this->tagClose('div', 2);
$this->rowColOpen('form-row text-center', 'col-12');
$this->submitInput('Login', 'submitInput', 'btn btn-main');
$this->tagClose('div', 2);
$this->tagClose('form');
$this->tagClose('div', 4);
$this->pageFooter();
}
}
protected function usernameExists(string $username): bool
{
$select = $this->dbConnect()->prepare("SELECT `user` FROM `auth` WHERE `user` = ? LIMIT 1;");
$select->execute([$username]);
$row = $select->fetch(PDO::FETCH_ASSOC);
if (!empty($row)) {//Row found
$this->user = $row['user'];
return true;
} else {//NO row found
return false;
}
}
protected function checkPasswordCorrect(string $password): bool
{
$select = $this->dbConnect()->prepare("SELECT `pass` FROM `auth` WHERE `user` = ? LIMIT 1;");
$select->execute([$this->user]);
$row = $select->fetch(PDO::FETCH_ASSOC);
if (password_verify($password, $row['pass'])) {
return true;//Password is correct
} else {
return false;//Bad password
}
}
protected function doLoginWasSuccess(): bool
{
$update = $this->dbConnect()->prepare("UPDATE `auth` SET `login_count` = (login_count + 1), `last_login` = NOW() WHERE `user` = ? LIMIT 1;");
return $update->execute([$this->user]);
}
protected function addLoginFailCount(): bool
{
$update = $this->dbConnect()->prepare("UPDATE `auth` SET login_fails = (login_fails + 1), `last_fail` = NOW() WHERE `user` = ? LIMIT 1;");
return $update->execute([$this->user]);
}
protected function addLoginFailAttempt(): bool
{
$insert = $this->dbConnect()->prepare('INSERT IGNORE INTO `login_attempts` (`user`, `ip`) VALUES (?, ?)');
return $insert->execute([$this->user, $this->ip_address]);
}
protected function setToken(int $length = 32)
{
$this->sessionStartIfNone();
$_SESSION['token'] = $this->genID($length);//Set session as token
$this->token = $_SESSION['token'];
$update_token = $this->dbConnect()->prepare("UPDATE `auth` SET `token` = ? WHERE `user` = ? LIMIT 1;");
$update_token->execute([$_SESSION['token'], $this->user]);
}
protected function indexRedirect()
{
header("Location: index.php");
exit;
}
protected function getRecentFailCount(): int
{
$select = $this->dbConnect()->prepare("SELECT COUNT(*) as the_count FROM `login_attempts` WHERE `ip` = ? AND `datetime` > (NOW() - INTERVAL 10 MINUTE);");
$select->execute([$this->ip_address]);
return $select->fetch()['the_count'];//login fails for IP in last 10 minutes
}
protected function hasLockTimePassed(): bool
{
$select = $this->dbConnect()->prepare("SELECT `datetime` FROM `login_attempts` WHERE `ip` = ? ORDER BY `datetime` DESC LIMIT 1;");
$select->execute([$this->ip_address]);
$locked_until = $select->fetch(PDO::FETCH_ASSOC);
$time = new DateTime($locked_until['datetime']);
$time->add(new DateInterval("PT" . self::IP_LOCK_MINUTES . "M"));
$locked_until_formatted = $time->format('Y-m-d H:i:s');
$now = date('Y-m-d H:i:s');
if ($now > $locked_until_formatted) {//Time has passed
return true;
} else {
return false;
}
}
public function attemptLogin(string $username, string $password)
{
$this->ip_address = $_SERVER['REMOTE_ADDR'];
if ($this->getRecentFailCount() >= self::FAIL_ATTEMPTS_ALLOWED) {
if (!$this->hasLockTimePassed()) {//IP is currently not allowed to attempt login
$this->indexRedirect();//Redirect to index and show login form
} else {//IP has passed lock time
if ($this->usernameExists($username)) {//Username found
if ($this->checkPasswordCorrect($password)) {//Password is correct
$this->doLoginWasSuccess();//Add login counter and last login datetime
$this->setToken();//Set session token
$this->indexRedirect();//Redirect to index and show servers
} else {//Password is wrong
$this->addLoginFailCount();
$this->addLoginFailAttempt();
$this->indexRedirect();//Redirect to index and login form
}
} else {//Username not found in DB
$this->indexRedirect();//Redirect to index and show login form
}
}
} else {//A clean attempt
echo $this->getRecentFailCount();
if ($this->usernameExists($username)) {//Username found
if ($this->checkPasswordCorrect($password)) {//Password is correct
$this->doLoginWasSuccess();//Add login counter and last login datetime
$this->setToken();//Set session token
$this->indexRedirect();//Redirect to index and show servers
} else {//Password is wrong
$this->addLoginFailCount();//Add 1 onto login fail count
$this->addLoginFailAttempt();//ip and datetime into login attempt fail logs
$this->indexRedirect();//Redirect to index and show login form
}
} else {//Username not found in DB
$this->indexRedirect();//Redirect to index and show login form
}
}
}
protected function deleteAuthFile()
{
unlink('AUTH.php');
}
}

View File

@ -394,6 +394,27 @@ CREATE TABLE IF NOT EXISTS `speed_tests`
/*!40000 ALTER TABLE `speed_tests`
ENABLE KEYS */;
CREATE TABLE IF NOT EXISTS `auth`
(
`user` varchar(64) NOT NULL,
`pass` varchar(255) DEFAULT NULL,
`token` char(32) DEFAULT NULL,
`login_count` int(11) DEFAULT 0,
`login_fails` int(11) DEFAULT 0,
`last_login` datetime DEFAULT NULL,
`last_fail` datetime DEFAULT NULL,
PRIMARY KEY (`user`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `login_attempts`
(
`user` varchar(124) DEFAULT NULL,
`ip` varchar(124) DEFAULT NULL,
`datetime` datetime DEFAULT current_timestamp()
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
/*!40101 SET SQL_MODE = IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS = IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT = @OLD_CHARACTER_SET_CLIENT */;

View File

@ -1,2 +0,0 @@
ALTER TABLE `servers`
ADD COLUMN `asn` VARCHAR(124) NULL DEFAULT NULL AFTER `notes`;

20
update1.4to1.5.sql Normal file
View File

@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS `auth`
(
`user` varchar(64) NOT NULL,
`pass` varchar(255) DEFAULT NULL,
`token` char(32) DEFAULT NULL,
`login_count` int(11) DEFAULT 0,
`login_fails` int(11) DEFAULT 0,
`last_login` datetime DEFAULT NULL,
`last_fail` datetime DEFAULT NULL,
PRIMARY KEY (`user`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `login_attempts`
(
`user` varchar(124) DEFAULT NULL,
`ip` varchar(124) DEFAULT NULL,
`datetime` datetime DEFAULT current_timestamp()
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;