diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | classes/BusinessAdmin.php | 46 | ||||
-rw-r--r-- | classes/Constants.php | 4 | ||||
-rw-r--r-- | classes/File.php | 14 | ||||
-rw-r--r-- | include/file-get.php | 47 | ||||
-rw-r--r-- | index.php | 3 | ||||
-rw-r--r-- | install/index.php | 113 | ||||
-rw-r--r-- | install/upgrade.php | 21 |
8 files changed, 189 insertions, 63 deletions
@@ -56,7 +56,6 @@ In classes/constants.php, you should at least set the following options: `ba_offer`, `ba_client`, etc. * `files_folder`: the absolute path to the files folder (this is a folder in the BusinessAdmin directory) -* `files_folder_external`: an HTTP(S) URL to the same files folder * `url_external`: the external HTTP(S) URL to BusinessAdmin * `url_internal`: the same URL, without the domain part @@ -84,6 +83,8 @@ Go to `/install/index.php?password_cost` to benchmark your system to find an appropriate password cost. Set the constant `password_cost` in `classes/constants.php` to this value. +You need to deny any requests for any files in the /files folder. + ## Permissions The files folder will be used to put generated invoices in. Make your server @@ -171,6 +172,7 @@ are listed by name and removal time. This way, you never really lose your file. ### 0.5 (Jul 28, 2016) +0.5.1 Files are hidden from the world. 0.5 Braintree integration. ### 0.4 (Jul 26, 2016) diff --git a/classes/BusinessAdmin.php b/classes/BusinessAdmin.php index 3e654a2..be6f1a4 100644 --- a/classes/BusinessAdmin.php +++ b/classes/BusinessAdmin.php @@ -76,6 +76,52 @@ class BusinessAdmin { } /** + * Get all file ids + * + * @see BusinessAdmin::getFiles() This funtion returns instances of the file 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 getFileIds($pdo, $where = [], $variables = []) { + $ids = []; + $files = $pdo->prepare("SELECT `id` FROM `".Constants::db_prefix."file`" . ((count($where) > 0) ? (" WHERE (" . implode(') AND (', $where) . ")") : "")); + $files->execute($variables); + $files = $files->fetchAll(PDO::FETCH_ASSOC); + foreach ($files as $file) { + $ids[] = $file['id']; + } + return $ids; + } + + /** + * Get all files + * + * @see BusinessAdmin::getFileIds() This function returns just the ids of the files, and not instances of the file 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 file[] An array indexed by id of instances of the file class + */ + public static function getFiles($pdo, $where = [], $variables = []) { + $ids = self::getFileIds($pdo, $where, $variables); + $files = []; + foreach ($ids as $id) { + $files[$id] = new File($pdo, $id); + } + return $files; + } + + /** * 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 fb2435d..fbac6cf 100644 --- a/classes/Constants.php +++ b/classes/Constants.php @@ -30,8 +30,6 @@ class Constants { /** @const files_folder The folder to store all files (appendices, invoices, etc.) in; with a trailing slash */ const files_folder = '/var/www/localhost/BusinessAdmin/files/'; - /** @const files_folder_external The external URI to this folder; with a trailing slash */ - const files_folder_external = 'http://localhost/BusinessAdmin/files/'; /** @const files_folder_trash The folder inside files_folder to use a trash, without any trailing slashes */ const files_folder_trash = 'trash'; @@ -80,5 +78,5 @@ class Constants { const password_cost = 10; /** @const version Version of BusinessAdmin. Don't change this yourself! */ - const version = '0.5'; + const version = '0.5.1'; } diff --git a/classes/File.php b/classes/File.php index 2545dc9..4a28f80 100644 --- a/classes/File.php +++ b/classes/File.php @@ -27,7 +27,17 @@ class File extends Model { public $table = 'file', - $fillable_columns = ['filename']; + $fillable_columns = ['filename', 'secret_key']; + + /** + * A random max-63-char string that can be used as secret_key + * + * @return string The random string + */ + public static function getRandomSecretKey() { + return preg_replace('/[^\w]+/', '', + base64_encode(openssl_random_pseudo_bytes(45))); + } /** * Get the full internal path to the file @@ -48,7 +58,7 @@ class File extends Model { * @return string The URI */ public function getFilenameURI() { - return Constants::files_folder_external . $this->filename; + return Constants::url_external . 'file/get?name=' . $this->filename . '&key=' . $this->secret_key; } //------------------------------------------------------------------------------ diff --git a/include/file-get.php b/include/file-get.php new file mode 100644 index 0000000..b3f575d --- /dev/null +++ b/include/file-get.php @@ -0,0 +1,47 @@ +<?php +/** + * Landing page + * + * This handles basically all requests. Every request not to an existing file path, should be redirected here. + * This file checks basic configuration and includes the required page, based on the REQUEST_URI. + * + * @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'); + +$filename = $_REQUEST['name']; +$filepath = Constants::files_folder . $filename; + +$key = $_REQUEST['key']; + +$files = BusinessAdmin::getFiles($_pdo, ['`filename`=?'], [$filename]); +if (count($files) == 0 || !file_exists($filepath) || is_dir($filepath)) { + http_response_code(404); + header('Content-type: text/plain'); + echo "$filename could not be found"; +} elseif (array_pop($files)->secret_key != $key) { + http_response_code(403); + header('Content-type: text/plain'); + echo "incorrect key"; +} else { + header('Content-type: ' . mime_content_type($filepath)); + header('Content-Disposition: attachment; filename="'.$filename.'"'); + readfile($filepath); +} @@ -56,7 +56,8 @@ $pages = array( '/settings' => './include/settings.php', '/users/new' => './include/users-new.php', '/ajax/collapse' => './include/ajax-collapse.php', - '/pay' => './include/pay.php' + '/pay' => './include/pay.php', + '/file/get' => './include/file-get.php' ); $_page = null; diff --git a/install/index.php b/install/index.php index d40959f..519e3c5 100644 --- a/install/index.php +++ b/install/index.php @@ -22,72 +22,73 @@ require('../conf.php'); if (isset($_GET['create_tables'])) { try { $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."assignment` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `offerId` smallint(5) unsigned NOT NULL, - `title` tinytext NOT NULL, - `description` text NOT NULL, - `hours` float NOT NULL, - `price_per_hour` float NOT NULL, - `VAT_percentage` float NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `offerId - title` (`offerId`,`title`(255)), - KEY `offerId` (`offerId`) + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `offerId` smallint(5) unsigned NOT NULL, + `title` tinytext NOT NULL, + `description` text NOT NULL, + `hours` float NOT NULL, + `price_per_hour` float NOT NULL, + `VAT_percentage` float NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `offerId - title` (`offerId`,`title`(255)), + KEY `offerId` (`offerId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."client` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `name` tinytext NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`(255)) + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `name` tinytext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`(255)) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."contact` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `clientId` smallint(5) unsigned NOT NULL, - `name` tinytext NOT NULL, - `email` varchar(680) NOT NULL, - `address` tinytext NOT NULL, - `address_2` tinytext, - `postal_code` varchar(7) NOT NULL, - `city` tinytext NOT NULL, - `country` tinytext NOT NULL, - `language` varchar(3) NOT NULL DEFAULT 'en', - PRIMARY KEY (`id`), - UNIQUE KEY `clientId-name` (`clientId`,`name`(255)), - KEY `clientId` (`clientId`) + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `clientId` smallint(5) unsigned NOT NULL, + `name` tinytext NOT NULL, + `email` varchar(680) NOT NULL, + `address` tinytext NOT NULL, + `address_2` tinytext, + `postal_code` varchar(7) NOT NULL, + `city` tinytext NOT NULL, + `country` tinytext NOT NULL, + `language` varchar(3) NOT NULL DEFAULT 'en', + PRIMARY KEY (`id`), + UNIQUE KEY `clientId-name` (`clientId`,`name`(255)), + KEY `clientId` (`clientId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."discount` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `offerId` smallint(5) unsigned NOT NULL, - `title` tinytext NOT NULL, - `description` text NOT NULL, - `value` float unsigned NOT NULL, + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `offerId` smallint(5) unsigned NOT NULL, + `title` tinytext NOT NULL, + `description` text NOT NULL, + `value` float unsigned NOT NULL, `VAT_percentage` float NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."file` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `filename` varchar(100) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `filename` (`filename`) + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `filename` varchar(100) NOT NULL, + `secret_key` varchar(63) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `filename` (`filename`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."offer` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `contactId` smallint(5) unsigned NOT NULL, - `start_date` date NOT NULL, - `end_date` date NOT NULL, - `invoice_date` date NOT NULL, - `accepted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `contactId` smallint(5) unsigned NOT NULL, + `start_date` date NOT NULL, + `end_date` date NOT NULL, + `invoice_date` date NOT NULL, + `accepted` tinyint(1) unsigned NOT NULL DEFAULT '0', `invoice_fileId` smallint(5) unsigned DEFAULT NULL, `payment_key` varchar(63) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `invoice_fileId` (`invoice_fileId`), - KEY `contactId` (`contactId`), - KEY `contactId_2` (`contactId`), - KEY `invoice_fileId_2` (`invoice_fileId`) + PRIMARY KEY (`id`), + UNIQUE KEY `invoice_fileId` (`invoice_fileId`), + KEY `contactId` (`contactId`), + KEY `contactId_2` (`contactId`), + KEY `invoice_fileId_2` (`invoice_fileId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1"); $_pdo->query("CREATE TABLE IF NOT EXISTS `payment` ( @@ -100,24 +101,24 @@ if (isset($_GET['create_tables'])) { ) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); $_pdo->query("CREATE TABLE IF NOT EXISTS `".Constants::db_prefix."user` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `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`)"); + ADD CONSTRAINT `assignment_ibfk_1` FOREIGN KEY (`offerId`) REFERENCES `".Constants::db_prefix."offer` (`id`)"); - $_pdo->query("ALTER TABLE `".Constants::db_prefix."contact` - ADD CONSTRAINT `contact_ibfk_1` FOREIGN KEY (`clientId`) REFERENCES `".Constants::db_prefix."client` (`id`)"); + $_pdo->query("ALTER LE `".Constants::db_prefix."contact` + ADD CONSTRAINT `contact_ibfk_1` FOREIGN KEY (`clientId`) REFERENCES `".Constants::db_prefix."client` (`id`)"); - $_pdo->query("ALTER TABLE `".Constants::db_prefix."discount` - ADD CONSTRAINT `discount_ibfk_1` FOREIGN KEY (`offerId`) REFERENCES `".Constants::db_prefix."offer` (`id`);"); + $_pdo->query("ALTER LE `".Constants::db_prefix."discount` + ADD CONSTRAINT `discount_ibfk_1` FOREIGN KEY (`offerId`) REFERENCES `".Constants::db_prefix."offer` (`id`);"); - $_pdo->query("ALTER TABLE `".Constants::db_prefix."offer` - ADD CONSTRAINT `offer_ibfk_1` FOREIGN KEY (`invoice_fileId`) REFERENCES `".Constants::db_prefix."file` (`id`), - ADD CONSTRAINT `offer_ibfk_2` FOREIGN KEY (`contactId`) REFERENCES `".Constants::db_prefix."contact` (`id`)"); + $_pdo->query("ALTER LE `".Constants::db_prefix."offer` + ADD CONSTRAINT `offer_ibfk_1` FOREIGN KEY (`invoice_fileId`) REFERENCES `".Constants::db_prefix."file` (`id`), + ADD CONSTRAINT `offer_ibfk_2` FOREIGN KEY (`contactId`) REFERENCES `".Constants::db_prefix."contact` (`id`)"); $_pdo->query("CREATE UNIQUE INDEX `payment_uniq_1` ON `".Constants::db_prefix."payment` (`offerId`);"); $_pdo->query("ALTER TABLE `payment` diff --git a/install/upgrade.php b/install/upgrade.php index 8ead230..db0c913 100644 --- a/install/upgrade.php +++ b/install/upgrade.php @@ -31,6 +31,8 @@ function lower_version($that, $new) { for ($i = 0; $i < count($new); $i++) { if ($new[$i] > $that[$i]) { return true; + } elseif ($new[$i] < $that[$i]) { + return false; } } return false; @@ -111,15 +113,34 @@ if (isset($_GET['upgrade'])) { try { $_pdo->query("ALTER TABLE `".Constants::db_prefix."offer` ADD `payment_key` VARCHAR(63) DEFAULT NULL;"); + $_pdo->query("ALTER TABLE `".Constants::db_prefix."payment` ADD `braintree_id` VARCHAR(36) DEFAULT NULL, ADD `braintree_status` VARCHAR (63) NULL DEFAULT NULL;"); + $_pdo->query("CREATE UNIQUE INDEX `payment_uniq_1` ON `".Constants::db_prefix."payment` (`offerId`);"); } catch (PDOException $e) { echo "Altering the database structure failed with a PDOException ({$e->getCode()}): {$e->getMessage()}<br/>" . $e->getTraceAsString(); } } + if (lower_version($_GET['upgrade'], '0.5.1')) { + try { + $_pdo->query("ALTER TABLE `".Constants::db_prefix."file` + ADD `secret_key` VARCHAR(63) DEFAULT NULL;"); + + $files = $_pdo->query("SELECT `id` FROM `".Constants::db_prefix."file` WHERE `secret_key` IS NULL;"); + $stmt = $_pdo->prepare("UPDATE `".Constants::db_prefix."file` SET `secret_key`=? WHERE `id`=?"); + foreach ($files->fetchAll(PDO::FETCH_ASSOC) as $file) { + $key = preg_replace('/[^\w]+/', '', + base64_encode(openssl_random_pseudo_bytes(45))); + $stmt->execute([$key, $file['id']]); + } + } catch (PDOException $e) { + echo "Altering the database structure failed with a PDOException ({$e->getCode()}): {$e->getMessage()}<br/>" . $e->getTraceAsString(); + } + } + echo "<br/>All done."; } ?> |