Merge ssh://source.netsyms.com:2322/Business/BusinessAppTemplate

# Conflicts:
#	.gitignore
#	README.md
#	action.php
#	api.php
#	app.php
#	composer.json
#	composer.lock
#	index.php
#	lang/en_us.php
#	lang/messages.php
#	lib/login.php
#	lib/userinfo.php
#	mobile/index.php
#	nbproject/project.properties
#	nbproject/project.xml
#	pages.php
#	pages/404.php
#	pages/home.php
#	required.php
#	settings.template.php
#	static/css/app.css
#	static/img/logo.png
#	static/img/logo.svg
#	static/js/app.js
master
Skylar Ittner 6 years ago
commit 0593f4c6d4

2
.gitignore vendored

@ -2,4 +2,4 @@ vendor
settings.php settings.php
nbproject/private nbproject/private
database.mwb.bak database.mwb.bak
*.sync-conflict* *.sync-conflict*

@ -1,7 +1,7 @@
BinStack BinStack
======== ========
BinStack is the easy way to keep track of your business assets. BinStack is the easy way to keep track of your business assets.
Never wander in search of the stapler again. Never wander in search of the stapler again.
https://netsyms.biz/apps/binstack https://netsyms.biz/apps/binstack
@ -9,20 +9,20 @@ https://netsyms.biz/apps/binstack
Features Features
-------- --------
**Asset Tracking** **Asset Tracking**
Easily manage locations, tag numbers, assigned users, and other important item Easily manage locations, tag numbers, assigned users, and other important item
properties. properties.
**Fully Searchable** **Fully Searchable**
Start typing in the search box to instantly filter hundreds of results down to Start typing in the search box to instantly filter hundreds of results down to
only a few. Sorting also built in. only a few. Sorting also built in.
**Autofill** **Autofill**
You don't need to remember category codes or usernames. Just start typing and You don't need to remember category codes or usernames. Just start typing and
BinStack will figure out what you're looking for. BinStack will figure out what you're looking for.
**Mobile-ready** **Mobile-ready**
Take inventory or find what you need on the go. BinStack looks and works Take inventory or find what you need on the go. BinStack looks and works
great on modern smartphones and tablets. great on modern smartphones and tablets.
Installing Installing

@ -223,4 +223,4 @@ switch ($VARS['action']) {
session_destroy(); session_destroy();
header('Location: index.php'); header('Location: index.php');
die("Logged out."); die("Logged out.");
} }

@ -38,4 +38,4 @@ switch ($VARS['action']) {
default: default:
header("HTTP/1.1 400 Bad Request"); header("HTTP/1.1 400 Bad Request");
die("\"400 Bad Request\""); die("\"400 Bad Request\"");
} }

@ -18,6 +18,12 @@ if (!is_empty($_GET['page'])) {
$pageid = "404"; $pageid = "404";
} }
} }
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false);
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false);
header("Link: <static/css/app.css>; rel=preload; as=style", false);
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -43,6 +49,7 @@ if (!is_empty($_GET['page'])) {
if (isset(PAGES[$pageid]['styles'])) { if (isset(PAGES[$pageid]['styles'])) {
foreach (PAGES[$pageid]['styles'] as $style) { foreach (PAGES[$pageid]['styles'] as $style) {
echo "<link href=\"$style\" rel=\"stylesheet\">\n"; echo "<link href=\"$style\" rel=\"stylesheet\">\n";
header("Link: <$style>; rel=preload; as=style", false);
} }
} }
?> ?>
@ -169,8 +176,9 @@ END;
if (isset(PAGES[$pageid]['scripts'])) { if (isset(PAGES[$pageid]['scripts'])) {
foreach (PAGES[$pageid]['scripts'] as $script) { foreach (PAGES[$pageid]['scripts'] as $script) {
echo "<script src=\"$script\"></script>\n"; echo "<script src=\"$script\"></script>\n";
header("Link: <$script>; rel=preload; as=script", false);
} }
} }
?> ?>
</body> </body>
</html> </html>

@ -72,6 +72,11 @@ if (checkLoginServer()) {
} else { } else {
$alert = lang("login server unavailable", false); $alert = lang("login server unavailable", false);
} }
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false);
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false);
header("Link: <static/css/index.css>; rel=preload; as=style", false);
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -147,4 +152,4 @@ if (checkLoginServer()) {
<script src="static/js/jquery-3.3.1.min.js"></script> <script src="static/js/jquery-3.3.1.min.js"></script>
<script src="static/js/bootstrap.min.js"></script> <script src="static/js/bootstrap.min.js"></script>
</body> </body>
</html> </html>

@ -0,0 +1,127 @@
<?php
/**
* Check if a given ipv4 address is in a given cidr
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
* @return boolean true if the ip is in this range / false if not.
* @author Thorsten Ott <https://gist.github.com/tott/7684443>
*/
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. <https://stackoverflow.com/a/7952169/2534036>
*/
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
}

@ -40,6 +40,33 @@ function checkLoginServer() {
} }
} }
/**
* Checks if the given AccountHub API key is valid by attempting to
* access the API with it.
* @param String $key The API key to check
* @return boolean TRUE if the key is valid, FALSE if invalid or something went wrong
*/
function checkAPIKey($key) {
try {
$client = new GuzzleHttp\Client();
$response = $client
->request('POST', PORTAL_API, [
'form_params' => [
'key' => $key,
'action' => "ping"
]
]);
if ($response->getStatusCode() === 200) {
return true;
}
return false;
} catch (Exception $e) {
return false;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Account handling // // Account handling //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

@ -124,4 +124,4 @@ function getManagedUIDs($manageruid) {
} else { } else {
return []; return [];
} }
} }

@ -1,14 +1,13 @@
<?php <?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* /*
* Mobile app API * Mobile app API
*/ */
// The name of the permission needed to log in.
// Set to null if you don't need it.
$access_permission = "INV_VIEW";
require __DIR__ . "/../required.php"; require __DIR__ . "/../required.php";
require __DIR__ . "/../lib/login.php"; require __DIR__ . "/../lib/login.php";
@ -93,7 +92,7 @@ switch ($VARS['action']) {
if (user_exists($VARS['username'])) { if (user_exists($VARS['username'])) {
if (get_account_status($VARS['username']) == "NORMAL") { if (get_account_status($VARS['username']) == "NORMAL") {
if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) { if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) {
if (account_has_permission($VARS['username'], "INV_VIEW")) { if (is_null($access_permission) || account_has_permission($VARS['username'], $access_permission)) {
doLoginUser($VARS['username'], $VARS['password']); doLoginUser($VARS['username'], $VARS['password']);
$_SESSION['mobile'] = true; $_SESSION['mobile'] = true;
exit(json_encode(["status" => "OK"])); exit(json_encode(["status" => "OK"]));
@ -107,4 +106,4 @@ switch ($VARS['action']) {
default: default:
http_response_code(404); http_response_code(404);
die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."])); die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."]));
} }

@ -52,4 +52,4 @@ redirectifnotloggedin();
<a href="app.php?page=items&filter=stock" class="text-light"><i class="fas fa-arrow-right"></i> <?php lang("view understocked"); ?></a> <a href="app.php?page=items&filter=stock" class="text-light"><i class="fas fa-arrow-right"></i> <?php lang("view understocked"); ?></a>
</div> </div>
</div> </div>
</div> </div>

@ -12,10 +12,12 @@ ob_start(); // allow sending headers after content
// Unicode, solves almost all stupid encoding problems // Unicode, solves almost all stupid encoding problems
header('Content-Type: text/html; charset=utf-8'); 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-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block'); 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('X-Frame-Options: "DENY"');
header('Referrer-Policy: "no-referrer, strict-origin-when-cross-origin"'); header('Referrer-Policy: "no-referrer, strict-origin-when-cross-origin"');
$SECURE_NONCE = base64_encode(random_bytes(8)); $SECURE_NONCE = base64_encode(random_bytes(8));
@ -142,6 +144,7 @@ function lang($key, $echo = true) {
if (array_key_exists($key, STRINGS)) { if (array_key_exists($key, STRINGS)) {
$str = STRINGS[$key]; $str = STRINGS[$key];
} else { } else {
trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
$str = $key; $str = $key;
} }
@ -164,6 +167,7 @@ function lang2($key, $replace, $echo = true) {
if (array_key_exists($key, STRINGS)) { if (array_key_exists($key, STRINGS)) {
$str = STRINGS[$key]; $str = STRINGS[$key];
} else { } else {
trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
$str = $key; $str = $key;
} }

@ -54,4 +54,4 @@ define('LANGUAGE', "en_us");
define("FOOTER_TEXT", ""); define("FOOTER_TEXT", "");
define("COPYRIGHT_NAME", "Netsyms Technologies"); define("COPYRIGHT_NAME", "Netsyms Technologies");
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////

@ -65,4 +65,4 @@ body {
.mobile-app-display { .mobile-app-display {
display: none; display: none;
} }

@ -23,4 +23,4 @@ try {
window.history.replaceState("", "", getniceurl()); window.history.replaceState("", "", getniceurl());
} catch (ex) { } catch (ex) {
} }

Loading…
Cancel
Save