aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCamil Staps2016-07-26 00:16:17 +0200
committerCamil Staps2016-07-26 00:17:07 +0200
commit93b405ab9f69538546165c75a301c0c57a5359cf (patch)
tree7fab746b7fadcd26d012255b7bfad65a5b14ef61
parentUpdate makefile for d9936a9 (diff)
User authentication mechanism
-rw-r--r--README.md9
-rw-r--r--classes/BusinessAdmin.php46
-rw-r--r--classes/constants.php5
-rw-r--r--classes/user.php172
-rw-r--r--include/about.php3
-rw-r--r--include/assignments-edit.php1
-rw-r--r--include/assignments-new.php3
-rw-r--r--include/assignments-overview.php2
-rw-r--r--include/assignments-view.php2
-rw-r--r--include/assignments.php3
-rw-r--r--include/clients-edit.php3
-rw-r--r--include/clients-new.php3
-rw-r--r--include/clients-overview.php4
-rw-r--r--include/clients-view.php2
-rw-r--r--include/clients.php3
-rw-r--r--include/contacts-edit.php3
-rw-r--r--include/contacts-new.php3
-rw-r--r--include/contacts-overview.php4
-rw-r--r--include/contacts.php3
-rw-r--r--include/discounts-edit.php1
-rw-r--r--include/discounts-new.php1
-rw-r--r--include/discounts-overview.php2
-rw-r--r--include/discounts.php1
-rw-r--r--include/home.php5
-rw-r--r--include/offers-edit.php3
-rw-r--r--include/offers-new.php3
-rw-r--r--include/offers-overview.php2
-rw-r--r--include/offers-view.php2
-rw-r--r--include/offers.php3
-rw-r--r--include/settings.php84
-rw-r--r--index.php1
-rw-r--r--install/index.php19
-rw-r--r--install/upgrade.php13
-rw-r--r--login-ajax.php37
-rw-r--r--login.php89
-rw-r--r--nav.php215
36 files changed, 629 insertions, 126 deletions
diff --git a/README.md b/README.md
index fdcfd8e..6c81c39 100644
--- a/README.md
+++ b/README.md
@@ -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');
+?>
diff --git a/index.php b/index.php
index 129c83c..3c8d842 100644
--- a/index.php
+++ b/index.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']);
diff --git a/nav.php b/nav.php
index 4ff4a57..3b67dbb 100644
--- a/nav.php
+++ b/nav.php
@@ -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>