diff options
author | Malf Furious <m@lfurio.us> | 2016-02-07 14:01:22 -0500 |
---|---|---|
committer | Malf Furious <m@lfurio.us> | 2016-02-07 14:01:22 -0500 |
commit | 72e37180b69db2c067b926b54c64e1989bef0be4 (patch) | |
tree | d5e76f4894d3822f1923464c641c63fa283bdd2b | |
parent | 43ca317ea7d49396eb2958a38aed8b737ef4186b (diff) | |
parent | c5564a0a2ae183c533a38905eccdbf383030cd4c (diff) | |
download | scrott-72e37180b69db2c067b926b54c64e1989bef0be4.tar.gz scrott-72e37180b69db2c067b926b54c64e1989bef0be4.zip |
Merge branch 'auth' into dev
-rw-r--r-- | app/class/framework.class.php | 41 | ||||
-rw-r--r-- | app/class/object.class.php | 25 | ||||
-rw-r--r-- | app/class/setting.class.php | 30 | ||||
-rw-r--r-- | app/class/user.class.php | 95 | ||||
-rw-r--r-- | app/controller/auth.control.php | 53 | ||||
-rw-r--r-- | app/controller/root.control.php | 15 | ||||
-rw-r--r-- | app/model/auth.mod.php | 101 | ||||
-rw-r--r-- | app/model/common.mod.php | 9 | ||||
-rw-r--r-- | app/view/auth/default.view.php | 113 | ||||
-rw-r--r-- | app/view/common/foot.view.php | 1 | ||||
-rw-r--r-- | app/view/common/head.view.php | 8 | ||||
-rw-r--r-- | app/view/common/topp.view.php | 21 | ||||
-rw-r--r-- | examples/example.html | 22 | ||||
-rw-r--r-- | schema.sql | 4 |
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 </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"> @@ -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, |