Add app password support (#2793)

This commit is contained in:
DL6ER
2023-10-29 19:43:55 +01:00
committed by GitHub
3 changed files with 320 additions and 2 deletions

View File

@@ -40,6 +40,7 @@ $(function () {
{ data: "id" },
{ data: "valid", render: renderBool },
{ data: null },
{ data: "app", render: renderBool },
{ data: "login_at", render: utils.renderTimestamp },
{ data: "valid_until", render: utils.renderTimestamp },
{ data: "remote_addr", type: "ip-address" },
@@ -84,10 +85,10 @@ $(function () {
'">' +
'<span class="far fa-trash-alt"></span>' +
"</button>";
$("td:eq(8)", row).html(button);
$("td:eq(9)", row).html(button);
if (data.current_session) {
ownSessionID = data.id;
$("td:eq(6)", row).html(
$("td:eq(7)", row).html(
'<strong title="This is the session you are currently using for the web interface">' +
data.remote_addr +
"</strong>"
@@ -311,6 +312,69 @@ $("#modal-totp").on("shown.bs.modal", function () {
});
});
var apppwhash = null;
$("#modal-apppw").on("shown.bs.modal", function () {
$.ajax({
url: "/api/auth/app",
})
.done(function (data) {
apppwhash = data.app.hash;
$("#password_code").text(data.app.password);
$("#password_display").removeClass("hidden");
$("#password-spinner").hide();
})
.fail(function (data) {
apiFailure(data);
});
});
$("#apppw_submit").on("click", function () {
// Enable app password
setAppPassword();
});
$("#apppw_clear").on("click", function () {
// Disable app password
apppwhash = "";
setAppPassword();
});
function setAppPassword() {
$.ajax({
url: "/api/config",
type: "PATCH",
dataType: "json",
processData: false,
data: JSON.stringify({ config: { webserver: { api: { app_pwhash: apppwhash } } } }),
contentType: "application/json",
})
.done(function () {
$("#modal-apppw").modal("hide");
const verb = apppwhash.length > 0 ? "enabled" : "disabled";
const verb2 = apppwhash.length > 0 ? "will" : "may";
alert(
"App password has been " +
verb +
", you " +
verb2 +
" need to re-login to continue using the web interface."
);
location.reload();
})
.fail(function (data) {
apiFailure(data);
});
}
// Remove class "password_background" from the password input field with ID
// password_code when the user hovers over it or if it is focused
$("#password_code").on("mouseover focus", function () {
$(this).removeClass("password_background");
});
$("#password_code").on("mouseout blur", function () {
$(this).addClass("password_background");
});
// Trigger keyup event when pasting into the TOTP code input field
$("#totp_code").on("paste", function (e) {
$(e.target).keyup();

View File

@@ -136,8 +136,11 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
</select>
</div>
</div>
</div>
<div class="col-lg-12 pt-3">
<button type="button" id="button-enable-totp" class="btn btn-success hidden" data-toggle="modal" data-target="#modal-totp">Enable 2FA</button>
<button type="button" id="button-disable-totp" class="btn btn-danger hidden" data-toggle="modal" data-target="#modal-totp-disable">Disable 2FA</button>
<button type="button" id="button-apppw" class="btn btn-default pull-right" data-toggle="modal" data-target="#modal-apppw">Configure app password</button>
</div>
</div>
</div>
@@ -158,6 +161,7 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
<th title="Session ID used internally by our Pi-hole">ID</th>
<th title="Session is still valid (neither expired nor closed)">Valid</th>
<th title="Connection between client and Pi-hole is end-to-end encrypted">TLS</th>
<th title="Session used application password during authentication"><i class="fas fa-robot"></i></th>
<th title="Time at which the client created the session">Login at</th>
<th title="Time at which the session expires (if not prolonged)">Valid until</th>
<th title="The client that created this session (the current connection is highlighted in bold-face)">Client IP</th>
@@ -217,6 +221,39 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
</div>
</div>
<div class="modal fade" id="modal-apppw" style="display: none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h4 class="modal-title">Configure application password</h4>
</div>
<div class="modal-body">
<p>After you turn on two-factor (2FA) verification and set up an
Authenticator app, you may run into issues if you use apps or
other services that don't support two-step verification. In this
case, you can create and use an app password to sign in.</p>
<p>An app password is a long, randomly generated password that
can be used instead of your regular password + 2FA token when
signing in to the API. You can revoke the app password at any
time. The app password can also be used when 2FA is not enabled.</p>
<div class="text-center">
<i id="password-spinner" class="fas fa-spinner fa-pulse fa-5x"></i>
</div>
<p id="password_display" class="text-center hidden">&#x1F510;<br><strong>Your new app password is:</strong><br><code class="password_background m-5" id="password_code"></code></p>
<p>IMPORTANT: The app password generated here will only be shown
once and cannot be recovered. Make sure to store it in a safe
place!</p>
</div>
<div class="modal-footer">
<button type="button" id="apppw_clear" class="btn btn-default btn-danger pull-left">Remove currently set app password</button>
<button type="button" id="apppw_submit" class="btn btn-default btn-success">Enable new app password</button>
</div>
</div>
</div>
</div>
<script src="<?=pihole.fileversion('scripts/vendor/jquery.confirm.min.js')?>"></script>
<script src="<?=pihole.fileversion('scripts/vendor/qrious.min.js')?>"></script>
<script src="<?=pihole.fileversion('scripts/pi-hole/js/settings-api.js')?>"></script>

View File

@@ -1173,3 +1173,220 @@ table.dataTable tbody > tr > .selected {
.button-pad {
margin: 2px;
}
.m-0 {
margin: 0 !important;
}
.m-1 {
margin: 0.25rem !important;
}
.m-2 {
margin: 0.5rem !important;
}
.m-3 {
margin: 1rem !important;
}
.m-4 {
margin: 1.5rem !important;
}
.m-5 {
margin: 3rem !important;
}
.mt-0 {
margin-top: 0 !important;
}
.mr-0 {
margin-right: 0 !important;
}
.mb-0 {
margin-bottom: 0 !important;
}
.ml-0 {
margin-left: 0 !important;
}
.mx-0 {
margin-left: 0 !important;
margin-right: 0 !important;
}
.my-0 {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.mt-1 {
margin-top: 0.25rem !important;
}
.mr-1 {
margin-right: 0.25rem !important;
}
.mb-1 {
margin-bottom: 0.25rem !important;
}
.ml-1 {
margin-left: 0.25rem !important;
}
.mx-1 {
margin-left: 0.25rem !important;
margin-right: 0.25rem !important;
}
.my-1 {
margin-top: 0.25rem !important;
margin-bottom: 0.25rem !important;
}
.mt-2 {
margin-top: 0.5rem !important;
}
.mr-2 {
margin-right: 0.5rem !important;
}
.mb-2 {
margin-bottom: 0.5rem !important;
}
.ml-2 {
margin-left: 0.5rem !important;
}
.mx-2 {
margin-right: 0.5rem !important;
margin-left: 0.5rem !important;
}
.my-2 {
margin-top: 0.5rem !important;
margin-bottom: 0.5rem !important;
}
.mt-3 {
margin-top: 1rem !important;
}
.mr-3 {
margin-right: 1rem !important;
}
.mb-3 {
margin-bottom: 1rem !important;
}
.ml-3 {
margin-left: 1rem !important;
}
.mx-3 {
margin-right: 1rem !important;
margin-left: 1rem !important;
}
.my-3 {
margin-bottom: 1rem !important;
margin-top: 1rem !important;
}
.p-0 {
padding: 0 !important;
}
.p-1 {
padding: 0.25rem !important;
}
.p-2 {
padding: 0.5rem !important;
}
.p-3 {
padding: 1rem !important;
}
.p-4 {
padding: 1.5rem !important;
}
.p-5 {
padding: 3rem !important;
}
.pt-0 {
padding-top: 0 !important;
}
.pr-0 {
padding-right: 0 !important;
}
.pb-0 {
padding-bottom: 0 !important;
}
.pl-0 {
padding-left: 0 !important;
}
.px-0 {
padding-left: 0 !important;
padding-right: 0 !important;
}
.py-0 {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.pt-1 {
padding-top: 0.25rem !important;
}
.pr-1 {
padding-right: 0.25rem !important;
}
.pb-1 {
padding-bottom: 0.25rem !important;
}
.pl-1 {
padding-left: 0.25rem !important;
}
.px-1 {
padding-left: 0.25rem !important;
padding-right: 0.25rem !important;
}
.py-1 {
padding-top: 0.25rem !important;
padding-bottom: 0.25rem !important;
}
.pt-2 {
padding-top: 0.5rem !important;
}
.pr-2 {
padding-right: 0.5rem !important;
}
.pb-2 {
padding-bottom: 0.5rem !important;
}
.pl-2 {
padding-left: 0.5rem !important;
}
.px-2 {
padding-right: 0.5rem !important;
padding-left: 0.5rem !important;
}
.py-2 {
padding-top: 0.5rem !important;
padding-bottom: 0.5rem !important;
}
.pt-3 {
padding-top: 1rem !important;
}
.pr-3 {
padding-right: 1rem !important;
}
.pb-3 {
padding-bottom: 1rem !important;
}
.pl-3 {
padding-left: 1rem !important;
}
.px-3 {
padding-right: 1rem !important;
padding-left: 1rem !important;
}
.py-3 {
padding-bottom: 1rem !important;
padding-top: 1rem !important;
}
.password_background {
background-image: repeating-linear-gradient(
45deg,
white 0%,
white 2%,
rgb(0, 0, 0) 2%,
rgb(0, 0, 0) 4%,
white 4%
);
}