diff options
36 files changed, 629 insertions, 126 deletions
@@ -33,11 +33,6 @@ git clone --recursive https://github.com/camilstaps/BusinessAdmin.git * Naturally, you should have your server configured to run PHP files with a PHP backend. -* BusinessAdmin does not have a user management system. Anyone who has access - to the URL, has access to the system and all its data. It's therefore - necessary that you implement HTTP basic authentication. Refer to the - documentation of your server for how to do that. - ## Create a database BusinessAdmin assumes a MySQL database is setup properly. This can be: @@ -161,6 +156,10 @@ are listed by name and removal time. This way, you never really lose your file. # Changelog +### 0.4 (Jul 26, 2016) + +0.4 User authentication mechanism + ### 0.3 (Jul 20, 2016) 0.3 Discounts diff --git a/classes/BusinessAdmin.php b/classes/BusinessAdmin.php index dc1f3e7..ce332ee 100644 --- a/classes/BusinessAdmin.php +++ b/classes/BusinessAdmin.php @@ -30,6 +30,52 @@ class BusinessAdmin { //------------------------------------------------------------------------------ /** + * Get all user ids + * + * @see BusinessAdmin::getUsers() This funtion returns instances of the user class instead of just the ids + * + * @param PDO $pdo The PDO class for database connection + * @param string[] $where An array of WHERE clauses that will be AND-ed into a prepared statement + * @param mixed[] $variables An array of variables that should go into the prepared statement + * + * @throws PDOException Is something went wrong with the database + * + * @return int[] The ids + */ + public static function getUserIds($pdo, $where = [], $variables = []) { + $ids = []; + $users = $pdo->prepare("SELECT `id` FROM `".constants::db_prefix."user`" . ((count($where) > 0) ? (" WHERE (" . implode(') AND (', $where) . ")") : "")); + $users->execute($variables); + $users = $users->fetchAll(PDO::FETCH_ASSOC); + foreach ($users as $user) { + $ids[] = $user['id']; + } + return $ids; + } + + /** + * Get all users + * + * @see BusinessAdmin::getUserIds() This function returns just the ids of the users, and not instances of the user class + * + * @param PDO $pdo The PDO class for database connection + * @param string[] $where An array of WHERE clauses that will be AND-ed into a prepared statement + * @param mixed[] $variables An array of variables that should go into the prepared statement + * + * @throws PDOException If something went wrong with the database + * + * @return user[] An array indexed by id of instances of the user class + */ + public static function getUsers($pdo, $where = [], $variables = []) { + $ids = self::getUserIds($pdo, $where, $variables); + $users = []; + foreach ($ids as $id) { + $users[$id] = new user($pdo, $id); + } + return $users; + } + + /** * Get all client ids * * @see BusinessAdmin::getClients() This funtion returns instances of the client class instead of just the ids diff --git a/classes/constants.php b/classes/constants.php index d16bde4..77f47b0 100644 --- a/classes/constants.php +++ b/classes/constants.php @@ -69,6 +69,9 @@ class constants { /** @const fa_valuta see http://fontawesome.io/icons/#currency; the fa- postfix for valuta */ const fa_valuta = 'eur'; + /** @const password_cost for the password_hash function. Run install?password_cost to benchmark your system */ + const password_cost = 10; + /** @const version Version of BusinessAdmin. Don't change this yourself! */ - const version = '0.3'; + const version = '0.4'; } diff --git a/classes/user.php b/classes/user.php new file mode 100644 index 0000000..261fa3d --- /dev/null +++ b/classes/user.php @@ -0,0 +1,172 @@ +<?php +/** + * Provides the user class, an interface to the user table in the database + * + * @author Camil Staps + * + * BusinessAdmin: administrative software for small companies + * Copyright (C) 2015 Camil Staps (ViviSoft) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * An interface to the user table in the database + */ +class user { + /** + * @var pdo $pdo The PDO class for database communication + * @var int $id The id of the user + * @var string $username The username of the user + * @var string $password The (hashed) password of the user + */ + protected $pdo, $id, $username, $password; + + /** + * Hash a password + * + * @param string $password The password to be hashed + * @param int $cost The password cost + * + * @return string The hashed password + */ + public static function hash($password, $cost=null) { + return password_hash( + $password, + PASSWORD_DEFAULT, + ['cost' => is_null($cost) ? constants::password_cost : $cost] + ); + } + + /** + * Create a new instance + * + * @param PDO $pdo The PDO class, to access the database + * @param int $id The id of the user to fetch + * + * @throws PDOException If something went wrong with the database + * @throws Exception If the user could not be found + */ + public function __construct($pdo, $id) { + $this->pdo = $pdo; + + $stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."user` WHERE `id`=?"); + $stmt->execute(array($id)); + if ($stmt->rowCount() == 0) { + throw new Exception("The user with id '$id' could not be found."); + } + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + $this->id = $user['id']; + $this->username = $user['username']; + $this->password = $user['password']; + } + + //------------------------------------------------------------------------------ + // Getters and setters + //------------------------------------------------------------------------------ + + /** + * Get the ID of the user + * + * @return int The ID + */ + public function getId() { + return $this->id; + } + + /** + * Get the username of the user + * + * @return string The username + */ + public function getUsername() { + return $this->username; + } + + /** + * Set the username of the user + * + * @param string $username The new username for the user + * + * @throws PDOException If something went wrong with the database + * + * @return bool True on succes, false on failure + */ + public function setName($username) { + $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."user` SET `username`=? WHERE `id`=?"); + $stmt->execute(array($username, $this->id)); + if ($stmt->rowCount() == 1) { + $this->username = $username; + return true; + } else { + return false; + } + } + + /** + * Set the password of the user + * + * @param string $password The new password for the user + * + * @throws PDOException If something went wrong with the database + * + * @return bool True on succes, false on failure + */ + public function setPassword($password) { + $password = self::hash($password); + $stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."user` SET `password`=? WHERE `id`=?"); + $stmt->execute(array($password, $this->id)); + if ($stmt->rowCount() == 1) { + $this->password = $password; + return true; + } else { + return false; + } + } + + //------------------------------------------------------------------------------ + // Other functions + //------------------------------------------------------------------------------ + + /** + * Verify a password + * + * @param string $password The password to verify + * + * @return bool True iff the password can be accepted + */ + public function verifyPassword($password) { + return password_verify($password, $this->password); + } + + /** + * Remove this user from the database + * + * If this doesn't succeed (i.e. false is returned), that means the user was removed manually or by another instance of this class + * + * @throws PDOException If something went wrong with the database + * + * @return bool True on success, false on failure + */ + public function delete() { + $stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."user` WHERE `id`=?"); + $stmt->execute(array($this->id)); + if ($stmt->rowCount() != 1) { + return false; + } else { + return true; + } + } +} diff --git a/include/about.php b/include/about.php index bc93e14..554a80f 100644 --- a/include/about.php +++ b/include/about.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> @@ -104,4 +105,4 @@ info@camilstaps.nl</pre> <!-- /#wrapper --> <?php require('./footer.php'); -?>
\ No newline at end of file +?> diff --git a/include/assignments-edit.php b/include/assignments-edit.php index 4faad64..b52311a 100644 --- a/include/assignments-edit.php +++ b/include/assignments-edit.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); diff --git a/include/assignments-new.php b/include/assignments-new.php index 2de3b1f..7898a42 100644 --- a/include/assignments-new.php +++ b/include/assignments-new.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -42,4 +43,4 @@ try { $response->success = false; $response->message = "The assignment could not be created due to an error."; } -echo $response->getJson();
\ No newline at end of file +echo $response->getJson(); diff --git a/include/assignments-overview.php b/include/assignments-overview.php index 903a772..2de6858 100644 --- a/include/assignments-overview.php +++ b/include/assignments-overview.php @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +require_once('./login.php'); ?> <div class="col-lg-12"> diff --git a/include/assignments-view.php b/include/assignments-view.php index ffdc507..7a2923e 100644 --- a/include/assignments-view.php +++ b/include/assignments-view.php @@ -17,6 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +require_once('./login.php'); + $_assignment = new assignment($_pdo, $_id); ?> <div class="col-lg-6"> diff --git a/include/assignments.php b/include/assignments.php index 5ba21c6..eaa7163 100644 --- a/include/assignments.php +++ b/include/assignments.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> @@ -95,4 +96,4 @@ require('./header.php'); <?php require('./footer.php'); -?>
\ No newline at end of file +?> diff --git a/include/clients-edit.php b/include/clients-edit.php index c0b83c8..7d8d6fa 100644 --- a/include/clients-edit.php +++ b/include/clients-edit.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -39,4 +40,4 @@ try { $response->success = false; $response->message = "The client could not be edited due to an exception."; } -echo $response->message;
\ No newline at end of file +echo $response->message; diff --git a/include/clients-new.php b/include/clients-new.php index 9466638..b073b8e 100644 --- a/include/clients-new.php +++ b/include/clients-new.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -35,4 +36,4 @@ try { $response->success = false; $response->message = "The client could not be created due to a PDO error ({$e->getMessage()})."; } -echo $response->getJson();
\ No newline at end of file +echo $response->getJson(); diff --git a/include/clients-overview.php b/include/clients-overview.php index f3e2a24..7ce45a6 100644 --- a/include/clients-overview.php +++ b/include/clients-overview.php @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +require_once('./login.php'); ?> <div class="col-lg-6 col-md-6"> @@ -117,4 +119,4 @@ </script> </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/include/clients-view.php b/include/clients-view.php index 6aa900c..101d530 100644 --- a/include/clients-view.php +++ b/include/clients-view.php @@ -17,6 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +require_once('./login.php'); + $_client = new client($_pdo, $_id); ?> <div class="col-lg-12"> diff --git a/include/clients.php b/include/clients.php index 7248e0c..88608ab 100644 --- a/include/clients.php +++ b/include/clients.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> @@ -95,4 +96,4 @@ require('./header.php'); <?php require('./footer.php'); -?>
\ No newline at end of file +?> diff --git a/include/contacts-edit.php b/include/contacts-edit.php index 9e7c606..a2101b2 100644 --- a/include/contacts-edit.php +++ b/include/contacts-edit.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -62,4 +63,4 @@ try { $response->success = false; $response->message = "The contact could not be edited due to an exception."; } -echo $response->message;
\ No newline at end of file +echo $response->message; diff --git a/include/contacts-new.php b/include/contacts-new.php index c04fa72..0a72afb 100644 --- a/include/contacts-new.php +++ b/include/contacts-new.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -44,4 +45,4 @@ try { $response->success = false; $response->message = "The contact could not be created due to an error."; } -echo $response->getJson();
\ No newline at end of file +echo $response->getJson(); diff --git a/include/contacts-overview.php b/include/contacts-overview.php index cd3b1bc..cd7fc47 100644 --- a/include/contacts-overview.php +++ b/include/contacts-overview.php @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +require_once('./login.php'); ?> <div class="col-lg-7 col-md-7"> @@ -159,4 +161,4 @@ </script> </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/include/contacts.php b/include/contacts.php index 787bd9a..4b35603 100644 --- a/include/contacts.php +++ b/include/contacts.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> @@ -95,4 +96,4 @@ require('./header.php'); <?php require('./footer.php'); -?>
\ No newline at end of file +?> diff --git a/include/discounts-edit.php b/include/discounts-edit.php index e6859a1..a760e9c 100644 --- a/include/discounts-edit.php +++ b/include/discounts-edit.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); diff --git a/include/discounts-new.php b/include/discounts-new.php index 1900d47..8a5f527 100644 --- a/include/discounts-new.php +++ b/include/discounts-new.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); diff --git a/include/discounts-overview.php b/include/discounts-overview.php index 6160a8c..f9a3630 100644 --- a/include/discounts-overview.php +++ b/include/discounts-overview.php @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +require_once('./login.php'); ?> <div class="col-lg-12"> diff --git a/include/discounts.php b/include/discounts.php index 83cb1b8..8b160ab 100644 --- a/include/discounts.php +++ b/include/discounts.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> diff --git a/include/home.php b/include/home.php index 644f2ae..cb624fe 100644 --- a/include/home.php +++ b/include/home.php @@ -17,8 +17,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -require_once('index.php'); -require('header.php'); +require_once('./index.php'); +require_once('./login.php'); +require('./header.php'); ?> <div id="wrapper"> diff --git a/include/offers-edit.php b/include/offers-edit.php index b2a7156..95de9b3 100644 --- a/include/offers-edit.php +++ b/include/offers-edit.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -56,4 +57,4 @@ try { $response->success = false; $response->message = "The offer could not be edited due to an exception."; } -echo $response->message;
\ No newline at end of file +echo $response->message; diff --git a/include/offers-new.php b/include/offers-new.php index 46bec3b..0d86293 100644 --- a/include/offers-new.php +++ b/include/offers-new.php @@ -18,6 +18,7 @@ */ require_once('./conf.php'); +require_once('./login-ajax.php'); $response = new response(); @@ -36,4 +37,4 @@ try { $response->success = false; $response->message = "The offer could not be created due to an error."; } -echo $response->getJson();
\ No newline at end of file +echo $response->getJson(); diff --git a/include/offers-overview.php b/include/offers-overview.php index 594f01c..7fd2de2 100644 --- a/include/offers-overview.php +++ b/include/offers-overview.php @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +require_once('./login.php'); ?> <div class="col-lg-12"> diff --git a/include/offers-view.php b/include/offers-view.php index eec7701..082af35 100644 --- a/include/offers-view.php +++ b/include/offers-view.php @@ -17,6 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +require_once('./login.php'); + $_offer = new offer($_pdo, $_id); ?> <div class="col-lg-6"> diff --git a/include/offers.php b/include/offers.php index 872773d..1aa871a 100644 --- a/include/offers.php +++ b/include/offers.php @@ -18,6 +18,7 @@ */ require_once('./index.php'); +require_once('./login.php'); require('./header.php'); ?> @@ -154,4 +155,4 @@ require('./header.php'); <?php require('./footer.php'); -?>
\ No newline at end of file +?> diff --git a/include/settings.php b/include/settings.php new file mode 100644 index 0000000..7dfbbc3 --- /dev/null +++ b/include/settings.php @@ -0,0 +1,84 @@ +<?php +/** + * BusinessAdmin: administrative software for small companies + * Copyright (C) 2015 Camil Staps (ViviSoft) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +require_once('./index.php'); +require_once('./login.php'); +require('./header.php'); +?> + +<div id="wrapper"> + + <?php require('nav.php'); ?> + + <!-- Page Content --> + <div id="page-wrapper"> + <div class="row"> + <div class="col-lg-12"> + <h1 class="page-header">Settings</h1> + </div> + <!-- /.col-lg-12 --> + </div> + + <div class="row"> + <div class="col-md-4"> + <div class="panel panel-default"> + <div class="panel-heading">Password</div> + <div class="panel-body"> + <?php + if (isset($_POST['password_update'])) { + if ($_POST['password_update'] != $_POST['password_update2']) { + echo '<div class="alert alert-danger">The passwords don\'t match.</div>'; + } else if (!$_user->verifyPassword($_POST['password_current'])) { + echo '<div class="alert alert-danger">The current password was incorrect.</div>'; + } else { + try { + $_user->setPassword($_POST['password_update']); + echo '<div class="alert alert-success">Password successfully changed.</div>'; + } catch (PDOException $e) { + echo '<div class="alert alert-danger">An unknown error occurred.</div>'; + } + } + } + ?> + <form action="" method="post"> + <div class="form-group"> + <input class="form-control" type="password" name="password_current" placeholder="Current password"/> + </div> + <div class="form-group"> + <input class="form-control" type="password" name="password_update" placeholder="New password"/> + </div> + <div class="form-group"> + <input class="form-control" type="password" name="password_update2" placeholder="New password (verification)"/> + </div> + <input class="btn btn-primary" type="submit" value="Change password"/> + </form> + </div> + </div> + </div> + </div> + </div> + <!-- /.row --> + </div> + <!-- /#page-wrapper --> + +</div> +<!-- /#wrapper --> +<?php +require('./footer.php'); +?> @@ -53,6 +53,7 @@ $pages = array( '/discounts/new' => './include/discounts-new.php', '/discounts/edit' => './include/discounts-edit.php', '/about' => './include/about.php', + '/settings' => './include/settings.php', '/ajax/collapse' => './include/ajax-collapse.php' ); diff --git a/install/index.php b/install/index.php index 6c57769..41fb450 100644 --- a/install/index.php +++ b/install/index.php @@ -89,6 +89,13 @@ if (isset($_GET['create_tables'])) { KEY `contactId_2` (`contactId`), KEY `invoice_fileId_2` (`invoice_fileId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); + + $_pdo->query("CREATE TABLE IF NOT EXISTS `".constants::db_prefix."user` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(24) NOT NULL, + `password` varchar(255) NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); $_pdo->query("ALTER TABLE `".constants::db_prefix."assignment` ADD CONSTRAINT `assignment_ibfk_1` FOREIGN KEY (`offerId`) REFERENCES `".constants::db_prefix."offer` (`id`)"); @@ -120,6 +127,17 @@ if (isset($_GET['create_folders'])) { echo "Creating folder `" . constants::files_folder_trash . "` failed.<br/>"; } } + +if (isset($_GET['password_cost'])) { + $target = 1; + $start = $end = 0; + for ($cost = 10; $end - $start < $target; $cost++) { + $start = microtime(true); + user::hash('test', $cost); + $end = microtime(true); + } + echo "Password cost suggestion: $cost.<br/>You can set this in classes/constants.php."; +} ?> <hr/> @@ -129,6 +147,7 @@ if (isset($_GET['create_folders'])) { <ol> <li><a href="?create_tables">Create database tables</a></li> <li><a href="?create_folders">Create folders</a></li> + <li><a href="?password_cost">Finding a good password cost</a></li> </ol> <p>When you're done, it would be the neatest to remove the /install folder (even though this whole control panel should not be accessible for the public).</p> diff --git a/install/upgrade.php b/install/upgrade.php index 04cc03a..e145ba7 100644 --- a/install/upgrade.php +++ b/install/upgrade.php @@ -73,6 +73,19 @@ if (isset($_GET['upgrade'])) { } } + if (lower_version($_GET['upgrade'], '0.4')) { + try { + $_pdo->query("CREATE TABLE IF NOT EXISTS `".constants::db_prefix."user` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(24) NOT NULL, + `password` varchar(255) NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); + } catch (PDOException $e) { + echo "Altering the database structure failed with a PDOException ({$e->getCode()}): {$e->getMessage()}<br/>" . $e->getTraceAsString(); + } + } + echo "<br/>All done."; } ?> diff --git a/login-ajax.php b/login-ajax.php new file mode 100644 index 0000000..f8e1424 --- /dev/null +++ b/login-ajax.php @@ -0,0 +1,37 @@ +<?php +/** + * Check if the user is logged in + * + * This file should be required by all sensitive PHP scripts. It verifies that + * the client has been logged in, and if not, displays a login page. + * + * This file is only for files that are called through Ajax, and need a json + * response. For other files, there is login.php. + * + * @author Camil Staps + * + * BusinessAdmin: administrative software for small companies + * Copyright (C) 2015 Camil Staps (ViviSoft) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +require_once('./conf.php'); + +if (!isset($_SESSION['login']) || $_SESSION['login'] === false) { + print(json_encode(['error' => 'You need to be logged in.'])); + die(); +} + +$_user = new user($_pdo, $_SESSION['login']); diff --git a/login.php b/login.php new file mode 100644 index 0000000..e60b7ed --- /dev/null +++ b/login.php @@ -0,0 +1,89 @@ +<?php +/** + * Check if the user is logged in + * + * This file should be required by all sensitive PHP scripts. It verifies that + * the client has been logged in, and if not, displays a login page. + * + * See also login-ajax.php, which is specific for files that are loaded through + * Ajax (and typically require a json response). + * + * @author Camil Staps + * + * BusinessAdmin: administrative software for small companies + * Copyright (C) 2015 Camil Staps (ViviSoft) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +require_once('./conf.php'); + +if (isset($_GET['logout'])) { + $_SESSION['login'] = false; + header('Location: ' . constants::url_external); + die(); +} + +if (!isset($_SESSION['login']) || $_SESSION['login'] === false) { + if (isset($_POST['username'])) { + $users = BusinessAdmin::getUsers($_pdo, ['`username`=?'], [$_POST['username']]); + if (count($users) == 0) { + $_msg = "No user {$_POST['username']} found.<br/>"; + } else { + $user = array_pop($users); + if ($user->verifyPassword($_POST['password'])) { + $_SESSION['login'] = $user->getId(); + $_user = $user; + return; + } else { + $_msg = "Password incorrect.<br/>"; + } + } + } + + include('./header.php'); +?> + <div class="container"> + <div class="row"> + <div class="col-md-4 col-md-offset-4"> + <div class="login-panel panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Login<i class="fa fa-lock fa-fw fa-lg pull-right"></i></h3> + </div> + <div class="panel-body"> + <?php + if (isset($_msg)) { + echo "<div class='form-group alert alert-danger'>$_msg</div>"; + } + ?> + <form action="" method="post"> + <div class="form-group"> + <input class="form-control" type="text" name="username" placeholder="Username" autofocus="autofocus"/> + </div> + <div class="form-group"> + <input class="form-control" type="password" name="password" placeholder="Password"/> + </div> + <input type="submit" class="btn btn-lg btn-success btn-block" value="login"/> + </form> + </div> + </div> + </div> + </div> + </div> + <?php + include('./footer.php'); + die(); +} + +$_user = new user($_pdo, $_SESSION['login']); @@ -20,113 +20,120 @@ <!-- Navigation --> <nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle" 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> - <a class="navbar-brand" href="<?=constants::url_internal?>/"><?=constants::my_name?></a> - </div> - <!-- /.navbar-header --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle" 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> + <a class="navbar-brand" href="<?=constants::url_internal?>/"><?=constants::my_name?></a> + </div> + <!-- /.navbar-header --> - <ul class="nav navbar-top-links navbar-right"> - <!-- /.dropdown --> - <li class="dropdown"> - <a class="dropdown-toggle" data-toggle="dropdown" href="#"> - <i class="fa fa-tasks fa-fw"></i> <i class="fa fa-caret-down"></i> - </a> - <ul class="dropdown-menu dropdown-tasks"> - <?php - $offers = BusinessAdmin::getOffers($_pdo, array("`accepted`=1", "`start_date` <= CURDATE()", "`end_date` >= CURDATE()")); - $list = array(); - foreach ($offers as $offer) { - $start = BusinessAdmin::formatDate($offer->getStartDate(), false); - $end = BusinessAdmin::formatDate($offer->getEndDate(), false); - $since = mktime(0,0,0,date("n"),date("j"),date("Y")) - $offer->getStartDate(); - $total = $offer->getEndDate() - $offer->getStartDate(); - $percentage = ($total == 0) ? 100 : round($since / $total * 100); + <ul class="nav navbar-top-links navbar-right"> + <span> + Logged in as <a href="<?=constants::url_internal?>/settings"><?=$_user->getUsername()?></a> + (<a href="<?=constants::url_internal?>/?logout">logout</a>) + </span> + <!-- /.dropdown --> + <li class="dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" href="#"> + <i class="fa fa-tasks fa-fw"></i> <i class="fa fa-caret-down"></i> + </a> + <ul class="dropdown-menu dropdown-tasks"> + <?php + $offers = BusinessAdmin::getOffers($_pdo, array("`accepted`=1", "`start_date` <= CURDATE()", "`end_date` >= CURDATE()")); + $list = array(); + foreach ($offers as $offer) { + $start = BusinessAdmin::formatDate($offer->getStartDate(), false); + $end = BusinessAdmin::formatDate($offer->getEndDate(), false); + $since = mktime(0,0,0,date("n"),date("j"),date("Y")) - $offer->getStartDate(); + $total = $offer->getEndDate() - $offer->getStartDate(); + $percentage = ($total == 0) ? 100 : round($since / $total * 100); - // We want to sort on percentage (DESC) and secondly end date (ASC) so start date (DESC) - $list[str_pad($percentage, 3, '0', STR_PAD_LEFT) . $offer->getStartDate()] = array( - 'start' => $start, - 'end' => $end, - 'id' => $offer->getId(), - 'contactClientName' => $offer->getContact()->getClient()->getName(), - 'percentage' => $percentage, - 'price' => constants::invoice_valuta . $offer->calculate(offer::SUBTOTAL) - ); - } - krsort($list, SORT_STRING); - foreach ($list as $item) { - echo "<li> - <a href='".constants::url_internal."/offers?id={$item['id']}'> - <div> - <p> - <strong>{$item['contactClientName']}</strong> ({$item['start']} - {$item['end']}; {$item['price']}) - </p> - <div class='progress progress-striped active' title='{$item['percentage']}% complete'> - <div class='progress-bar progress-bar-".($item['percentage'] < 60 ? 'info' : ($item['percentage'] < 80 ? 'warning' : 'danger'))."' style='width:{$item['percentage']}%;' aria-valuemax='100' aria-valuemin='0' aria-valuenow='{$item['percentage']}' role='progressbar'></div> - </div> - </div> - </a> - </li>"; - } - if (count($list) == 0) { - echo "<li><a href='#'><div><p>There are no currently active offers.</p></div></a></li>"; - } - ?> - <li> - <a class="text-center" href="<?=constants::url_internal?>/offers"> - <strong>See All Offers</strong> - <i class="fa fa-angle-right"></i> - </a> - </li> - </ul> - <!-- /.dropdown-tasks --> - </li> - <!-- /.dropdown --> - </ul> - <!-- /.navbar-top-links --> + // We want to sort on percentage (DESC) and secondly end date (ASC) so start date (DESC) + $list[str_pad($percentage, 3, '0', STR_PAD_LEFT) . $offer->getStartDate()] = array( + 'start' => $start, + 'end' => $end, + 'id' => $offer->getId(), + 'contactClientName' => $offer->getContact()->getClient()->getName(), + 'percentage' => $percentage, + 'price' => constants::invoice_valuta . $offer->calculate(offer::SUBTOTAL) + ); + } + krsort($list, SORT_STRING); + foreach ($list as $item) { + echo "<li> + <a href='".constants::url_internal."/offers?id={$item['id']}'> + <div> + <p> + <strong>{$item['contactClientName']}</strong> ({$item['start']} - {$item['end']}; {$item['price']}) + </p> + <div class='progress progress-striped active' title='{$item['percentage']}% complete'> + <div class='progress-bar progress-bar-".($item['percentage'] < 60 ? 'info' : ($item['percentage'] < 80 ? 'warning' : 'danger'))."' style='width:{$item['percentage']}%;' aria-valuemax='100' aria-valuemin='0' aria-valuenow='{$item['percentage']}' role='progressbar'></div> + </div> + </div> + </a> + </li>"; + } + if (count($list) == 0) { + echo "<li><a href='#'><div><p>There are no currently active offers.</p></div></a></li>"; + } + ?> + <li> + <a class="text-center" href="<?=constants::url_internal?>/offers"> + <strong>See All Offers</strong> + <i class="fa fa-angle-right"></i> + </a> + </li> + </ul> + <!-- /.dropdown-tasks --> + </li> + <!-- /.dropdown --> + </ul> + <!-- /.navbar-top-links --> - <div class="navbar-default sidebar" role="navigation"> - <div class="sidebar-nav navbar-collapse"> - <ul class="nav" id="side-menu"> - <li title="Dashboard"> - <a <?php if($_page=='/') echo 'class="active"'; ?> href="<?=constants::url_internal?>/"><i class="fa fa-dashboard fa-fw"></i> <span class="nav-title">Dashboard</span></a> - </li> - <li title="Clients"> - <a <?php if($_page=='/clients') echo 'class="active"'; ?> href="<?=constants::url_internal?>/clients"><i class="fa fa-institution fa-fw"></i> <span class="nav-title">Clients</span></a> - </li> - <li title="Contacts"> - <a <?php if($_page=='/contacts') echo 'class="active"'; ?> href="<?=constants::url_internal?>/contacts"><i class="fa fa-user fa-fw"></i> <span class="nav-title">Contacts</span></a> - </li> - <li title="Offers"> - <a <?php if($_page=='/offers') echo 'class="active"'; ?> href="<?=constants::url_internal?>/offers"><i class="fa fa-briefcase fa-fw"></i> <span class="nav-title">Offers</span></a> - </li> - <li title="Assignments"> - <a <?php if($_page=='/assignments') echo 'class="active"'; ?> href="<?=constants::url_internal?>/assignments"><i class="fa fa-check-square fa-fw"></i> <span class="nav-title">Assignments</span></a> - </li> - <li title="Discounts"> - <a <?php if($_page=='/discounts') echo 'class="active"'; ?> href="<?=constants::url_internal?>/discounts"><i class="fa fa-percent fa-fw"></i> <span class="nav-title">Discounts</span></a> - </li> - <li title="About"> - <a <?php if($_page=='/about') echo 'class="active"'; ?> href="<?=constants::url_internal?>/about"><i class="fa fa-info-circle fa-fw"></i> <span class="nav-title">About</span></a> - </li> - </ul> + <div class="navbar-default sidebar" role="navigation"> + <div class="sidebar-nav navbar-collapse"> + <ul class="nav" id="side-menu"> + <li title="Dashboard"> + <a <?php if($_page=='/') echo 'class="active"'; ?> href="<?=constants::url_internal?>/"><i class="fa fa-dashboard fa-fw"></i> <span class="nav-title">Dashboard</span></a> + </li> + <li title="Clients"> + <a <?php if($_page=='/clients') echo 'class="active"'; ?> href="<?=constants::url_internal?>/clients"><i class="fa fa-institution fa-fw"></i> <span class="nav-title">Clients</span></a> + </li> + <li title="Contacts"> + <a <?php if($_page=='/contacts') echo 'class="active"'; ?> href="<?=constants::url_internal?>/contacts"><i class="fa fa-user fa-fw"></i> <span class="nav-title">Contacts</span></a> + </li> + <li title="Offers"> + <a <?php if($_page=='/offers') echo 'class="active"'; ?> href="<?=constants::url_internal?>/offers"><i class="fa fa-briefcase fa-fw"></i> <span class="nav-title">Offers</span></a> + </li> + <li title="Assignments"> + <a <?php if($_page=='/assignments') echo 'class="active"'; ?> href="<?=constants::url_internal?>/assignments"><i class="fa fa-check-square fa-fw"></i> <span class="nav-title">Assignments</span></a> + </li> + <li title="Discounts"> + <a <?php if($_page=='/discounts') echo 'class="active"'; ?> href="<?=constants::url_internal?>/discounts"><i class="fa fa-percent fa-fw"></i> <span class="nav-title">Discounts</span></a> + </li> + <li title="Settings"> + <a <?php if($_page=='/settings') echo 'class="active"'; ?> href="<?=constants::url_internal?>/settings"><i class="fa fa-cog fa-fw"></i> <span class="nav-title">Settings</span></a> + </li> + <li title="About"> + <a <?php if($_page=='/about') echo 'class="active"'; ?> href="<?=constants::url_internal?>/about"><i class="fa fa-info-circle fa-fw"></i> <span class="nav-title">About</span></a> + </li> + </ul> - <a href="#" id="collapse-menu" title="Collapse menu"><i class="fa fa-fw fa-caret-square-o-left"></i></a> + <a href="#" id="collapse-menu" title="Collapse menu"><i class="fa fa-fw fa-caret-square-o-left"></i></a> - <?php if (isset($_SESSION['ba-collapse']) && $_SESSION['ba-collapse'] == true) : ?> - <script type="text/javascript"> - $(document).ready(function(){ - $('#collapse-menu').trigger('click', { load: true }); - }); - </script> - <?php endif; ?> - </div> - <!-- /.sidebar-collapse --> - </div> - <!-- /.navbar-static-side --> + <?php if (isset($_SESSION['ba-collapse']) && $_SESSION['ba-collapse'] == true) : ?> + <script type="text/javascript"> + $(document).ready(function(){ + $('#collapse-menu').trigger('click', { load: true }); + }); + </script> + <?php endif; ?> + </div> + <!-- /.sidebar-collapse --> + </div> + <!-- /.navbar-static-side --> </nav> |