Browse Source

Merge BusinessAppTemplate

# Conflicts:
#	.gitignore
#	README.md
#	action.php
#	api.php
#	app.php
#	composer.json
#	composer.lock
#	index.php
#	langs/en/core.json
#	langs/en/titles.json
#	langs/messages.php
#	lib/Exceptions.lib.php
#	lib/Login.lib.php
#	lib/Notifications.lib.php
#	lib/Strings.lib.php
#	lib/User.lib.php
#	mobile/index.php
#	nbproject/project.properties
#	nbproject/project.xml
#	pages.php
#	pages/home.php
#	required.php
#	settings.template.php
#	static/css/bootstrap.min.css
#	static/img/logo.svg
#	static/js/app.js
Skylar Ittner 4 months ago
parent
commit
f1f682780c
16 changed files with 264 additions and 22 deletions
  1. 1
    1
      .gitignore
  2. 1
    1
      action.php
  3. 1
    1
      api.php
  4. 1
    1
      app.php
  5. BIN
      database.mwb
  6. 1
    1
      lib/Exceptions.lib.php
  7. 135
    0
      lib/IPUtils.lib.php
  8. 4
    0
      lib/Strings.lib.php
  9. 1
    1
      mobile/index.php
  10. 1
    1
      pages/home.php
  11. 19
    12
      required.php
  12. 1
    1
      settings.template.php
  13. 1
    1
      static/css/bootstrap.min.css
  14. BIN
      static/img/logo.png
  15. 1
    1
      static/js/app.js
  16. 96
    0
      tests/User.test.php

+ 1
- 1
.gitignore View File

@@ -4,4 +4,4 @@
4 4
 /nbproject/private
5 5
 *.sync-conflict*
6 6
 test*
7
-/conf/
7
+/conf/

+ 1
- 1
action.php View File

@@ -117,4 +117,4 @@ switch ($VARS['action']) {
117 117
         $database->delete('userkeys', ['AND' => ['uid' => $_SESSION['uid'], 'typeid' => 1]]);
118 118
         returnToSender("feed_key_reset");
119 119
         break;
120
-}
120
+}

+ 1
- 1
api.php View File

@@ -439,4 +439,4 @@ switch ($VARS['action']) {
439 439
     default:
440 440
         http_response_code(404);
441 441
         die(json_encode("404 Not Found: the requested action is not available."));
442
-}
442
+}

+ 1
- 1
app.php View File

@@ -194,4 +194,4 @@ END;
194 194
         }
195 195
         ?>
196 196
     </body>
197
-</html>
197
+</html>

BIN
database.mwb View File


+ 1
- 1
lib/Exceptions.lib.php View File

@@ -32,4 +32,4 @@ class PasswordMismatchException extends Exception {
32 32
         parent::__construct($message, $code, $previous);
33 33
     }
34 34
 
35
-}
35
+}

+ 135
- 0
lib/IPUtils.lib.php View File

@@ -0,0 +1,135 @@
1
+<?php
2
+
3
+/* This Source Code Form is subject to the terms of the Mozilla Public
4
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
+
7
+class IPUtils {
8
+
9
+    /**
10
+     * Check if a given ipv4 address is in a given cidr
11
+     * @param  string $ip    IP to check in IPV4 format eg. 127.0.0.1
12
+     * @param  string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
13
+     * @return boolean true if the ip is in this range / false if not.
14
+     * @author Thorsten Ott <https://gist.github.com/tott/7684443>
15
+     */
16
+    public static function ip4_in_cidr($ip, $cidr) {
17
+        if (strpos($cidr, '/') == false) {
18
+            $cidr .= '/32';
19
+        }
20
+        // $range is in IP/CIDR format eg 127.0.0.1/24
21
+        list( $cidr, $netmask ) = explode('/', $cidr, 2);
22
+        $range_decimal = ip2long($cidr);
23
+        $ip_decimal = ip2long($ip);
24
+        $wildcard_decimal = pow(2, ( 32 - $netmask)) - 1;
25
+        $netmask_decimal = ~ $wildcard_decimal;
26
+        return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
27
+    }
28
+
29
+    /**
30
+     * Check if a given ipv6 address is in a given cidr
31
+     * @param string $ip IP to check in IPV6 format
32
+     * @param string $cidr CIDR netmask
33
+     * @return boolean true if the IP is in this range, false otherwise.
34
+     * @author MW. <https://stackoverflow.com/a/7952169>
35
+     */
36
+    public static function ip6_in_cidr($ip, $cidr) {
37
+        $address = inet_pton($ip);
38
+        $subnetAddress = inet_pton(explode("/", $cidr)[0]);
39
+        $subnetMask = explode("/", $cidr)[1];
40
+
41
+        $addr = str_repeat("f", $subnetMask / 4);
42
+        switch ($subnetMask % 4) {
43
+            case 0:
44
+                break;
45
+            case 1:
46
+                $addr .= "8";
47
+                break;
48
+            case 2:
49
+                $addr .= "c";
50
+                break;
51
+            case 3:
52
+                $addr .= "e";
53
+                break;
54
+        }
55
+        $addr = str_pad($addr, 32, '0');
56
+        $addr = pack("H*", $addr);
57
+
58
+        $binMask = $addr;
59
+        return ($address & $binMask) == $subnetAddress;
60
+    }
61
+
62
+    /**
63
+     * Check if the REMOTE_ADDR is on Cloudflare's network.
64
+     * @return boolean true if it is, otherwise false
65
+     */
66
+    public static function validateCloudflare() {
67
+        if (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
68
+            // Using IPv6
69
+            $cloudflare_ips_v6 = [
70
+                "2400:cb00::/32",
71
+                "2405:8100::/32",
72
+                "2405:b500::/32",
73
+                "2606:4700::/32",
74
+                "2803:f800::/32",
75
+                "2c0f:f248::/32",
76
+                "2a06:98c0::/29"
77
+            ];
78
+            $valid = false;
79
+            foreach ($cloudflare_ips_v6 as $cidr) {
80
+                if (ip6_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
81
+                    $valid = true;
82
+                    break;
83
+                }
84
+            }
85
+        } else {
86
+            // Using IPv4
87
+            $cloudflare_ips_v4 = [
88
+                "103.21.244.0/22",
89
+                "103.22.200.0/22",
90
+                "103.31.4.0/22",
91
+                "104.16.0.0/12",
92
+                "108.162.192.0/18",
93
+                "131.0.72.0/22",
94
+                "141.101.64.0/18",
95
+                "162.158.0.0/15",
96
+                "172.64.0.0/13",
97
+                "173.245.48.0/20",
98
+                "188.114.96.0/20",
99
+                "190.93.240.0/20",
100
+                "197.234.240.0/22",
101
+                "198.41.128.0/17"
102
+            ];
103
+            $valid = false;
104
+            foreach ($cloudflare_ips_v4 as $cidr) {
105
+                if (ip4_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
106
+                    $valid = true;
107
+                    break;
108
+                }
109
+            }
110
+        }
111
+        return $valid;
112
+    }
113
+
114
+    /**
115
+     * Makes a good guess at the client's real IP address.
116
+     *
117
+     * @return string Client IP or `0.0.0.0` if we can't find anything
118
+     */
119
+    public static function getClientIP() {
120
+        // If CloudFlare is in the mix, we should use it.
121
+        // Check if the request is actually from CloudFlare before trusting it.
122
+        if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
123
+            if (validateCloudflare()) {
124
+                return $_SERVER["HTTP_CF_CONNECTING_IP"];
125
+            }
126
+        }
127
+
128
+        if (isset($_SERVER["REMOTE_ADDR"])) {
129
+            return $_SERVER["REMOTE_ADDR"];
130
+        }
131
+
132
+        return "0.0.0.0"; // This will not happen unless we aren't a web server
133
+    }
134
+
135
+}

+ 4
- 0
lib/Strings.lib.php View File

@@ -21,6 +21,10 @@ class Strings {
21 21
 
22 22
         $this->load("en");
23 23
 
24
+        if ($language == "en") {
25
+            return;
26
+        }
27
+
24 28
         if (file_exists(__DIR__ . "/../langs/$language/")) {
25 29
             $this->language = $language;
26 30
             $this->load($language);

+ 1
- 1
mobile/index.php View File

@@ -241,4 +241,4 @@ switch ($VARS['action']) {
241 241
     default:
242 242
         http_response_code(404);
243 243
         die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."]));
244
-}
244
+}

+ 1
- 1
pages/home.php View File

@@ -153,4 +153,4 @@
153 153
         <?php
154 154
     }
155 155
     ?>
156
-</div>
156
+</div>

+ 19
- 12
required.php View File

@@ -8,31 +8,30 @@
8 8
  * This file contains global settings and utility functions.
9 9
  */
10 10
 ob_start(); // allow sending headers after content
11
-//
12
-// Composer
13
-require __DIR__ . '/vendor/autoload.php';
14
-
15 11
 // Settings file
16 12
 require __DIR__ . '/settings.php';
17 13
 
18 14
 // Unicode, solves almost all stupid encoding problems
19 15
 header('Content-Type: text/html; charset=utf-8');
20 16
 
21
-// l33t $ecurity h4x
17
+// Strip PHP version
18
+header('X-Powered-By: PHP');
19
+
20
+// Security
22 21
 header('X-Content-Type-Options: nosniff');
23 22
 header('X-XSS-Protection: 1; mode=block');
24
-header('X-Powered-By: PHP'); // no versions makes it harder to find vulns
25 23
 header('X-Frame-Options: "DENY"');
26 24
 header('Referrer-Policy: "no-referrer, strict-origin-when-cross-origin"');
27 25
 $SECURE_NONCE = base64_encode(random_bytes(8));
28 26
 
29 27
 
30
-$session_length = 60 * 60; // 1 hour
28
+$session_length = 60 * 60 * 1; // 1 hour
29
+ini_set('session.gc_maxlifetime', $session_length);
31 30
 session_set_cookie_params($session_length, "/", null, false, false);
32 31
 
33 32
 session_start(); // stick some cookies in it
34
-//// renew session cookie
35
-setcookie(session_name(), session_id(), time() + $session_length);
33
+// renew session cookie
34
+setcookie(session_name(), session_id(), time() + $session_length, "/", false, false);
36 35
 
37 36
 $captcha_server = (CAPTCHA_ENABLED === true ? preg_replace("/http(s)?:\/\//", "", CAPTCHA_SERVER) : "");
38 37
 if ($_SESSION['mobile'] === TRUE) {
@@ -44,7 +43,7 @@ if ($_SESSION['mobile'] === TRUE) {
44 43
             . "frame-src 'none'; "
45 44
             . "font-src 'self'; "
46 45
             . "connect-src *; "
47
-            . "style-src 'self' 'unsafe-inline'; "
46
+            . "style-src 'self' 'unsafe-inline' $captcha_server; "
48 47
             . "script-src 'self' 'unsafe-inline' $captcha_server");
49 48
 } else {
50 49
     header("Content-Security-Policy: "
@@ -55,10 +54,14 @@ if ($_SESSION['mobile'] === TRUE) {
55 54
             . "frame-src 'none'; "
56 55
             . "font-src 'self'; "
57 56
             . "connect-src *; "
58
-            . "style-src 'self' 'nonce-$SECURE_NONCE'; "
57
+            . "style-src 'self' 'nonce-$SECURE_NONCE' $captcha_server; "
59 58
             . "script-src 'self' 'nonce-$SECURE_NONCE' $captcha_server");
60 59
 }
61 60
 
61
+//
62
+// Composer
63
+require __DIR__ . '/vendor/autoload.php';
64
+
62 65
 // List of alert messages
63 66
 require __DIR__ . '/langs/messages.php';
64 67
 
@@ -69,6 +72,10 @@ foreach ($libs as $lib) {
69 72
 
70 73
 $Strings = new Strings(LANGUAGE);
71 74
 
75
+/**
76
+ * Kill off the running process and spit out an error message
77
+ * @param string $error error message
78
+ */
72 79
 function sendError($error) {
73 80
     global $SECURE_NONCE;
74 81
     die("<!DOCTYPE html>"
@@ -379,4 +386,4 @@ function engageRateLimit() {
379 386
         // Add a record for the IP address
380 387
         $database->insert('rate_limit', ["ipaddr" => getClientIP(), "lastaction" => date("Y-m-d H:i:s")]);
381 388
     }
382
-}
389
+}

+ 1
- 1
settings.template.php View File

@@ -166,4 +166,4 @@ define("QUERY_LIMIT", 1000);
166 166
 
167 167
 define("FOOTER_TEXT", "");
168 168
 define("COPYRIGHT_NAME", "Netsyms Technologies");
169
-//////////////////////////////////////////////////////////////
169
+//////////////////////////////////////////////////////////////

+ 1
- 1
static/css/bootstrap.min.css
File diff suppressed because it is too large
View File


BIN
static/img/logo.png View File


+ 1
- 1
static/js/app.js View File

@@ -80,4 +80,4 @@ try {
80 80
     window.history.replaceState("", "", getniceurl());
81 81
 } catch (ex) {
82 82
 
83
-}
83
+}

+ 96
- 0
tests/User.test.php View File

@@ -0,0 +1,96 @@
1
+<?php
2
+
3
+/*
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+// Fill these in with valid credentials for an account with NORMAL status
10
+$valid_user = "";
11
+$valid_pass = "";
12
+
13
+require __DIR__ . "/../required.php";
14
+error_reporting(E_ALL);
15
+ini_set('display_errors', 'On');
16
+header("Content-Type: text/plain");
17
+
18
+// Test invalid user responses
19
+
20
+$user = new User(784587254);
21
+if ($user->exists()) {
22
+    echo "FAIL: Invalid user ID marked as existing\n";
23
+} else {
24
+    echo "OK\n";
25
+}
26
+if ($user->getUID() != 784587254) {
27
+    echo "FAIL: Invalid user has mismatched UID\n";
28
+} else {
29
+    echo "OK\n";
30
+}
31
+
32
+$user = User::byUsername("r9483yt8934t");
33
+if ($user->exists()) {
34
+    echo "FAIL: Invalid username marked as existing\n";
35
+} else {
36
+    echo "OK\n";
37
+}
38
+
39
+if ($user->checkPassword("gbirg4wre") != false) {
40
+    echo "FAIL: Invalid user and invalid password allowed\n";
41
+} else {
42
+    echo "OK\n";
43
+}
44
+
45
+if ($user->has2fa() != false) {
46
+    echo "FAIL: Invalid user has 2fa\n";
47
+} else {
48
+    echo "OK\n";
49
+}
50
+
51
+if ($user->getUsername() != "r9483yt8934t") {
52
+    echo "FAIL: Invalid user has mismatched username\n";
53
+} else {
54
+    echo "OK\n";
55
+}
56
+
57
+if ($user->getStatus()->get() != 0) {
58
+    echo "FAIL: Invalid user has real account status\n";
59
+} else {
60
+    echo "OK\n";
61
+}
62
+
63
+if ($user->getStatus()->getString() != "OTHER_0") {
64
+    echo "FAIL: Invalid user has wrong account status string\n";
65
+} else {
66
+    echo "OK\n";
67
+}
68
+
69
+// Test valid user responses
70
+
71
+$user = User::byUsername($valid_user);
72
+if (!$user->exists()) {
73
+    echo "FAIL: Valid user does not exist\n";
74
+} else {
75
+    echo "OK\n";
76
+}
77
+
78
+if ($user->checkPassword($valid_pass) !== true) {
79
+    echo "FAIL: Valid user and password not allowed\n";
80
+} else {
81
+    echo "OK\n";
82
+}
83
+
84
+if ($user->getUsername() != $valid_user) {
85
+    echo "FAIL: Valid user has mismatched username\n";
86
+} else {
87
+    echo "OK\n";
88
+}
89
+
90
+if ($user->getStatus()->getString() != "NORMAL") {
91
+    echo "FAIL: Valid user has wrong account status string\n";
92
+} else {
93
+    echo "OK\n";
94
+}
95
+
96
+exit("ALL OK");

Loading…
Cancel
Save