Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add server-side password protection for the web interface #197

Merged
merged 43 commits into from
Nov 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
3bc6474
Add server-side password protection for the web interface
DL6ER Nov 16, 2016
a754c3c
Use hashed once logged in to hide the plain-text password
DL6ER Nov 16, 2016
c3a6b0e
Fixed a typo
DL6ER Nov 16, 2016
72e5fc5
Change button text to 'Login'
DL6ER Nov 16, 2016
70f9076
Added "Logout" button which redirects to index.php without providing …
DL6ER Nov 16, 2016
7cbd9bd
Show "Logout" only if a password is defined
DL6ER Nov 16, 2016
cb874e7
Show "Logout" only if (a) password is set and (b) user has successful…
DL6ER Nov 16, 2016
b6cf46a
Undid change to footer.php
DL6ER Nov 16, 2016
4dfbcff
Hide main navigation if user is not logged in
DL6ER Nov 16, 2016
06df8f0
Always use only hashes for the password
DL6ER Nov 16, 2016
6d5c901
Compute double hashes to avoid rainbow table vulnerability
DL6ER Nov 16, 2016
41fe76c
Capitalized variable name
DL6ER Nov 16, 2016
08aab09
Merge branch 'devel' into auth
DL6ER Nov 16, 2016
8c278f3
Resolve merge conflict from updateing devel branch
DL6ER Nov 16, 2016
4372c2e
Extend hash auth to API calls
DL6ER Nov 16, 2016
ef38679
Merge branch 'devel' into auth
DL6ER Nov 18, 2016
0df93ca
Address codacy-bot's complaints
DL6ER Nov 18, 2016
8ecd218
Many changes that are documented in PR #197
DL6ER Nov 18, 2016
03ea07e
api.php?summaryRaw also accessible without auth token
DL6ER Nov 19, 2016
e575988
Added some more comments in api.php
DL6ER Nov 19, 2016
aad1b65
Added comments to header.php
DL6ER Nov 19, 2016
77e0724
Added comments in index.php
DL6ER Nov 19, 2016
b53009a
Added comments to js/pihole/index.js
DL6ER Nov 19, 2016
d899293
Test if POST and GET variables are set before trying to actually acce…
DL6ER Nov 19, 2016
6781fa7
Merge branch 'devel' into auth
DL6ER Nov 20, 2016
2c93be0
Extended current password protection to gravity.sh page
DL6ER Nov 20, 2016
06f8f0d
Actually pass the password hash to the script that runs the update
DL6ER Nov 20, 2016
02dc741
Move from GET to SESSION variables for the sake of convenience
DL6ER Nov 20, 2016
cd75d7e
Remove hash from the javascript scripts.
DL6ER Nov 20, 2016
bfdefdd
Removed hash from Query log request
DL6ER Nov 20, 2016
28dd6ed
Minor change
DL6ER Nov 20, 2016
f84d545
Allow GET hash for API calls
DL6ER Nov 20, 2016
0cd099f
Added session timer
DL6ER Nov 20, 2016
829c854
Completely hide session timer if there is no session (i.e. no passwor…
DL6ER Nov 20, 2016
1cf031a
Show "-- : --" if session timer has expired. Show session timer only …
DL6ER Nov 20, 2016
9e3a092
Addressed codacy issues
DL6ER Nov 21, 2016
3dea0cf
Moved Donate item outside of $auth scope on the main menu
DL6ER Nov 21, 2016
f0adb16
Merge branch 'devel' into auth
DL6ER Nov 21, 2016
4675b5a
Load "Top Domains", "Top Advertisers", and "Top Clients" only if auth…
DL6ER Nov 21, 2016
07376ff
Fixed copy-and-paste error
DL6ER Nov 23, 2016
d9adccc
Merge branch 'devel' into auth
DL6ER Nov 23, 2016
21649a2
Extended password protection to php/queryads.php
DL6ER Nov 23, 2016
f3fa7ff
Show 'Help' only if authorized
DL6ER Nov 23, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions api.php
Original file line number Diff line number Diff line change
@@ -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']);
Expand All @@ -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());
}

Expand Down
56 changes: 54 additions & 2 deletions header.php
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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 -->
Expand All @@ -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();
}
?>
10 changes: 8 additions & 2 deletions index.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php
$indexpage = true;
require "header.php";
?>
<!-- Small boxes (Stat box) -->
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -195,7 +201,7 @@
<!-- /.col -->
</div>
<!-- /.row -->

<?php } ?>
<?php
require "footer.php";
?>
Expand Down
39 changes: 39 additions & 0 deletions js/pihole/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
2 changes: 1 addition & 1 deletion js/pihole/header.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Remove JS warning
var jswarn = document.getElementById("js-warn-exit");
jswarn.parentNode.removeChild(jswarn);
jswarn.parentNode.removeChild(jswarn);
99 changes: 58 additions & 41 deletions js/pihole/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions js/pihole/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ $(document).ready(function() {

function refreshData() {
tableApi.ajax.url("api.php?getAllQueries").load();
// updateSessionTimer();
}

function add(domain,list) {
Expand Down
Loading