Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
2 changes: 1 addition & 1 deletion deployment/config.base.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
; DO NOT EDIT THIS FILE. Copy this file to `config.ini` and edit that file.
[upstream]
version = "1.7.4" ; Current upstream version of the web portal
version = "1.7.5" ; Current upstream version of the web portal
repo = "https://github.com/UnityHPC/account-portal" ; Upstream URL for the web portal

[site]
Expand Down
32 changes: 26 additions & 6 deletions resources/lib/UnityHTTPD.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,24 @@ public static function die(?string $x = null): never
}
}

public static function redirectOverrideMethodGet(?string $dest = null): never
{
self::redirect(dest: $dest, preserve_request_method: false);
}

/*
send HTTP header, set HTTP response code,
print a message just in case the browser fails to redirect if PHP is not being run from the CLI,
and then die
*/
public static function redirect(?string $dest = null): never
{
public static function redirect(
?string $dest = null,
bool $preserve_request_method = true,
): never {
$dest ??= getRelativeURL($_SERVER["REQUEST_URI"]);
// TODO check $_SERVER["REDIRECT_STATUS"]?
header("Location: $dest");
http_response_code(302);
http_response_code($preserve_request_method ? 307 : 303);
if (CONFIG["site"]["enable_redirect_message"]) {
echo "If you're reading this message, then your browser has failed to redirect you " .
"to the proper destination. click <a href='$dest'>here</a> to continue.";
Expand Down Expand Up @@ -81,10 +88,12 @@ public static function gracefulDie(
self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid);
if (
($_SERVER["REQUEST_METHOD"] ?? "") == "POST" &&
!str_starts_with($_SERVER["REQUEST_URI"], "/lan/api/")
!str_starts_with($_SERVER["REQUEST_URI"], "/lan/api/") &&
!str_starts_with($_SERVER["REQUEST_URI"], "/panel/ajax/")
) {
self::messageError($title, implode("\n", $body_paragraphs));
self::redirect();
// change request method POST into GET to prevent an infinite looop of errors
self::redirectOverrideMethodGet();
Comment thread
simonLeary42 marked this conversation as resolved.
Outdated
} else {
if (!headers_sent()) {
http_response_code($http_response_code);
Expand Down Expand Up @@ -239,8 +248,19 @@ public static function errorHandler(
return false;
}

public static function assertRequestMethod(string $expected): void
{
if (($found = $_SERVER["REQUEST_METHOD"] ?? "") != $expected) {
UnityHTTPD::badRequest(
"expected request method '$expected', got '$found'",
"invalid request method",
);
}
}

public static function getPostData(string $key): string
{
self::assertRequestMethod("POST");
if (!array_key_exists($key, $_POST)) {
self::badRequest("\$_POST has no array key '$key'");
}
Expand Down Expand Up @@ -417,7 +437,7 @@ public static function validatePostCSRFToken(): void
"Invalid Session Token",
"This can happen if you leave your browser open for too long. Error ID: $errorid",
);
self::redirect();
self::redirectOverrideMethodGet();
}
}

Expand Down
4 changes: 1 addition & 3 deletions resources/templates/header.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@
<main>

<div id="messages" aria-label="messages">
{% if messages|length >= 3 %}
<button id="clear_all_messages_button">Clear All Messages</button>
{% endif %}
<button id="clear_all_messages_button" style="display: none;">Clear All Messages</button>
{% set level_enum = enum('UnityWebPortal\\lib\\UnityHTTPDMessageLevel') %}
{% for message in messages|reverse %}
{% set title = message[0] %}
Expand Down
4 changes: 2 additions & 2 deletions resources/templates/header.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
&& ($_POST["form_type"] ?? null) == "clearView"
) {
unset($_SESSION["viewUser"]);
UnityHTTPD::redirect(getRelativeURL("admin/user-mgmt.php"));
UnityHTTPD::redirectOverrideMethodGet(getRelativeURL("admin/user-mgmt.php"));
}
// Webroot files need to handle their own POSTs before loading the header
// so that they can do UnityHTTPD::badRequest before anything else has been printed.
// They also must not redirect like standard PRG practice because this
// header also needs to handle POST data. So this header does the PRG redirect
// for all pages.
unset($_POST); // unset ensures that header must not come before POST handling
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}

if (isset($SSO)) {
Expand Down
2 changes: 1 addition & 1 deletion webroot/admin/pi-mgmt.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
$group = new UnityGroup(UnityHTTPD::getPostData("pi"), $LDAP, $SQL, $MAILER);
if ($group->getIsDisabled()) {
UnityHTTPD::messageError("Cannot Disable PI Group", "Group is already disabled");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
$group->disable();
UnityHTTPD::messageSuccess("Group Disabled", $group->gid);
Expand Down
2 changes: 1 addition & 1 deletion webroot/admin/user-mgmt.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
switch ($_POST["form_type"]) {
case "viewAsUser":
$_SESSION["viewUser"] = $_POST["uid"];
UnityHTTPD::redirect(getRelativeURL("panel/account.php"));
UnityHTTPD::redirectOverrideMethodGet(getRelativeURL("panel/account.php"));
break; /** @phpstan-ignore deadCode.unreachable */
}
}
Expand Down
26 changes: 21 additions & 5 deletions webroot/js/messages.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
function hideClearAllMessagesButtonIfAllMessagesAlreadyCleared() {
function updateClearMessagesButtonVisibility(minMessageCount = 3) {
var visibleMessages = $('#messages .message:visible').length;
if (visibleMessages === 0) {
if (visibleMessages >= minMessageCount) {
$('#clear_all_messages_button').show();
} else {
$('#clear_all_messages_button').hide();
}
}

$(document).ready(function () {
updateClearMessagesButtonVisibility();

$('#messages').on('click', '.message button', function () {
var button = $(this);
var message = button.parent();
message.hide();
$.ajax({
url: '/panel/ajax/delete_message.php',
method: 'POST',
Expand All @@ -18,14 +21,27 @@ $(document).ready(function () {
'title': button.data('title'),
'body': button.data('body')
},
success: function () {
message.hide();
updateClearMessagesButtonVisibility();
},
error: function (result) {
$("#messages").append(result.responseText);
}
Comment thread
simonLeary42 marked this conversation as resolved.
});
hideClearAllMessagesButtonIfAllMessagesAlreadyCleared();
});

$('#clear_all_messages_button').on('click', function () {
$('#messages .message:visible button').click();
$.ajax({
url: '/panel/ajax/clear_messages.php',
method: 'POST',
success: function () {
$('#messages .message').hide();
$('#clear_all_messages_button').hide();
},
error: function (result) {
$("#messages").append(result.responseText);
}
Comment thread
simonLeary42 marked this conversation as resolved.
});
});
});
4 changes: 1 addition & 3 deletions webroot/lan/api/bump-last-login.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

use UnityWebPortal\lib\UnityHTTPD;

if ($_SERVER["REQUEST_METHOD"] !== "POST") {
UnityHTTPD::badRequest("invalid request method {$_SERVER['REQUEST_METHOD']}");
}
UnityHTTPD::assertRequestMethod("POST");
UnityHTTPD::validateAPIKey();
$uid = UnityHTTPD::getQueryParameter("uid");
$SQL->updateUserLastLogin($uid);
Expand Down
26 changes: 13 additions & 13 deletions webroot/panel/account.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
} catch (EncodingUnknownException | EncodingConversionException $e) {
UnityHTTPD::errorLog("uploaded key has bad encoding", "", error: $e);
UnityHTTPD::messageError("SSH Key Not Added: Invalid Encoding", "");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
break;
case "generate":
Expand All @@ -40,7 +40,7 @@
"No Keys Added",
"No keys found associated with GitHub account."
);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
break;
default:
Expand Down Expand Up @@ -77,7 +77,7 @@
$stub_fingprint = substr($sha256_fingerprint, 0, 6);
UnityHTTPD::messageSuccess("SSH Key Added", "Fingerprint: $stub_fingprint");
}
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "delKey":
$key = _base64_decode(UnityHTTPD::getPostData("delKey"));
Expand All @@ -86,10 +86,10 @@
$USER->removeSSHKey($key);
} catch (ArrayKeyException) {
UnityHTTPD::messageError("Cannot Remove SSH Key", "Key not found");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
UnityHTTPD::messageSuccess("SSH Key Removed", "$key_short");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "loginshell":
$shell = UnityHTTPD::getPostData("shellSelect");
Expand All @@ -98,47 +98,47 @@
}
$USER->setLoginShell($shell);
UnityHTTPD::messageSuccess("Login Shell Changed", "");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "pi_request":
if ($USER->isPI()) {
UnityHTTPD::messageError("Cannot Submit PI Request", "Already a PI");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
if ($SQL->requestExists($USER->uid, UnitySQL::REQUEST_BECOME_PI)) {
UnityHTTPD::messageError("Cannot Submit PI Request", "This request already exists");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
if ($_POST["tos"] != "agree") {
UnityHTTPD::badRequest("user did not agree to terms of service");
}
$USER->getPIGroup()->requestGroup();
UnityHTTPD::messageSuccess("PI Group Requested", "");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "cancel_pi_request":
if (!$SQL->requestExists($USER->uid, UnitySQL::REQUEST_BECOME_PI)) {
UnityHTTPD::messageError("Cannot Cancel PI Request", "No PI request found");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
$USER->getPIGroup()->cancelGroupRequest();
UnityHTTPD::messageSuccess("PI Request Cancelled", "");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "disable":
if ($hasGroups) {
UnityHTTPD::messageError(
"Cannot Disable",
"You are a PI or you are a member of at least one PI group"
);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
if ($USER->getFlag(UserFlag::DISABLED)) {
UnityHTTPD::badRequest("user is already disabled", "");
}
$USER->disable(UnityUserDisabledReason::DisabledSelf);
UnityHTTPD::messageSuccess("Account Disabled", "");
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
}
}
Expand Down
9 changes: 9 additions & 0 deletions webroot/panel/ajax/clear_messages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

require_once __DIR__ . "/../../../resources/autoload.php";

use UnityWebPortal\lib\UnityHTTPD;

UnityHTTPD::assertRequestMethod("POST");
UnityHTTPD::clearMessages();
UnityHTTPD::die();
Comment thread
simonLeary42 marked this conversation as resolved.
2 changes: 1 addition & 1 deletion webroot/panel/disabled_account.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
UnityHTTPD::validatePostCSRFToken();
$USER->reEnable();
UnityHTTPD::messageSuccess("Account Re-Enabled", "");
UnityHTTPD::redirect(getRelativeURL("panel/account.php"));
UnityHTTPD::redirectOverrideMethodGet(getRelativeURL("panel/account.php"));
}
}
require getTemplatePath("header.php");
Expand Down
12 changes: 6 additions & 6 deletions webroot/panel/groups.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
$pi_group = new UnityGroup($gid, $LDAP, $SQL, $MAILER);
if (!$pi_group->exists()) {
UnityHTTPD::messageError("This PI Doesn't Exist", $gid);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
return $pi_group;
};
Expand All @@ -32,28 +32,28 @@
"Invalid Group Membership Request",
"You've already requested this"
);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
if ($pi_account->memberUIDExists($USER->uid)) {
UnityHTTPD::messageError(
"Invalid Group Membership Request",
"You're already in this PI group"
);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
}
}
$pi_account->newUserRequest($USER);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "removePIForm":
$pi_account = $getPIGroupFromPost();
$pi_account->removeUser($USER, UnityGroupUserRemovedReason::RemovedSelf);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
case "cancelPIForm":
$pi_account = $getPIGroupFromPost();
$pi_account->cancelGroupJoinRequest($USER);
UnityHTTPD::redirect();
UnityHTTPD::redirectOverrideMethodGet();
break; /** @phpstan-ignore deadCode.unreachable */
}
}
Expand Down
2 changes: 1 addition & 1 deletion webroot/panel/new_account.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
if (UnityHTTPD::getPostData("form_type") === "register") {
UnityHTTPD::validatePostCSRFToken();
$USER->init($SSO["firstname"], $SSO["lastname"], $SSO["mail"], $SSO["org"]);
UnityHTTPD::redirect(getRelativeURL("panel/account.php"));
UnityHTTPD::redirectOverrideMethodGet(getRelativeURL("panel/account.php"));
}
}
require getTemplatePath("header.php");
Expand Down
Loading
Loading