summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalf Furious <m@lfurio.us>2016-02-07 14:01:22 -0500
committerMalf Furious <m@lfurio.us>2016-02-07 14:01:22 -0500
commit72e37180b69db2c067b926b54c64e1989bef0be4 (patch)
treed5e76f4894d3822f1923464c641c63fa283bdd2b
parent43ca317ea7d49396eb2958a38aed8b737ef4186b (diff)
parentc5564a0a2ae183c533a38905eccdbf383030cd4c (diff)
downloadscrott-72e37180b69db2c067b926b54c64e1989bef0be4.tar.gz
scrott-72e37180b69db2c067b926b54c64e1989bef0be4.zip
Merge branch 'auth' into dev
-rw-r--r--app/class/framework.class.php41
-rw-r--r--app/class/object.class.php25
-rw-r--r--app/class/setting.class.php30
-rw-r--r--app/class/user.class.php95
-rw-r--r--app/controller/auth.control.php53
-rw-r--r--app/controller/root.control.php15
-rw-r--r--app/model/auth.mod.php101
-rw-r--r--app/model/common.mod.php9
-rw-r--r--app/view/auth/default.view.php113
-rw-r--r--app/view/common/foot.view.php1
-rw-r--r--app/view/common/head.view.php8
-rw-r--r--app/view/common/topp.view.php21
-rw-r--r--examples/example.html22
-rw-r--r--schema.sql4
14 files changed, 512 insertions, 26 deletions
diff --git a/app/class/framework.class.php b/app/class/framework.class.php
index d1293de..74c4b14 100644
--- a/app/class/framework.class.php
+++ b/app/class/framework.class.php
@@ -4,7 +4,11 @@
is_file("scrott.conf.php") &&
require_once "scrott.conf.php";
+/* Init PHP session */
+session_start();
+
require_once "class/mysql.class.php";
+require_once "class/user.class.php";
/*
* Global functions / operations and access to contextual or session-based information
@@ -48,6 +52,43 @@ abstract class Framework
}
/*
+ * Get a user object for the currently logged in user. Returns false if session is logged out.
+ */
+ function getCurrentUser()
+ {
+ if (isset($_SESSION['userguid']))
+ return new User($_SESSION['userguid']);
+
+ return false;
+ }
+
+ /*
+ * Get the IP address the client held when the current session began
+ */
+ function getOriginIP()
+ {
+ return $_SESSION['userip'];
+ }
+
+ /*
+ * Set the current logged in user
+ */
+ function setCurrentUser($user = null)
+ {
+ if ($user != null && isset($user->guid))
+ {
+ $_SESSION['userguid'] = $user->guid;
+ $_SESSION['userip'] = $_SERVER['REMOTE_ADDR'];
+ }
+
+ else
+ {
+ unset($_SESSION['userguid']);
+ unset($_SESSION['userip']);
+ }
+ }
+
+ /*
* Get or create the app's database connection object (this is a singleton object and dependent on system-level config)
*/
static function getDbConnection()
diff --git a/app/class/object.class.php b/app/class/object.class.php
index bcd8dfa..96cc810 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -77,6 +77,8 @@ abstract class Object extends Framework
{
if (isset($this->guid))
{
+ $this->timeUpdated = $this->getCurrentTimestamp();
+
/* Update Base */
$updateStr = "";
@@ -117,6 +119,8 @@ abstract class Object extends Framework
else
{
$this->guid = $this->getNewGUID();
+ $this->timeCreated = $this->getCurrentTimestamp();
+ $this->timeUpdated = $this->timeCreated;
/* Insert Base */
$colsStr = "";
@@ -180,6 +184,16 @@ abstract class Object extends Framework
}
/*
+ * Get current timestamp for object database purposes
+ */
+ function getCurrentTimestamp()
+ {
+ $query = "SELECT now() AS stamp";
+ $result = $this->db->query($query);
+ return $result[0]['stamp'];
+ }
+
+ /*
* Check whether given GUID exists
*/
function isGUID($guid)
@@ -200,13 +214,20 @@ abstract class Object extends Framework
{
do
{
- $sha = hash("sha256", rand());
- $guid = substr($sha, 0, 8);
+ $guid = substr($this->getBlob(), 0, 8);
}
while ($this->isGUID($guid));
return $guid;
}
+
+ /*
+ * Get a random sha256 blob
+ */
+ function getBlob()
+ {
+ return hash("sha256", openssl_random_pseudo_bytes(64));
+ }
}
/*
diff --git a/app/class/setting.class.php b/app/class/setting.class.php
index ea5fac3..e3ef7f1 100644
--- a/app/class/setting.class.php
+++ b/app/class/setting.class.php
@@ -23,6 +23,36 @@ class Setting extends Framework
return $res[0]['value'];
}
+
+ /*
+ * Helper function for setting setting values on the database
+ */
+ static function setValue($key, $value)
+ {
+ $db = parent::getDbConnection();
+ $escdKey = $db->esc($key);
+ $escdValue = $db->esc($value);
+
+ if (self::getValue($key) === false)
+ $query = "INSERT INTO setting (`key`, value) VALUES('" . $escdKey . "', '" . $escdValue . "')";
+ else
+ $query = "UPDATE setting SET value = '" . $escdValue . "' WHERE `key` = '" . $escdKey . "'";
+
+ $db->query($query);
+ }
+
+ /*
+ * Should the app allow the public to signup their own accounts with Scrott?
+ */
+ static function allowPublicSignup($value = null)
+ {
+ $opt = "allowPublicSignup";
+
+ if ($value != null)
+ self::setValue($opt, $value);
+
+ return self::getValue($opt);
+ }
}
?>
diff --git a/app/class/user.class.php b/app/class/user.class.php
index 8ef91ae..bd2e174 100644
--- a/app/class/user.class.php
+++ b/app/class/user.class.php
@@ -17,6 +17,7 @@ class User extends Object
"key",
"salt",
"alias",
+ "admin",
"email",
"emailConf",
"emailConfKey"
@@ -25,6 +26,100 @@ class User extends Object
parent::__construct("user", $cols);
$this->loadObj($guid);
}
+
+ /*
+ * Initialize object by username
+ */
+ function initByUsername($username)
+ {
+ $query = "SELECT guid FROM object WHERE type = 'user' AND name = '" . $this->db->esc($username) . "'";
+ $result = $this->db->query($query);
+
+ if (count($result) == 0)
+ return false;
+
+ $this->loadObj($result[0]['guid']);
+ return true;
+ }
+
+ /*
+ * Get all users -- ordered by name, ascending
+ */
+ function getAllUsers_orderByName()
+ {
+ $query = "SELECT guid FROM `object` WHERE `type` = 'user' ORDER BY name";
+ $result = $this->db->query($query);
+
+ $users = array();
+
+ foreach ($result as $u)
+ $users[] = new User($u['guid']);
+
+ return $users;
+ }
+
+ /*
+ * Check whether a given username is currently in use
+ */
+ function usernameInUse($username)
+ {
+ $escd_username = $this->db->esc($username);
+
+ $query = "SELECT name FROM object WHERE type = 'user' AND name = '" . $escd_username . "'";
+ $results = $this->db->query($query);
+
+ if (count($results) > 0)
+ return true;
+
+ return false;
+ }
+
+ /*
+ * Generate a key from a user's password and salt
+ */
+ function getKey($password, $salt)
+ {
+ return hash("sha256", $salt . $password);
+ }
+
+ /*
+ * Create a new User object with the given username and keyed with the given plain-text password
+ * This function returns false if $username is already being used
+ * On success, this object should be initialized as the new user (use only on new User() objects)
+ */
+ function createNewUser($username, $password)
+ {
+ if ($this->usernameInUse($username))
+ return false;
+
+ /* if there exist no users already, make this new one an admin */
+ if (count($this->getAllUsers_orderByName()) == 0)
+ $this->admin = 1;
+
+ $this->perms = 0;
+ $this->name = $username;
+ $this->type = "user";
+ $this->salt = $this->getBlob();
+ $this->key = $this->getKey($password, $this->salt);
+ $this->emailConf = 0;
+ $this->emailConfKey = $this->getBlob();
+
+ $this->saveObj();
+
+ $this->owner = $this->guid;
+ $this->saveObj();
+
+ return true;
+ }
+
+ /*
+ * Validate the password for this user. Returns true if correct, false otherwise
+ */
+ function validatePassword($password)
+ {
+ $key = $this->getKey($password, $this->salt);
+ return $key == $this->key;
+ }
}
?>
diff --git a/app/controller/auth.control.php b/app/controller/auth.control.php
new file mode 100644
index 0000000..f441310
--- /dev/null
+++ b/app/controller/auth.control.php
@@ -0,0 +1,53 @@
+<?php
+
+require_once "class/controller.class.php";
+require_once "model/auth.mod.php";
+
+/*
+ * Auth is used to login or register new user accounts
+ */
+class Auth extends Controller
+{
+ /*
+ * Controller implementation
+ */
+ function handle($argv)
+ {
+ $mod = new AuthModel();
+
+ switch ($_REQUEST['input']['action'])
+ {
+ case "signup":
+ $this->action_signup($mod);
+ break;
+
+ case "login":
+ $this->action_login($mod);
+ break;
+
+ default:
+ $this->action_default($mod);
+ break;
+ }
+ }
+
+ function action_default($mod)
+ {
+ $mod->deflt();
+ include "view/auth/default.view.php";
+ }
+
+ function action_signup($mod)
+ {
+ $mod->signup($_REQUEST['input']);
+ $this->action_default($mod);
+ }
+
+ function action_login($mod)
+ {
+ $mod->login($_REQUEST['input']);
+ $this->action_default($mod);
+ }
+}
+
+?>
diff --git a/app/controller/root.control.php b/app/controller/root.control.php
index 437cae1..06abf27 100644
--- a/app/controller/root.control.php
+++ b/app/controller/root.control.php
@@ -2,6 +2,7 @@
require_once "class/controller.class.php";
require_once "controller/sysconf.control.php";
+require_once "controller/auth.control.php";
/*
* Root-level controller for Scrott app. This object will delegate the page request to the
@@ -27,8 +28,20 @@ class Root extends Controller
}
/* TODO */
+ /* TODO -- only auth if logged out */
+ else if (!$this->getCurrentUser())
+ {
+ $ctrl = new Auth();
+ $ctrl->handle($argv);
+ }
+
else
- echo "Configuration is present!";
+ {
+ echo "logged in as:!";
+ echo "<pre>";
+ var_dump($this->getCurrentUser());
+ echo "</pre>";
+ }
}
/*
diff --git a/app/model/auth.mod.php b/app/model/auth.mod.php
new file mode 100644
index 0000000..23b8288
--- /dev/null
+++ b/app/model/auth.mod.php
@@ -0,0 +1,101 @@
+<?php
+
+require_once "model/common.mod.php";
+require_once "class/user.class.php";
+require_once "class/form.class.php";
+require_once "class/setting.class.php";
+
+class AuthModel extends CommonModel
+{
+ /*
+ * Default action
+ */
+ function deflt()
+ {
+ $userTbl = new User();
+
+ if (count($userTbl->getAllUsers_orderByName()) == 0)
+ {
+ $this->noaccounts = true;
+ $this->activeTab['signup'] = "in active";
+ $this->tabSwap = false;
+ }
+
+ else
+ {
+ $this->activeTab['login'] = "in active";
+ $this->tabSwap = Setting::allowPublicSignup();
+ }
+ }
+
+ /*
+ * Attempt to register a new account
+ */
+ function signup($input)
+ {
+ $userTbl = new User();
+
+ if (!Setting::allowPublicSignup() && count($userTbl->getAllUsers_orderByName()) > 0)
+ {
+ $this->logError("You may not signup at this time");
+ return;
+ }
+
+ $form = new Form();
+ $form->field_text("username");
+ $form->field_text("password", null, false);
+ $form->field_text("cPassword", null, false);
+
+ if (!$form->populate($input))
+ {
+ $this->logFormErrors($form);
+ return;
+ }
+
+ if ($form->password != $form->cPassword)
+ {
+ $this->logError("Passwords do not match");
+ return;
+ }
+
+ $user = new User();
+
+ if (!$user->createNewUser($form->username, $form->password))
+ {
+ $this->logError("Your requested username is already in use");
+ return;
+ }
+
+ $this->setCurrentUser($user);
+ $this->redirectTo($this->ap() . "/");
+ }
+
+ /*
+ * Attempt to login
+ */
+ function login($input)
+ {
+ $form = new Form();
+ $form->field_text("username");
+ $form->field_text("password", null, false);
+
+ if (!$form->populate($input))
+ {
+ $this->logFormErrors($form);
+ return;
+ }
+
+ $user = new User();
+
+ if (!($user->initByUsername($form->username) && $user->validatePassword($form->password)))
+ {
+ $this->logError("Username or password is incorrect");
+ return;
+ }
+
+ $this->setCurrentUser($user);
+ $this->redirectTo($this->ap() . "/");
+ }
+}
+
+?>
diff --git a/app/model/common.mod.php b/app/model/common.mod.php
new file mode 100644
index 0000000..d4270d8
--- /dev/null
+++ b/app/model/common.mod.php
@@ -0,0 +1,9 @@
+<?php
+
+require_once "model/master.mod.php";
+
+class CommonModel extends MasterModel
+{
+}
+
+?>
diff --git a/app/view/auth/default.view.php b/app/view/auth/default.view.php
new file mode 100644
index 0000000..16085e7
--- /dev/null
+++ b/app/view/auth/default.view.php
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+
+<html lang="en">
+ <head>
+ <?php include "view/common/head.view.php"; ?>
+ <title>Scrott - Not logged in</title>
+ </head>
+
+ <body>
+ <?php include "view/common/topp.view.php"; ?>
+
+ <div class="container">
+ <?php if (isset($mod->noaccounts)) { ?>
+ <div class="jumbotron">
+ <h1>Almost there....</h1>
+ <p class="text-center">Scrott's configuration is working, but no user accounts exist.<br />Use the form below to signup as an admin.</p>
+ <h5 class="text-center">It is presumed that you are the administrator for this Scrott install.<br />There is a security risk involved with exposing this page to the public!</h5>
+ </div>
+ <?php } ?>
+
+ <div class="tab-content">
+ <div class="tab-pane fade <?=$mod->activeTab['login']?>" id="loginTab">
+ <div class="row">
+ <div class="col-md-4"></div>
+
+ <div class="col-md-4">
+ <div class="panel panel-default">
+ <div class="panel-body text-center">
+ <form method="post" action="<?=$mod->ap()?>">
+ <input type="hidden" name="input[action]" value="login" />
+ <h1>Login</h1>
+
+ <div class="form-group">
+ <label for="loginUsername">Username</label>
+ <input type="text" name="input[username]" id="loginUsername" class="form-control" required="true" autofocus />
+ </div>
+
+ <div class="form-group">
+ <label for="loginPassword">Password</label>
+ <input type="password" name="input[password]" id="loginPassword" class="form-control" />
+ </div>
+
+ <div class="btn-group pull-right">
+ <?php if ($mod->tabSwap) { ?>
+ <a href="#signupTab" class="btn btn-default" aria-controls="signup" data-toggle="tab">
+ Signup <span class="glyphicon glyphicon-user"></span>
+ </a>
+ <?php } ?>
+
+ <button type="submit" class="btn btn-success">
+ Login <span class="glyphicon glyphicon-log-in"></span>
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-md-4"></div>
+ </div>
+ </div>
+
+ <div class="tab-pane fade <?=$mod->activeTab['signup']?>" id="signupTab">
+ <div class="row">
+ <div class="col-md-3"></div>
+
+ <div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <form method="post" action="<?=$mod->ap()?>">
+ <input type="hidden" name="input[action]" value="signup" />
+ <h1 class="text-center">Signup for Scrott</h1>
+
+ <div class="form-group">
+ <label for="signupUsername">Username</label>
+ <input type="text" name="input[username]" id="signupUsername" class="form-control" required="true" maxlength="50" />
+ </div>
+
+ <div class="form-group">
+ <label for="signupPassword">Password</label>
+ <input type="password" name="input[password]" id="signupPassword" class="form-control" />
+ </div>
+
+ <div class="form-group">
+ <label for="signupCPassword">Confirm Password</label>
+ <input type="password" name="input[cPassword]" id="signupCPassword" class="form-control" />
+ </div>
+
+ <div class="btn-group pull-right">
+ <?php if ($mod->tabSwap) { ?>
+ <a href="#loginTab" class="btn btn-default" aria-controls="login" data-toggle="tab">
+ Cancel <span class="glyphicon glyphicon-remove"></span>
+ </a>
+ <?php } ?>
+
+ <button type="submit" class="btn btn-success">
+ Signup <span class="glyphicon glyphicon-user"></span>
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-md-3"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <?php include "view/common/foot.view.php"; ?>
+ </body>
+</html>
diff --git a/app/view/common/foot.view.php b/app/view/common/foot.view.php
new file mode 100644
index 0000000..a24a145
--- /dev/null
+++ b/app/view/common/foot.view.php
@@ -0,0 +1 @@
+<?php include "view/master/foot.view.php"; ?>
diff --git a/app/view/common/head.view.php b/app/view/common/head.view.php
new file mode 100644
index 0000000..b23ec18
--- /dev/null
+++ b/app/view/common/head.view.php
@@ -0,0 +1,8 @@
+<?php include "view/master/head.view.php"; ?>
+
+<style type="text/css">
+ body
+ {
+ padding-top: 70px;
+ }
+</style>
diff --git a/app/view/common/topp.view.php b/app/view/common/topp.view.php
new file mode 100644
index 0000000..05e4862
--- /dev/null
+++ b/app/view/common/topp.view.php
@@ -0,0 +1,21 @@
+<?php include "view/master/topp.view.php"; ?>
+
+<nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="container-fluid">
+
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#scrottnav" aria-expanded="false">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+
+ <a href="<?=$mod->ar()?>/" class="navbar-brand"><span class="glyphicon glyphicon-pencil"></span> Scrott</a>
+ </div>
+
+ <div class="collapse navbar-collapse" id="scrottnav">
+ <p class="navbar-text navbar-right"><i>Not Logged In&nbsp;</i></p>
+ </div>
+
+ </div>
+</nav>
diff --git a/examples/example.html b/examples/example.html
index 44c91f2..7f1ac2a 100644
--- a/examples/example.html
+++ b/examples/example.html
@@ -1,31 +1,11 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <title>Scrott - Save the World</title>
-
- <style type="text/css">
-body
-{
- padding-top: 70px;
-}
- </style>
- </head>
-
<body>
<!--NAVBAR-->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
- <!--TITLE AND EXPAND BUTTON-->
- <div class="navbar-header">
- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-1" aria-expanded="false">
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
-
- <a href="/" class="navbar-brand"><span class="glyphicon glyphicon-pencil"></span> Scrott</a>
- </div>
+
<!--NAVBAR CONTENT-->
<div class="collapse navbar-collapse" id="navbar-collapse-1">
diff --git a/schema.sql b/schema.sql
index add1472..8902fbf 100644
--- a/schema.sql
+++ b/schema.sql
@@ -47,7 +47,7 @@ DROP TABLE IF EXISTS `object`;
CREATE TABLE `object` (
`guid` varchar(10) NOT NULL,
`perms` int(10) unsigned NOT NULL DEFAULT 0,
- `owner` varchar(10) NOT NULL,
+ `owner` varchar(10) DEFAULT NULL,
`parent` varchar(10) DEFAULT NULL,
`name` varchar(50) NOT NULL,
`timeCreated` datetime NOT NULL,
@@ -86,7 +86,6 @@ CREATE TABLE `msg_read` (
);
/* User objects - special attributes */
-/* Notice: 'timeUpdated' field should be used to store the time of the start of the user's current session */
/* Notice: 'key' field is the user's hashed and salted password -- SHA256 */
/* Notice: 'salt' is a random SHA256 output, used as salt for user's password */
DROP TABLE IF EXISTS `user`;
@@ -95,6 +94,7 @@ CREATE TABLE `user` (
`key` varchar(64) NOT NULL,
`salt` varchar(64) NOT NULL,
`alias` varchar(50) DEFAULT NULL,
+ `admin` int(10) unsigned NOT NULL DEFAULT 0,
`email` varchar(50) DEFAULT NULL,
`emailConf` int(10) unsigned NOT NULL DEFAULT 0,
`emailConfKey` varchar(64) NOT NULL,