Merge pull request #197 from DL6ER/auth

Add server-side password protection for the web interface
This commit is contained in:
Mcat12
2016-11-23 12:38:24 -05:00
committed by GitHub
10 changed files with 234 additions and 52 deletions
+19 -6
View File
@@ -1,13 +1,18 @@
<?php
$api = true;
require "php/password.php";
include('data.php');
header('Content-type: application/json');
$data = array();
// Works without authorization
if (isset($_GET['summaryRaw'])) {
$data = array_merge($data, getSummaryData());
}
// Works without authorization
if (isset($_GET['summary']) || !count($_GET)) {
$sum = getSummaryData();
$sum['ads_blocked_today'] = number_format( $sum['ads_blocked_today']);
@@ -17,37 +22,45 @@
$data = array_merge($data, $sum);
}
// Works without authorization
if (isset($_GET['overTimeData'])) {
$data = array_merge($data, getOverTimeData());
}
// Works without authorization
if (isset($_GET['overTimeData10mins'])) {
$data = array_merge($data, getOverTimeData10mins());
}
if (isset($_GET['topItems'])) {
// Requires authorization
if (isset($_GET['topItems']) && $auth) {
$data = array_merge($data, getTopItems());
}
if (isset($_GET['recentItems'])) {
// Requires authorization
if (isset($_GET['recentItems']) && $auth) {
if (is_numeric($_GET['recentItems'])) {
$data = array_merge($data, getRecentItems($_GET['recentItems']));
}
}
if (isset($_GET['getQueryTypes'])) {
// Requires authorization
if (isset($_GET['getQueryTypes']) && $auth) {
$data = array_merge($data, getIpvType());
}
if (isset($_GET['getForwardDestinations'])) {
// Requires authorization
if (isset($_GET['getForwardDestinations']) && $auth) {
$data = array_merge($data, getForwardDestinations());
}
if (isset($_GET['getQuerySources'])) {
// Requires authorization
if (isset($_GET['getQuerySources']) && $auth) {
$data = array_merge($data, getQuerySources());
}
if (isset($_GET['getAllQueries'])) {
// Requires authorization
if (isset($_GET['getAllQueries']) && $auth) {
$data = array_merge($data, getAllQueries());
}
+54 -2
View File
@@ -1,9 +1,11 @@
<?php
if (isset($_GET['enable'])) {
require "php/password.php";
if (isset($_GET['enable']) && $auth) {
exec('sudo pihole enable');
$refer = $_SERVER['HTTP_REFERER'];
header("location:$refer");
} elseif (isset($_GET['disable'])) {
} elseif (isset($_GET['disable']) && $auth) {
exec('sudo pihole disable');
$refer = $_SERVER['HTTP_REFERER'];
header("location:$refer");
@@ -44,6 +46,9 @@
$used = $mem[2] - $mem[5] - $mem[6];
$total = $mem[1];
$memory_usage = $used/$total*100;
// For session timer
$maxlifetime = ini_get("session.gc_maxlifetime");
?>
<!DOCTYPE html>
@@ -134,6 +139,7 @@
<div class="col-xs-4 text-center">
<a href="https://github.com/pi-hole/pi-hole/releases">Updates</a>
</div>
<div class="col-xs-12 text-center" id="sessiontimer">Session is valid for <span id="sessiontimercounter"><?php if($auth && strlen($pwhash) > 0){echo $maxlifetime;}else{echo "0";} ?></span></div>
</li>
<!-- Menu Footer -->
<li class="user-footer">
@@ -232,6 +238,7 @@
<i class="fa fa-home"></i> <span>Main Page</span>
</a>
</li>
<?php if($auth){ ?>
<!-- Query Log -->
<li>
<a href="queries.php">
@@ -270,18 +277,41 @@
echo ' <li><a href="?enable"><i class="fa fa-play"></i> <span>Enable</span></a></li>';
}
?>
<!-- Logout -->
<?php
// Show Logout button if $auth is set and authorization is required
if(strlen($pwhash) > 0) { ?>
<li>
<a href="index.php?logout">
<i class="fa fa-user-times"></i> <span>Logout</span>
</a>
</li>
<?php } ?>
<?php } ?>
<!-- Login -->
<?php
// Show Login button if $auth is *not* set and authorization is required
if(strlen($pwhash) > 0 && !$auth) { ?>
<li>
<a href="index.php?login">
<i class="fa fa-user"></i> <span>Login</span>
</a>
</li>
<?php } ?>
<!-- Donate -->
<li>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">
<i class="fa fa-paypal"></i> <span>Donate</span>
</a>
</li>
<?php if($auth){ ?>
<!-- Help -->
<li>
<a href="help.php">
<i class="fa fa-question-circle"></i> <span>Help</span>
</a>
</li>
<?php } ?>
</ul>
</section>
<!-- /.sidebar -->
@@ -290,3 +320,25 @@
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
<?php
// If password is not equal to the password set
// in the setupVars.conf file, then we skip any
// content and just complete the page. If no
// password is set at all, we keep the current
// behavior: everything is always authorized
// and will be displayed
//
// If auth is required and not set, i.e. no successfully logged in,
// we show the reduced version of the summary (index) page
if(!$auth && (!isset($indexpage) || isset($_GET['login']))){ ?>
<div class="page-header">
<h1>Login required!</h1>
</div>
<form action="" method="POST">
Password: <input type="password" name="pw">&nbsp;<input type="submit" value="Login">
</form>
<?php
require "footer.php";
exit();
}
?>
+8 -2
View File
@@ -1,4 +1,5 @@
<?php
$indexpage = true;
require "header.php";
?>
<!-- Small boxes (Stat box) -->
@@ -75,7 +76,12 @@
</div>
</div>
</div>
<?php
// If the user is logged in, then we show the more detailed index page.
// Even if we would include them here anyhow, there would be nothing to
// show since the API will respect the privacy of the user if he defines
// a password
if($auth){ ?>
<div class="row">
<div class="col-md-6">
<div class="box" id="query-types">
@@ -195,7 +201,7 @@
<!-- /.col -->
</div>
<!-- /.row -->
<?php } ?>
<?php
require "footer.php";
?>
+39
View File
@@ -55,3 +55,42 @@ $.getJSON("https://api.github.com/repos/pi-hole/AdminLTE/releases/latest", funct
*/
if(piholeVersion !== "vDev" && versionCompare(piholeVersion, "v2.7") < 0)
alert("Pi-hole needs to be updated to at least v2.7 before you can use features such as whitelisting/blacklisting from this web interface!")
// Session timer
var sessionvalidity = parseInt(document.getElementById("sessiontimercounter").textContent);
var start = new Date;
function updateSessionTimer()
{
start = new Date;
start.setSeconds(start.getSeconds() + sessionvalidity);
}
if(sessionvalidity > 0)
{
// setSeconds will correctly handle wrap-around cases
updateSessionTimer();
setInterval(function() {
var current = new Date;
var totalseconds = (start - current) / 1000;
// var hours = Math.floor(totalseconds / 3600);
// totalseconds = totalseconds % 3600;
var minutes = Math.floor(totalseconds / 60);
if(minutes < 10){ minutes = "0" + minutes; }
var seconds = Math.floor(totalseconds % 60);
if(seconds < 10){ seconds = "0" + seconds; }
if(totalseconds > 0)
document.getElementById("sessiontimercounter").textContent = minutes + ":" + seconds;
else
document.getElementById("sessiontimercounter").textContent = "-- : --";
}, 1000);
}
else
{
document.getElementById("sessiontimer").style.display = "none";
}
+1 -1
View File
@@ -1,3 +1,3 @@
// Remove JS warning
var jswarn = document.getElementById("js-warn-exit");
jswarn.parentNode.removeChild(jswarn);
jswarn.parentNode.removeChild(jswarn);
+58 -41
View File
@@ -24,7 +24,7 @@ $(document).ready(function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
}
};
var animate = false;
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
timeLineChart = new Chart(ctx, {
type: "line",
@@ -96,55 +96,70 @@ $(document).ready(function() {
}
});
ctx = document.getElementById("queryTypeChart").getContext("2d");
queryTypeChart = new Chart(ctx, {
type: "doughnut",
data: {
labels: [],
datasets: [{ data: [] }]
},
options: {
legend: {
display: false
},
animation: {
duration: 2000
},
cutoutPercentage: 0
}
});
ctx = document.getElementById("forwardDestinationChart").getContext("2d");
forwardDestinationChart = new Chart(ctx, {
type: "doughnut",
data: {
labels: [],
datasets: [{ data: [] }]
},
options: {
legend: {
display: false
},
animation: {
duration: 2000
},
cutoutPercentage: 0
}
});
// Pull in data via AJAX
updateSummaryData();
updateQueriesOverTime();
updateQueryTypes();
// Create / load "Query Types" only if authorized
if(!!document.getElementById("queryTypeChart"))
{
ctx = document.getElementById("queryTypeChart").getContext("2d");
queryTypeChart = new Chart(ctx, {
type: "doughnut",
data: {
labels: [],
datasets: [{ data: [] }]
},
options: {
legend: {
display: false
},
animation: {
duration: 2000
},
cutoutPercentage: 0
}
});
updateQueryTypes();
}
updateTopClientsChart();
// Create / load "Forward Destinations" only if authorized
if(!!document.getElementById("forwardDestinationChart"))
{
ctx = document.getElementById("forwardDestinationChart").getContext("2d");
forwardDestinationChart = new Chart(ctx, {
type: "doughnut",
data: {
labels: [],
datasets: [{ data: [] }]
},
options: {
legend: {
display: false
},
animation: {
duration: 2000
},
cutoutPercentage: 0
}
});
updateForwardDestinations();
}
updateForwardDestinations();
// Create / load "Top Domains" and "Top Advertisers" only if authorized
if(!!document.getElementById("domain-frequency")
&& !!document.getElementById("ad-frequency"))
{
updateTopLists();
}
updateTopLists();
// Create / load "Top Clients" only if authorized
if(!!document.getElementById("client-frequency"))
{
updateTopClientsChart();
}
});
// Functions to update data in page
@@ -169,6 +184,8 @@ function updateSummaryData(runOnce) {
$("h3#ads_percentage_today").text(data.ads_percentage_today + "%");
$("h3.statistic.glow").removeClass("glow")
}, 500);
updateSessionTimer();
}).done(function() {
if (runOnce !== true) {
setTimeout(updateSummaryData, 10000);
+1
View File
@@ -72,6 +72,7 @@ $(document).ready(function() {
function refreshData() {
tableApi.ajax.url("api.php?getAllQueries").load();
// updateSessionTimer();
}
function add(domain,list) {
+3
View File
@@ -1,4 +1,7 @@
<?php
require "password.php";
if(!$auth) die("Not authorized");
ob_end_flush();
ini_set("output_buffering", "0");
ob_implicit_flush(true);
+48
View File
@@ -0,0 +1,48 @@
<?php
// Start a new PHP session (or continue an existing one)
session_start();
$pwhash = parse_ini_file("/etc/pihole/setupVars.conf")['WEBPASSWORD'];
// If the user wants to log out, we free all session variables currently registered
if(isset($_GET["logout"]))
{
session_unset();
}
// Test if password is set
if(strlen($pwhash) > 0)
{
// Compare doubly hashes password input with saved hash
if(isset($_POST["pw"]))
{
$postinput = hash('sha256',hash('sha256',$_POST["pw"]));
if($postinput == $pwhash)
{
$_SESSION["hash"] = $pwhash;
$auth = true;
}
}
// Compare auth hash with saved hash
else if (isset($_SESSION["hash"]))
{
if($_SESSION["hash"] == $pwhash)
$auth = true;
}
// API can use the hash to get data without logging in via plain-text password
else if (isset($api) && isset($_GET["auth"]))
{
if($_GET["auth"] == $pwhash)
$auth = true;
}
else
{
// Password or hash wrong
$auth = false;
}
}
else
{
// No password set
$auth = true;
}
?>
+3
View File
@@ -1,4 +1,7 @@
<?php
require "password.php";
if(!$auth) die("Not authorized");
ob_end_flush();
ini_set("output_buffering", "0");
ob_implicit_flush(true);