Subversion Repositories web_pages

Rev

Rev 13 | Rev 15 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 13 Rev 14
Line 1... Line 1...
1
<?php
1
<?php
2
 
-
 
3
/**
2
/**
4
     Copyright (c) 2025, Daily Data, Inc.
3
 * Copyright (c) 2025, Daily Data, Inc.
-
 
4
 * All rights reserved.
5
 
5
 *
6
    Redistribution and use in source and binary forms, with or without modification,
6
 * Redistribution and use in source and binary forms, with or without modification,
7
    are permitted provided that the following conditions are met:
7
 * are permitted provided that the following conditions are met:
8
 
8
 *
9
        Redistributions of source code must retain the above copyright notice, this
9
 * 1. Redistributions of source code must retain the above copyright notice, this list
10
           list of conditions and the following disclaimer.
10
 *    of conditions and the following disclaimer.
11
        Redistributions in binary form must reproduce the above copyright notice, 
11
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
12
           this list of conditions and the following disclaimer in the documentation
12
 *    list of conditions and the following disclaimer in the documentation and/or other
13
           and/or other materials provided with the distribution.
13
 *    materials provided with the distribution.
-
 
14
 * 3. Neither the name of Daily Data, Inc. nor the names of its contributors may be
-
 
15
 *    used to endorse or promote products derived from this software without specific
-
 
16
 *    prior written permission.
14
 
17
 *
15
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
16
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
18
    IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
    OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-
 
24
    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
25
 
-
 
26
    PHP script which reads tab delimited file users.csv (defined in $csvFile). Users
-
 
27
    are presented with a username and password box and a router selector.
-
 
28
    When processed, will read data file and determine if the credentials match any line
-
 
29
    and, if so, display a QR Code suitable for scanning with one time password program.
-
 
30
 
-
 
31
    Assumes csv file and qr image created by processOPNSense.pl.
-
 
32
 
-
 
33
    Version 1.0.0 RWR 2025-09-21
-
 
34
       Initial Release
27
 * DAMAGE.
35
    Version 1.1.0 RWR 2025-09-27
-
 
36
       Added capability of downloading VPN configuration file
-
 
37
 
-
 
38
 */
28
 */
39
 
29
 
40
// Define the path to the CSV file
-
 
41
$csvFile = 'users.csv';
-
 
42
// Define the path to the .ovpn files, if applicable
-
 
43
$ovpnDir = 'ovpn_files/';
-
 
44
$ovpnRegex = 'mcnoc_([a-z0-9]+)\.ovpn';
-
 
45
$ovpnFileName = '';
-
 
46
// name of image file
-
 
47
$imageFileName = '';
-
 
48
// topt code
-
 
49
$code = '';
-
 
50
 
-
 
51
 
-
 
52
function csvToArray( $csvFile, $delimiter = "\t" ) {
-
 
53
   // Initialize an array to hold the data
-
 
54
   $dataArray = [];
-
 
55
 
-
 
56
   // Open the CSV file for reading
-
 
57
   if (($handle = fopen($csvFile, 'r')) !== FALSE) {
-
 
58
       // Get the headers from the first row
-
 
59
       $headers = fgetcsv($handle, 1000, $delimiter);
-
 
60
 
-
 
61
       // Loop through each row in the CSV
-
 
62
       while (($data = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
-
 
63
           // Combine headers with data to create an associative array
-
 
64
           $rowData = array_combine($headers, $data);
-
 
65
           // Add the associative array to the main data array
-
 
66
           $dataArray[] = $rowData;
-
 
67
       }
-
 
68
       fclose($handle);
-
 
69
   } else {
-
 
70
       echo "Could not open the CSV file for reading.";
-
 
71
   }
30
/**
72
   return $dataArray;
-
 
73
} // csvToArray
-
 
74
 
-
 
75
 
31
 
-
 
32
   PHP script which reads configuration file containing user information. Users
-
 
33
   are presented with a username and password box and a router selector.
-
 
34
   When processed, will read data file and determine if the credentials match any line
-
 
35
   and
-
 
36
   * display a QR Code suitable for scanning with one time authentication app
-
 
37
   * display the OTP seed code in case user wants to enter it manually
-
 
38
   * provide a link to download the OpenVPN configuration file for that user
-
 
39
 
76
function ovpnFile ( $path, $username, $ovpnRegex ) {
40
   Assumes configuration file generated by loadOpnSense.pl
-
 
41
   Assumes QR code images are pre-generated and stored in a web accessible location
-
 
42
   Assumes OpenVPN configuration files are pre-generated and stored in a web accessible location
-
 
43
   Assumes passwords are hashed using password_hash function
-
 
44
 
-
 
45
   The configuration file is a json file with the following structure
-
 
46
   {
-
 
47
      "router1" : {
77
   if ( $files = scandir( $path ) ) {
48
         "users" : {
78
      foreach ($files as $key => $value ) {
49
            "user1" : {
79
         if ( preg_match( "/$ovpnRegex/", $value, $matches ) ) {
50
               "password" : "<hashed password>",
80
            if ( $matches[1] == $username ) {
51
               "otp_seed" : "<otp seed>",
81
               return $path . $matches[0];
52
               "qrFile" : "<path to qr code image file>",
-
 
53
               "ovpnFile" : "<path to openvpn config file>"
-
 
54
            },
-
 
55
            "user2" : {
-
 
56
               ...
82
            }
57
            }
83
         }
58
         }
-
 
59
      },
-
 
60
      "router2" : {
-
 
61
         ...
84
      }
62
      }
85
   } else {
-
 
86
      die( "Error trying to scan directory $path\n" );
-
 
87
   }
63
   }
-
 
64
 
-
 
65
   Version 1.0.0 RWR 2025-09-21
88
   return '';
66
      Initial Release
-
 
67
   Version 1.1.0 RWR 2025-09-27
-
 
68
      Added capability of downloading VPN configuration file
-
 
69
   Version 1.2.0 RWR 2025-10-01
-
 
70
     Reading from json file instead of csv
-
 
71
     Removed dependency on exec and external commands
-
 
72
     Added logging of successful logins
-
 
73
     automatically generate router list from config file
-
 
74
     Added some error checking for missing qr code image file and ovpn file
-
 
75
 
-
 
76
 */
-
 
77
 
-
 
78
// this is the only variable you may need to change
-
 
79
// everything else is in the configuration file
-
 
80
$configFile = './users.json';
-
 
81
 
-
 
82
// we need the config data for form processing and validation, so always load it
-
 
83
if ( file_exists( $configFile ) ) {
-
 
84
   $configData = json_decode( file_get_contents( $configFile ), true );
89
} // ovpnFile
85
} else {
-
 
86
   die( "Configuration file $configFile not found" );
-
 
87
}
90
 
88
 
91
// Check if the form is submitted
89
// Check if the form is submitted
92
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
90
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
93
    // Get the username and password from the form
91
   // Get the username and password from the form
94
    $username = $_POST['username'];
92
    $username = $_POST['username'];
95
    $password = $_POST['password'];
93
    $password = $_POST['password'];
96
    $router = $_POST['router'];
94
    $router = $_POST['router'];
97
 
95
 
98
    $users = csvToArray( $csvFile, "\t" );
-
 
99
    if ( $users ) {
96
    if ( $configData ) {
100
       $isValidUser = false;
97
       $isValidUser = false;
101
       foreach ( $users as $key => $data ) {
98
      if ( isset( $configData[$router]) && isset( $configData[$router]['users'][$username] ) ) {
102
          if ( $data['name'] === $username && $data['router'] === $router ) {
99
         $data = $configData[$router]['users'][$username];
103
             if ( password_verify( $password, $data['password'] ) ) {
100
         if ( password_verify( $password, $data['password'] ) ) {
104
                $code = $data['otp'];
101
            $isValidUser = true;
105
                $imageFileName = $data['filename'];
102
            $code = $data['otp_seed'] ?? 'unknown';
106
                $isValidUser = true;
103
            $imageFileName = $data['qrFile'];
107
                $ovpnFileName = ovpnFile( $ovpnDir, $data['name'], $ovpnRegex );
104
            $ovpnFileName = $data['ovpnFile'];
108
                $log = date("Y-m-d H:i:s") . "\t" . $_SERVER['REMOTE_ADDR'] . "\t" .
105
            $log = date("Y-m-d H:i:s") . "\t" . $_SERVER['REMOTE_ADDR'] . "\t" .
109
                  "Success\t" . $username."\t" .PHP_EOL;
106
            "Success\t" . $username."\t" .PHP_EOL;
110
                file_put_contents( './log_'.date("j.n.Y").'.log', $log, FILE_APPEND );
107
            file_put_contents( './log_'.date("j.n.Y").'.log', $log, FILE_APPEND );
111
             }
-
 
112
             break;
-
 
113
         }
108
         }
114
      }
109
      }
115
      if ( ! $isValidUser )
-
 
116
         echo "<h1>Password wrong, or invalid user $username for router $router</h1>";
-
 
117
    } else {
110
   } else {
118
        echo '<h1>Could not open the CSV file.</h1>';
111
      print '<h1>Could not open the configuration file.</h1>';
119
    }
112
   }
-
 
113
   if ( ! $isValidUser )
-
 
114
         print "<h1>Password wrong, or invalid user $username for router $router</h1>";
120
}
115
} // if form submitted
121
 
116
 
122
?>
117
?>
123
 
118
 
124
<!DOCTYPE html>
119
<!DOCTYPE html>
125
<html lang="en">
120
<html lang="en">
Line 129... Line 124...
129
    <title>Get QR Code</title>
124
    <title>Get QR Code</title>
130
</head>
125
</head>
131
<body>
126
<body>
132
    <?php
127
    <?php
133
       if ( ! empty( $imageFileName ) && ! empty( $code ) ) {
128
       if ( ! empty( $imageFileName ) && ! empty( $code ) ) {
134
          echo "<div style='text-align: center;'>";
129
         print "<div style='text-align: center;'>";
-
 
130
         if ( empty($imageFileName) || !file_exists($imageFileName)) {
-
 
131
            print "<p><b>QR Code image file not found</b></p>";
-
 
132
         } else {
-
 
133
            // all good
135
          echo "<img src='$imageFileName' alt='$code'>";
134
            print "<img src='$imageFileName' alt='$code'>";
-
 
135
         }
136
          echo "<br />Your code for $router is<br /><b>$code</b>";
136
         print "<br />Your code for $router is<br /><b>$code</b>";
137
          if ( !empty( $ovpnFileName ) ) {
137
         if ( empty( $ovpnFileName ) ) {
-
 
138
            print "<br />No OpenVPN configuration file available";
-
 
139
         } else {
138
             echo "<br /><a href='$ovpnFileName' download>Download your OpenVPN Config File</a>";
140
            print "<br /><a href='$ovpnFileName' download>Download your OpenVPN Config File</a>";
139
          }
141
         }
140
          echo '</div>';
142
         print '</div>';
141
       }
143
      }
142
    ?>
144
    ?>
143
    <p>This page is updated hourly. If change your password, it will not be reflected here for an hour</p>
145
    <p>This page is updated hourly. If change your password, it will not be reflected here for an hour</p>
144
    <form method="POST" action="">
146
    <form method="POST" action="">
145
        <label for="username">Username:</label>
147
        <label for="username">Username:</label>
146
        <input type="text" id="username" name="username" required>
148
        <input type="text" id="username" name="username" required>
Line 149... Line 151...
149
        <input type="password" id="password" name="password" required>
151
        <input type="password" id="password" name="password" required>
150
        <br>
152
        <br>
151
        <label for="router">Router:</label>
153
        <label for="router">Router:</label>
152
         <select name="router" id="router">
154
         <select name="router" id="router">
153
             <?php
155
             <?php
154
                // gets a list of all routers listed in $csvFile into array $routers
156
               // gets a list of all routers listed in $csvFile into array $routers
155
                exec( "tail -n+2 $csvFile | cut -f1 | sort | uniq | sort", $routers );
157
               $routers = array_keys( $configData );
156
                #die( "<pre>" . print_r( $routers, true) . "</pre>" );
158
                #die( "<pre>" . print_r( $routers, true) . "</pre>" );
157
                foreach ( $routers as $index => $name ) {
159
                foreach ( $routers as $index => $name ) {
158
                   print "<option value='$name'>$name</option>\n";
160
                   print "<option value='$name'>$name</option>\n";
159
                }
161
                }
160
            ?>
162
            ?>