diff --git a/defaults/config.ini.default b/defaults/config.ini.default index 9b139f4d..5ab81435 100644 --- a/defaults/config.ini.default +++ b/defaults/config.ini.default @@ -12,6 +12,7 @@ url = "https://127.0.0.1:8000/" ; URL of the website description = "The Unity Web Portal is a lightweight HPC cluster front-end" ; Description of the website logo = "logo.png" ; path to logo file, in the webroot/assets/branding folder terms_of_service_url = "https://github.com" ; this can be external or a portal page created with "content management" +account_policy_url = "https://github.com" ; this can be external or a portal page created with "content management" [ldap] uri = "ldap://identity" ; URI of remote LDAP server diff --git a/resources/lib/UnityGroup.php b/resources/lib/UnityGroup.php index 80cd7f18..0a91a9ee 100644 --- a/resources/lib/UnityGroup.php +++ b/resources/lib/UnityGroup.php @@ -198,6 +198,41 @@ public function denyGroup($operator = null, $send_mail = true) } } + public function cancelGroupRequest($send_mail = true) + { + if (!$this->SQL->requestExists($this->getOwner()->getUID())) { + return; + } + + $this->SQL->removeRequest($this->getOwner()->getUID()); + + if ($send_mail) { + // send email to requestor + $this->MAILER->sendMail( + "admin", + "group_request_cancelled" + ); + } + } + + public function cancelGroupJoinRequest($user, $send_mail = true) + { + if (!$this->requestExists($user)) { + return; + } + + $this->SQL->removeRequest($user->getUID(), $this->pi_uid); + + if ($send_mail) { + // send email to requestor + $this->MAILER->sendMail( + $this->getOwner()->getMail(), + "group_join_request_cancelled", + ["group" => $this->pi_uid] + ); + } + } + // /** // * This method will delete the group, either by admin action or PI action // */ @@ -251,7 +286,7 @@ public function approveUser($new_user, $send_mail = true) $this->addUserToGroup($new_user); // remove request, this will fail silently if the request doesn't exist - $this->removeRequest($new_user->getUID()); + $this->SQL->removeRequest($new_user->getUID(), $this->pi_uid); // send email to the requestor if ($send_mail) { @@ -283,7 +318,7 @@ public function denyUser($new_user, $send_mail = true) } // remove request, this will fail silently if the request doesn't exist - $this->removeRequest($new_user->getUID()); + $this->SQL->removeRequest($new_user->getUID(), $this->pi_uid); if ($send_mail) { // send email to the user @@ -522,11 +557,6 @@ private function addRequest($uid) $this->SQL->addRequest($uid, $this->pi_uid); } - private function removeRequest($uid) - { - $this->SQL->removeRequest($uid, $this->pi_uid); - } - // // Public helper functions // diff --git a/resources/lib/UnityLDAP.php b/resources/lib/UnityLDAP.php index b7ef7cab..1e33be9c 100644 --- a/resources/lib/UnityLDAP.php +++ b/resources/lib/UnityLDAP.php @@ -38,6 +38,7 @@ class UnityLDAP extends ldapConn private $pi_groupOU; private $org_groupOU; private $adminGroup; + private $userGroup; private $custom_mappings_path; diff --git a/resources/lib/UnitySQL.php b/resources/lib/UnitySQL.php index 9aaef1c9..d9be2ca9 100644 --- a/resources/lib/UnitySQL.php +++ b/resources/lib/UnitySQL.php @@ -20,7 +20,7 @@ class UnitySQL // FIXME this string should be changed to something more intuitive, requires production sql change - private const REQUEST_BECOME_PI = "admin"; + public const REQUEST_BECOME_PI = "admin"; private $conn; diff --git a/resources/lib/UnitySite.php b/resources/lib/UnitySite.php index c8d89bcb..f19e1438 100644 --- a/resources/lib/UnitySite.php +++ b/resources/lib/UnitySite.php @@ -26,10 +26,8 @@ public static function die($x = null) public static function redirect($destination) { - if ($_SERVER["PHP_SELF"] != $destination) { - header("Location: $destination"); - self::die("Redirect failed, click here to continue."); - } + header("Location: $destination"); + self::die("Redirect failed, click here to continue."); } private static function headerResponseCode(int $code, string $reason) diff --git a/resources/mail/group_join_request_cancelled.php b/resources/mail/group_join_request_cancelled.php new file mode 100644 index 00000000..7fb02df1 --- /dev/null +++ b/resources/mail/group_join_request_cancelled.php @@ -0,0 +1,11 @@ +Subject = "Unity Account Request Cancelled"; +?> + +
Hello,
+ +Your request to join group '' on the Unity Cluster has been cancelled per your request. +
+ diff --git a/resources/mail/group_request_cancelled.php b/resources/mail/group_request_cancelled.php new file mode 100644 index 00000000..e143d1d0 --- /dev/null +++ b/resources/mail/group_request_cancelled.php @@ -0,0 +1,10 @@ +Subject = "Unity PI Account Request Cancelled"; +?> + +Hello,
+ +Your request for a PI account on the Unity Cluster has been cancelled per your request.
+ diff --git a/resources/templates/header.php b/resources/templates/header.php index 8e48ffcc..590f8943 100644 --- a/resources/templates/header.php +++ b/resources/templates/header.php @@ -11,7 +11,7 @@ } if (isset($SSO)) { - if (!$_SESSION["user_exists"]) { + if (!$_SESSION["user_exists"] && !str_ends_with($_SERVER['PHP_SELF'], "/panel/new_account.php")) { UnitySite::redirect($CONFIG["site"]["prefix"] . "/panel/new_account.php"); } } diff --git a/test/functional/CancelRequestTest.php b/test/functional/CancelRequestTest.php new file mode 100644 index 00000000..36028d6e --- /dev/null +++ b/test/functional/CancelRequestTest.php @@ -0,0 +1,71 @@ +assertEquals($x, count($SQL->getRequestsByUser($USER->getUID()))); + } + + public function testCancelPIRequest() + { + global $USER, $SQL; + switchUser(...getNonExistentUser()); + // First create a request + try { + http_post( + __DIR__ . "/../../webroot/panel/new_account.php", + ["new_user_sel" => "pi", "eula" => "agree", "confirm_pi" => "agree"] + ); + } catch (PhpUnitNoDieException $e) { + // Ignore the exception from http_post + } + + $this->assertNumberGroupRequests(1); + + // Now try to cancel it + try { + http_post( + __DIR__ . "/../../webroot/panel/new_account.php", + ["cancel" => "true"] # value of cancel is arbitrary + ); + } catch (PhpUnitNoDieException $e) { + // Ignore the exception from http_post + } + + $this->assertNumberGroupRequests(0); + } + + public function testCancelGroupJoinRequest() + { + global $USER, $SQL; + switchUser(...getNonExistentUser()); + + try { + http_post( + __DIR__ . "/../../webroot/panel/new_account.php", + ["new_user_sel" => "not_pi", "eula" => "agree", "pi" => getExistingPI()] + ); + } catch (PhpUnitNoDieException $e) { + // Ignore the exception from http_post + } + + $this->assertNumberGroupRequests(1); + + // Now try to cancel it + try { + http_post( + __DIR__ . "/../../webroot/panel/new_account.php", + ["cancel" => "true"] # value of cancel is arbitrary + ); + } catch (PhpUnitNoDieException $e) { + // Ignore the exception from http_post + } + + $this->assertNumberGroupRequests(0); + } +} diff --git a/test/phpunit-bootstrap.php b/test/phpunit-bootstrap.php index ae5f6a1b..1b95c51c 100644 --- a/test/phpunit-bootstrap.php +++ b/test/phpunit-bootstrap.php @@ -76,7 +76,9 @@ function switchUser( function http_post(string $phpfile, array $post_data): void { global $CONFIG, $REDIS, $LDAP, $SQL, $MAILER, $WEBHOOK, $GITHUB, $SITE, $SSO, $OPERATOR, $USER, $SEND_PIMESG_TO_ADMINS, $LOC_HEADER, $LOC_FOOTER; + $_PREVIOUS_SERVER = $_SERVER; $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["PHP_SELF"] = preg_replace("/.*webroot\//", "/", $phpfile); $_POST = $post_data; ob_start(); try { @@ -84,20 +86,24 @@ function http_post(string $phpfile, array $post_data): void } finally { ob_get_clean(); // discard output unset($_POST); - unset($_SERVER["REQUEST_METHOD"]); + $_SERVER = $_PREVIOUS_SERVER; } } -function http_get(string $phpfile): void +function http_get(string $phpfile, array $get_data = array()): void { global $CONFIG, $REDIS, $LDAP, $SQL, $MAILER, $WEBHOOK, $GITHUB, $SITE, $SSO, $OPERATOR, $USER, $SEND_PIMESG_TO_ADMINS, $LOC_HEADER, $LOC_FOOTER; + $_PREVIOUS_SERVER = $_SERVER; $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["PHP_SELF"] = preg_replace("/.*webroot\//", "/", $phpfile); + $_GET = $get_data; ob_start(); try { include $phpfile; } finally { ob_get_clean(); // discard output - unset($_SERVER["REQUEST_METHOD"]); + unset($_GET); + $_PREVIOUS_SERVER = $_SERVER; } } @@ -153,10 +159,15 @@ function getUserIsPIHasAtLeastOneMember() function getNonExistentUser() { - return ["user1@nonexistent.test", "foo", "bar", "user1@nonexistent.test"]; + return ["user2000@org2.test", "foo", "bar", "user2000@org2.test"]; } function getAdminUser() { return ["user1@org1.test", "foo", "bar", "user1@org1.test"]; } + +function getExistingPI() +{ + return "pi_user1005_org3_test"; +} diff --git a/test/unit/UnityGithubTest.php b/test/unit/UnityGithubTest.php index a860db3d..51944d6c 100644 --- a/test/unit/UnityGithubTest.php +++ b/test/unit/UnityGithubTest.php @@ -17,7 +17,7 @@ public static function providerTestGetGithubKeys() # user with no keys ["sheldor1510", []], # user with 1 key - ["simonLeary42", ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGRl6JWPj+Gq2Lz9GjYdUl4/unLoFOyfgeiII1CxutpabPByRJbeuonR0zTpn51tZeYuAUOJBOeKt+Lj4i4UDGl6igpdXXSwkBXl7jxfRPwJ6WuTkDx7Z8ynwnqlDV2q089q4OX/b/uuHgsIhIBwrouKsRQaZIqTbwNMfiqQ2zl14V0KMrTPzOiwR6Q+hqSaR5Z29WKE7ff/OWzSC3/0T6avCmcCbQaRPJdVM+QC17B0vl8FzPwRjorMngwZ0cImdQ/0Ww1d12YAL7UWp1c2egfnthKP3MuQZnNF8ixsAk1eIIwTRdiI87BOoorW8NXhxXmhyheRCsFwyP4LJBqyUVoZJ0UYyk0AO4G9EStnfpiz8YXGK+M1G4tUrWgzs1cdjlHtgCWUmITtgabnYCC4141m7n4GZTk2H/lSrJcvAs3JEiwLTj1lzeGgzeSsz/XKsnOJyzjEVr2Jp3iT+J9PbQpfS0SxTCIGgxMqllovv79pfsF/zc+vaxqSShyHW7oyn7hLMHM60LO/IIX1RWGL3rD9ecXx2pXXQ1RhIkVteIi13XkFt+KW00cstFlAd3EHCoY/XorShd2jeID7tpnYlmNfotYUs6IKefvpNC0PWkh5UXFEv3SUfw4Wd8O0DiHfhkrhxn1W/GajqSIlZ5DKgPzFg8EHexv8lSa7WJg0H3YQ=="]] + ["simonLeary42", ["ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL2oMxcq04PWw1iB2ZQZezFPGmX2HEKhHD6kLIoLz1RUKTNN7Glw2iF5uMFnKxYgTvdfrNjrvvLnOXvhPBvjeec="]] ]; } diff --git a/webroot/panel/new_account.php b/webroot/panel/new_account.php index c4698df8..5abb4535 100644 --- a/webroot/panel/new_account.php +++ b/webroot/panel/new_account.php @@ -4,103 +4,134 @@ use UnityWebPortal\lib\UnitySite; use UnityWebPortal\lib\UnityGroup; - -require_once $LOC_HEADER; +use UnityWebPortal\lib\UnitySQL; if ($USER->exists()) { - UnitySite::redirect($CONFIG["site"]["prefix"] . "/panel/index.php"); // Redirect if account already exists + UnitySite::redirect($CONFIG["site"]["prefix"] . "/panel/index.php"); } -if ($_SERVER["REQUEST_METHOD"] == "POST") { - $errors = array(); +$pending_requests = $SQL->getRequestsByUser($USER->getUID()); - if (!isset($_POST["eula"]) || $_POST["eula"] != "agree") { - // checkbox was not checked - array_push($errors, "Accepting the EULA is required"); - } - - if ($_POST["new_user_sel"] == "not_pi") { - $form_group = new UnityGroup($_POST["pi"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); - if (!$form_group->exists()) { - array_push($errors, "The selected PI does not exist"); +if ($_SERVER["REQUEST_METHOD"] == "POST") { + if (isset($_POST["new_user_sel"])) { + if (!isset($_POST["eula"]) || $_POST["eula"] != "agree") { + UnitySite::badRequest("user did not agree to EULA"); + } + if ($_POST["new_user_sel"] == "not_pi") { + $form_group = new UnityGroup($_POST["pi"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + if (!$form_group->exists()) { + UnitySite::badRequest("The selected PI does not exist"); + } + $form_group->newUserRequest($USER); } - } - - // Request Account Form was Submitted - if (count($errors) == 0) { if ($_POST["new_user_sel"] == "pi") { - // requesting a PI account + if (!isset($_POST["confirm_pi"]) || $_POST["confirm_pi"] != "agree") { + UnitySite::badRequest("user did not agree to account policy"); + } $USER->getPIGroup()->requestGroup($SEND_PIMESG_TO_ADMINS); - } elseif ($_POST["new_user_sel"] == "not_pi") { - $form_group->newUserRequest($USER); } } + elseif (isset($_POST["cancel"])) { + foreach ($pending_requests as $request) { + if ($request["request_for"] == "admin") { + $USER->getPIGroup()->cancelGroupRequest(); + } else { + $pi_group = new UnityGroup($request["request_for"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $pi_group->cancelGroupJoinRequest($user=$USER); + } + } + } else { + UnitySite::badRequest("neither 'new_user_sel' or 'cancel' are set!"); + } + UnitySite::redirect($_SERVER['PHP_SELF']); } - +require_once $LOC_HEADER; ?>Requesting Ownership of PI Account/Group
+You will receive an email when your account has been approved.
+Email if you have not heard back in one business day.
+Requesting Membership in a PI Group
+You will receive an email when your account has been approved by the PI.
+You may need to remind them.
+