diff --git a/.gitignore b/.gitignore index df78ea5..c583050 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ vendor settings.php nbproject/private database.mwb.bak -*.sync-conflict* \ No newline at end of file +*.sync-conflict* diff --git a/LICENSE.md b/LICENSE.md index 488389e..e6a9914 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,18 +1,18 @@ -Copyright (c) 2017 Netsyms Technologies. +Copyright (c) 2018 Netsyms Technologies. -If you modify and redistribute this project, you must replace the branding +If you modify and redistribute this project, you must replace the branding assets with your own. -The branding assets include: +The branding assets include: * the application icon * the Netsyms N punchcard logo * the Netsyms for Business graph logo -If you are unsure if your usage is allowed, please contact us: +If you are unsure if your usage is allowed, please contact us: https://netsyms.com/contact legal@netsyms.com -All other portions of this application, +All other portions of this application, unless otherwise noted (in comments, headers, etc), are licensed as follows: Mozilla Public License Version 2.0 @@ -20,24 +20,24 @@ Mozilla Public License Version 2.0 ### 1. Definitions -**1.1. “Contributor”** +**1.1. “Contributor”** means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. -**1.2. “Contributor Version”** +**1.2. “Contributor Version”** means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. -**1.3. “Contribution”** +**1.3. “Contribution”** means Covered Software of a particular Contributor. -**1.4. “Covered Software”** +**1.4. “Covered Software”** means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. -**1.5. “Incompatible With Secondary Licenses”** +**1.5. “Incompatible With Secondary Licenses”** means * **(a)** that the initial Contributor has attached the notice described @@ -46,22 +46,22 @@ Mozilla Public License Version 2.0 version 1.1 or earlier of the License, but not also under the terms of a Secondary License. -**1.6. “Executable Form”** +**1.6. “Executable Form”** means any form of the work other than Source Code Form. -**1.7. “Larger Work”** - means a work that combines Covered Software with other material, in +**1.7. “Larger Work”** + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. -**1.8. “License”** +**1.8. “License”** means this document. -**1.9. “Licensable”** +**1.9. “Licensable”** means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. -**1.10. “Modifications”** +**1.10. “Modifications”** means any of the following: * **(a)** any file in Source Code Form that results from an addition to, @@ -70,7 +70,7 @@ Mozilla Public License Version 2.0 * **(b)** any new file in Source Code Form that contains any Covered Software. -**1.11. “Patent Claims” of a Contributor** +**1.11. “Patent Claims” of a Contributor** means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the @@ -78,16 +78,16 @@ Mozilla Public License Version 2.0 made, import, or transfer of either its Contributions or its Contributor Version. -**1.12. “Secondary License”** +**1.12. “Secondary License”** means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. -**1.13. “Source Code Form”** +**1.13. “Source Code Form”** means the form of the work preferred for making modifications. -**1.14. “You” (or “Your”)** +**1.14. “You” (or “Your”)** means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For diff --git a/action.php b/action.php index 3275bd4..3d953b8 100644 --- a/action.php +++ b/action.php @@ -17,6 +17,12 @@ if ($VARS['action'] !== "signout") { dieifnotloggedin(); } +/** + * Redirects back to the page ID in $_POST/$_GET['source'] with the given message ID. + * The message will be displayed by the app. + * @param string $msg message ID (see lang/messages.php) + * @param string $arg If set, replaces "{arg}" in the message string when displayed to the user. + */ function returnToSender($msg, $arg = "") { global $VARS; if ($arg == "") { diff --git a/api.php b/api.php index db6d4ca..3f6a541 100644 --- a/api.php +++ b/api.php @@ -165,4 +165,4 @@ switch ($VARS['action']) { default: header("HTTP/1.1 400 Bad Request"); die("\"400 Bad Request\""); -} \ No newline at end of file +} diff --git a/app.php b/app.php index 55fbe6b..24558d1 100644 --- a/app.php +++ b/app.php @@ -18,6 +18,12 @@ if (!is_empty($_GET['page'])) { $pageid = "404"; } } + +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=script", false); +header("Link: ; rel=preload; as=script", false); ?> @@ -43,6 +49,7 @@ if (!is_empty($_GET['page'])) { if (isset(PAGES[$pageid]['styles'])) { foreach (PAGES[$pageid]['styles'] as $style) { echo "\n"; + header("Link: <$style>; rel=preload; as=style", false); } } ?> @@ -169,8 +176,9 @@ END; if (isset(PAGES[$pageid]['scripts'])) { foreach (PAGES[$pageid]['scripts'] as $script) { echo "\n"; + header("Link: <$script>; rel=preload; as=script", false); } } ?> - \ No newline at end of file + diff --git a/index.php b/index.php index 6a8c7e2..5872a67 100644 --- a/index.php +++ b/index.php @@ -72,6 +72,11 @@ if (checkLoginServer()) { } else { $alert = lang("login server unavailable", false); } +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=script", false); +header("Link: ; rel=preload; as=script", false); ?> diff --git a/lang/en_us.php b/lang/en_us.php index dc167c6..2f49705 100644 --- a/lang/en_us.php +++ b/lang/en_us.php @@ -82,4 +82,4 @@ define("STRINGS", [ "finished on" => "Finished on: {date}", "started on" => "Started on: {date}", "add task" => "Add Task" -]); \ No newline at end of file +]); diff --git a/lib/iputils.php b/lib/iputils.php new file mode 100644 index 0000000..2d03e9a --- /dev/null +++ b/lib/iputils.php @@ -0,0 +1,127 @@ + + */ +function ip4_in_cidr($ip, $cidr) { + if (strpos($cidr, '/') == false) { + $cidr .= '/32'; + } + // $range is in IP/CIDR format eg 127.0.0.1/24 + list( $cidr, $netmask ) = explode('/', $cidr, 2); + $range_decimal = ip2long($cidr); + $ip_decimal = ip2long($ip); + $wildcard_decimal = pow(2, ( 32 - $netmask)) - 1; + $netmask_decimal = ~ $wildcard_decimal; + return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) ); +} + +/** + * Check if a given ipv6 address is in a given cidr + * @param string $ip IP to check in IPV6 format + * @param string $cidr CIDR netmask + * @return boolean true if the IP is in this range, false otherwise. + * @author MW. + */ +function ip6_in_cidr($ip, $cidr) { + $address = inet_pton($ip); + $subnetAddress = inet_pton(explode("/", $cidr)[0]); + $subnetMask = explode("/", $cidr)[1]; + + $addr = str_repeat("f", $subnetMask / 4); + switch ($subnetMask % 4) { + case 0: + break; + case 1: + $addr .= "8"; + break; + case 2: + $addr .= "c"; + break; + case 3: + $addr .= "e"; + break; + } + $addr = str_pad($addr, 32, '0'); + $addr = pack("H*", $addr); + + $binMask = $addr; + return ($address & $binMask) == $subnetAddress; +} + +/** + * Check if the REMOTE_ADDR is on Cloudflare's network. + * @return boolean true if it is, otherwise false + */ +function validateCloudflare() { + if (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + // Using IPv6 + $cloudflare_ips_v6 = [ + "2400:cb00::/32", + "2405:8100::/32", + "2405:b500::/32", + "2606:4700::/32", + "2803:f800::/32", + "2c0f:f248::/32", + "2a06:98c0::/29" + ]; + $valid = false; + foreach ($cloudflare_ips_v6 as $cidr) { + if (ip6_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) { + $valid = true; + break; + } + } + } else { + // Using IPv4 + $cloudflare_ips_v4 = [ + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "104.16.0.0/12", + "108.162.192.0/18", + "131.0.72.0/22", + "141.101.64.0/18", + "162.158.0.0/15", + "172.64.0.0/13", + "173.245.48.0/20", + "188.114.96.0/20", + "190.93.240.0/20", + "197.234.240.0/22", + "198.41.128.0/17" + ]; + $valid = false; + foreach ($cloudflare_ips_v4 as $cidr) { + if (ip4_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) { + $valid = true; + break; + } + } + } + return $valid; +} + +/** + * Makes a good guess at the client's real IP address. + * + * @return string Client IP or `0.0.0.0` if we can't find anything + */ +function getClientIP() { + // If CloudFlare is in the mix, we should use it. + // Check if the request is actually from CloudFlare before trusting it. + if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) { + if (validateCloudflare()) { + return $_SERVER["HTTP_CF_CONNECTING_IP"]; + } + } + + if (isset($_SERVER["REMOTE_ADDR"])) { + return $_SERVER["REMOTE_ADDR"]; + } + + return "0.0.0.0"; // This will not happen unless we aren't a web server +} diff --git a/lib/login.php b/lib/login.php index 825e6e2..83df942 100644 --- a/lib/login.php +++ b/lib/login.php @@ -6,7 +6,7 @@ /** - * Authentication and account functions. Connects to a Portal instance. + * Authentication and account functions. Connects to an AccountHub instance. */ /** diff --git a/lib/userinfo.php b/lib/userinfo.php index 8d63990..65f6b38 100644 --- a/lib/userinfo.php +++ b/lib/userinfo.php @@ -124,4 +124,4 @@ function getManagedUIDs($manageruid) { } else { return []; } -} \ No newline at end of file +} diff --git a/mobile/index.php b/mobile/index.php index 49451ea..50910fa 100644 --- a/mobile/index.php +++ b/mobile/index.php @@ -9,6 +9,10 @@ * Mobile app API */ +// The name of the permission needed to log in. +// Set to null if you don't need it. +$access_permission = "TASKFLOOR"; + require __DIR__ . "/../required.php"; require __DIR__ . "/../lib/login.php"; @@ -93,7 +97,7 @@ switch ($VARS['action']) { if (user_exists($VARS['username'])) { if (get_account_status($VARS['username']) == "NORMAL") { if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) { - if (account_has_permission($VARS['username'], "TASKFLOOR")) { + if (is_null($access_permission) || account_has_permission($VARS['username'], $access_permission)) { doLoginUser($VARS['username'], $VARS['password']); $_SESSION['mobile'] = true; exit(json_encode(["status" => "OK"])); @@ -107,4 +111,4 @@ switch ($VARS['action']) { default: http_response_code(404); die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."])); -} \ No newline at end of file +} diff --git a/pages/404.php b/pages/404.php index 21110a2..1e89266 100644 --- a/pages/404.php +++ b/pages/404.php @@ -9,4 +9,4 @@

- \ No newline at end of file + diff --git a/required.php b/required.php index 94f98c5..c78d2b9 100644 --- a/required.php +++ b/required.php @@ -12,10 +12,12 @@ ob_start(); // allow sending headers after content // Unicode, solves almost all stupid encoding problems header('Content-Type: text/html; charset=utf-8'); -// l33t $ecurity h4x +// Strip PHP version +header('X-Powered-By: PHP'); + +// Security header('X-Content-Type-Options: nosniff'); header('X-XSS-Protection: 1; mode=block'); -header('X-Powered-By: PHP'); // no versions makes it harder to find vulns header('X-Frame-Options: "DENY"'); header('Referrer-Policy: "no-referrer, strict-origin-when-cross-origin"'); $SECURE_NONCE = base64_encode(random_bytes(8)); @@ -80,7 +82,7 @@ function sendError($error) { . "

A fatal application error has occurred.

" . "(This isn't your fault.)" . "

Details:

" - . "

". htmlspecialchars($error) . "

"); + . "

" . htmlspecialchars($error) . "

"); } date_default_timezone_set(TIMEZONE); diff --git a/settings.template.php b/settings.template.php index ce92844..ec2b048 100644 --- a/settings.template.php +++ b/settings.template.php @@ -57,4 +57,4 @@ define('LANGUAGE', "en_us"); define("FOOTER_TEXT", ""); -define("COPYRIGHT_NAME", "Netsyms Technologies"); \ No newline at end of file +define("COPYRIGHT_NAME", "Netsyms Technologies"); diff --git a/static/css/app.css b/static/css/app.css index a33c748..9f903bd 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -122,4 +122,4 @@ body { .easy-autocomplete input { border-radius: 0px !important; -} \ No newline at end of file +} diff --git a/static/js/app.js b/static/js/app.js index 1738ece..0358b3b 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -11,7 +11,7 @@ function setupTooltips() { $(document).ready(function () { /* Fade out alerts */ $(".alert .close").click(function (e) { - $(this).parent().fadeOut('slow'); + $(this).parent().fadeOut("slow"); }); /* Activate tooltips */ @@ -31,4 +31,4 @@ try { window.history.replaceState("", "", getniceurl()); } catch (ex) { -} \ No newline at end of file +}