Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ repos:
resources/lib/phpopenldaper/.*|
vendor/.*|
resources/lib/.*|
resources/mail/.*|
)$

- repo: https://github.com/rbubley/mirrors-prettier
Expand All @@ -47,6 +48,7 @@ repos:
vendor/.*|
resources/templates/.*|
webroot/.*|
resources/mail/.*|
)$

# linters (work required) ########################################################################
Expand Down Expand Up @@ -81,6 +83,7 @@ repos:
resources/lib/phpopenldaper/.*|
vendor/.*|
resources/lib/.*|
resources/mail/.*|
)$
- id: php-l
name: php -l
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
This will enable strict mode and throw an exception rather than issuing a warning.
- `UnityHTTPD`'s user-facing error functionality (ex: `badRequest`) should only be called from `webroot/**/*.php`.
`resources/**/*.php` should throw exceptions instead.
- all pages under `webroot/admin/` must check for `$USER->isAdmin()` and call `UnityHTTPD::forbidden()` if not admin.
- all pages under `webroot/admin/` must check for `$USER->getFlag(UserFlag::ADMIN)` and call `UnityHTTPD::forbidden()` if not admin.

This repository will automatically check PRs for linting compliance.

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ rm "$prod" && ln -s "$old" "$prod"
### 1.5.0 -> 1.5.1

- the `[site]getting_started_url` option should be defined
- the `[ldap]admin_group` option has been renamed to `[ldap]user_flag_groups[admin]`
- the `[ldap]qualified_user_group` option has been renamed to `[ldap]user_flag_groups[qualified]`

### 1.4 -> 1.5

Expand Down
7 changes: 5 additions & 2 deletions defaults/config.ini.default
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ pass = "password" ; Admin bind password
custom_user_mappings_dir = "deployment/custom_user_mappings" ; for internal use only
basedn = "dc=unityhpc,dc=test" ; Base search DN
user_ou = "ou=users,dc=unityhpc,dc=test" ; User organizational unit (may contain more than user group)
qualified_user_group = "cn=unityusers,dc=unityhpc,dc=test" ; Qualified user group (in at least one PI group)
group_ou = "ou=groups,dc=unityhpc,dc=test" ; Group organizational unit
pigroup_ou = "ou=pi_groups,dc=unityhpc,dc=test" ; PI Group organizational unit
orggroup_ou = "ou=org_groups,dc=unityhpc,dc=test" ; ORG group organizational unit
admin_group = "cn=web_admins,dc=unityhpc,dc=test" ; admin dn (members of this group are admins on the web portal)
def_user_shell = "/bin/bash" ; Default shell for new users
offset_UIDGID = 1000000 ; start point when allocating new UID/GID pairs for a new user
offset_PIGID = 2000000 ; start point when allocating new GID for a new PI group
offset_ORGGID = 3000000 ; start point when allocating new GID for a new org group
user_flag_groups[admin] = "cn=web_admins,dc=unityhpc,dc=test" ; admin user group dn
user_flag_groups[ghost] = "cn=ghost,dc=unityhpc,dc=test" ; ghost user group dn
user_flag_groups[idlelocked] = "cn=idlelocked,dc=unityhpc,dc=test" ; idlelocked user group dn
user_flag_groups[locked] = "cn=locked,dc=unityhpc,dc=test" ; locked user group dn
user_flag_groups[qualified] = "cn=unityusers,dc=unityhpc,dc=test" ; qualified user group (in at least one PI group)

[sql]
host = "sql" ; mariadb hostname
Expand Down
3 changes: 2 additions & 1 deletion resources/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use UnityWebPortal\lib\UnityWebhook;
use UnityWebPortal\lib\UnityGithub;
use UnityWebPortal\lib\UnityHTTPD;
use UnityWebPortal\lib\UserFlag;

if (CONFIG["site"]["enable_exception_handler"]) {
set_exception_handler(["UnityWebPortal\lib\UnityHTTPD", "exceptionHandler"]);
Expand Down Expand Up @@ -56,7 +57,7 @@
$_SESSION["SSO"] = $SSO;

$OPERATOR = new UnityUser($SSO["user"], $LDAP, $SQL, $MAILER, $WEBHOOK);
$_SESSION["is_admin"] = $OPERATOR->isAdmin();
$_SESSION["is_admin"] = $OPERATOR->getFlag(UserFlag::ADMIN);

if (isset($_SESSION["viewUser"]) && $_SESSION["is_admin"]) {
$USER = new UnityUser($_SESSION["viewUser"], $LDAP, $SQL, $MAILER, $WEBHOOK);
Expand Down
5 changes: 3 additions & 2 deletions resources/lib/UnityGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function approveGroup(?UnityUser $operator = null, bool $send_mail = true
if ($send_mail) {
$this->MAILER->sendMail($this->getOwner()->getMail(), "group_created");
}
$this->getOwner()->setIsQualified(true); // having your own group makes you qualified
$this->getOwner()->setFlag(UserFlag::QUALIFIED, true); // having your own group makes you qualified
}

/**
Expand Down Expand Up @@ -191,7 +191,8 @@ public function approveUser(UnityUser $new_user, bool $send_mail = true): void
"org" => $new_user->getOrg(),
]);
}
$new_user->setIsQualified(true); // being in a group makes you qualified
// being in a group makes you qualified
$new_user->setFlag(UserFlag::QUALIFIED, true, doSendMail: true, doSendMailAdmin: false);
}

public function denyUser(UnityUser $new_user, bool $send_mail = true): void
Expand Down
55 changes: 19 additions & 36 deletions resources/lib/UnityLDAP.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
use UnityWebPortal\lib\exceptions\EntryNotFoundException;
use PHPOpenLDAPer\LDAPConn;
use PHPOpenLDAPer\LDAPEntry;
use UnityWebPortal\lib\PosixGroup;

enum UserFlag: string
{
case ADMIN = "admin";
case GHOST = "ghost";
case IDLELOCKED = "idlelocked";
case LOCKED = "locked";
case QUALIFIED = "qualified";
}

/**
* An LDAP connection class which extends LDAPConn tailored for the UnityHPC Platform
Expand Down Expand Up @@ -35,8 +45,8 @@ class UnityLDAP extends LDAPConn
private LDAPEntry $groupOU;
private LDAPEntry $pi_groupOU;
private LDAPEntry $org_groupOU;
private LDAPEntry $adminGroup;
private LDAPEntry $qualifiedUserGroup;

public array $userFlagGroups;

public function __construct()
{
Expand All @@ -46,8 +56,11 @@ public function __construct()
$this->groupOU = $this->getEntry(CONFIG["ldap"]["group_ou"]);
$this->pi_groupOU = $this->getEntry(CONFIG["ldap"]["pigroup_ou"]);
$this->org_groupOU = $this->getEntry(CONFIG["ldap"]["orggroup_ou"]);
$this->adminGroup = $this->getEntry(CONFIG["ldap"]["admin_group"]);
$this->qualifiedUserGroup = $this->getEntry(CONFIG["ldap"]["qualified_user_group"]);
$this->userFlagGroups = [];
foreach (UserFlag::cases() as $flag) {
$dn = CONFIG["ldap"]["user_flag_groups"][$flag->value];
$this->userFlagGroups[$flag->value] = new PosixGroup(new LDAPEntry($this->conn, $dn));
}
}

public function getUserOU(): LDAPEntry
Expand All @@ -70,16 +83,6 @@ public function getOrgGroupOU(): LDAPEntry
return $this->org_groupOU;
}

public function getAdminGroup(): LDAPEntry
{
return $this->adminGroup;
}

public function getQualifiedUserGroup(): LDAPEntry
{
return $this->qualifiedUserGroup;
}

public function getDefUserShell(): string
{
return $this->def_user_shell;
Expand Down Expand Up @@ -187,31 +190,11 @@ private function getAllGIDNumbersInUse(): array
);
}

public function getQualifiedUsersUIDs(): array
{
// should not use $user_ou->getChildren or $base_ou->getChildren(objectClass=posixAccount)
// qualified users might be outside user ou, and not all users in LDAP tree are qualified users
return $this->qualifiedUserGroup->getAttribute("memberuid");
}

public function getQualifiedUsers($UnitySQL, $UnityMailer, $UnityWebhook): array
{
$out = [];

$qualifiedUsers = $this->getQualifiedUsersUIDs();
sort($qualifiedUsers);
foreach ($qualifiedUsers as $user) {
$params = [$user, $this, $UnitySQL, $UnityMailer, $UnityWebhook];
array_push($out, new UnityUser(...$params));
}
return $out;
}

public function getQualifiedUsersAttributes(
array $attributes,
array $default_values = [],
): array {
$include_uids = $this->getQualifiedUsersUIDs();
$include_uids = $this->userFlagGroups["qualified"]->getMemberUIDs();
$user_attributes = $this->baseOU->getChildrenArrayStrict(
$attributes,
true, // recursive
Expand Down Expand Up @@ -308,7 +291,7 @@ public function getAllPIGroupOwnerAttributes(
public function getQualifiedUID2PIGIDs(): array
{
// initialize output so each UID is a key with an empty array as its value
$uids = $this->getQualifiedUsersUIDs();
$uids = $this->userFlagGroups["qualified"]->getMemberUIDs();
$uid2pigids = array_combine($uids, array_fill(0, count($uids), []));
// for each PI group, append that GID to the member list for each of its member UIDs
foreach (
Expand Down
55 changes: 31 additions & 24 deletions resources/lib/UnityUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,35 +97,51 @@ public function init(
$this->SQL->addLog($this->uid, $_SERVER["REMOTE_ADDR"], "user_added", $this->uid);
}

public function isQualified(): bool
public function getFlag(UserFlag $flag): bool
{
return $this->LDAP->getQualifiedUserGroup()->attributeValueExists("memberUid", $this->uid);
return $this->LDAP->userFlagGroups[$flag->value]->memberUIDExists($this->uid);
}

public function setIsQualified(bool $newIsQualified, bool $doSendMail = true): void
{
$oldIsQualified = $this->isQualified();
if ($oldIsQualified == $newIsQualified) {
public function setFlag(
UserFlag $flag,
bool $newValue,
bool $doSendMail = true,
bool $doSendMailAdmin = true,
): void {
$oldValue = $this->getFlag($flag);
if ($oldValue == $newValue) {
return;
}
if ($newIsQualified) {
$this->LDAP->getQualifiedUserGroup()->appendAttribute("memberuid", $this->uid);
$this->LDAP->getQualifiedUserGroup()->write();
if ($newValue) {
$this->LDAP->userFlagGroups[$flag->value]->addMemberUID($this->uid);
if ($doSendMail) {
$this->MAILER->sendMail($this->getMail(), "user_qualified", [
$this->MAILER->sendMail($this->getMail(), "user_flag_added", [
"user" => $this->uid,
"org" => $this->getOrg(),
"flag" => $flag,
]);
}
if ($doSendMailAdmin) {
$this->MAILER->sendMail("admin", "user_flag_added_admin", [
"user" => $this->uid,
"org" => $this->getOrg(),
"flag" => $flag,
]);
}
} else {
$this->LDAP
->getQualifiedUserGroup()
->removeAttributeEntryByValue("memberuid", $this->uid);
$this->LDAP->getQualifiedUserGroup()->write();
$this->LDAP->userFlagGroups[$flag->value]->removeMemberUID($this->uid);
if ($doSendMail) {
$this->MAILER->sendMail($this->getMail(), "user_dequalified", [
$this->MAILER->sendMail($this->getMail(), "user_flag_removed", [
"user" => $this->uid,
"org" => $this->getOrg(),
"flag" => $flag,
]);
}
if ($doSendMailAdmin) {
$this->MAILER->sendMail("admin", "user_flag_removed_admin", [
"user" => $this->uid,
"org" => $this->getOrg(),
"flag" => $flag,
]);
}
}
Expand Down Expand Up @@ -319,15 +335,6 @@ public function getHomeDir(): string
return $this->entry->getAttribute("homedirectory");
}

/**
* Checks if the current account is an admin
*/
public function isAdmin(): bool
{
$admins = $this->LDAP->getAdminGroup()->getAttribute("memberuid");
return in_array($this->uid, $admins);
}

/**
* Checks if current user is a PI
*/
Expand Down
10 changes: 0 additions & 10 deletions resources/mail/user_dequalified.php

This file was deleted.

55 changes: 55 additions & 0 deletions resources/mail/user_flag_added.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php use UnityWebPortal\lib\UserFlag; ?>
<?php switch ($data["flag"]):
case UserFlag::QUALIFIED: ?>
<?php $this->Subject = "User Activated"; ?>
<p>Hello,</p>
<p>Your account on the UnityHPC Platform has been activated. Your account details are below:</p>
<p>
<strong>Username</strong> <?php echo $data["user"]; ?>
<br>
<strong>Organization</strong> <?php echo $data["org"]; ?>
</p>
<p>
See the
<a href="<?php echo CONFIG["site"]["getting_started_url"]; ?>">Getting Started</a>
page in our documentation for next steps.
</p>
<p>If you believe this to be a mistake, please reply to this email as soon as possible.</p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::GHOST: ?>
<?php $this->Subject = "User Deleted"; ?>
<p>Hello,</p>
<p>Your account on the UnityHPC Platform has been deleted.</p>
<p>If you believe this to be a mistake, please reply to this email as soon as possible.</p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::LOCKED: ?>
<?php $this->Subject = "User Locked"; ?>
<p>Hello,</p>
<p>Your account on the UnityHPC Platform has been locked.</p>
<p>If you believe this to be a mistake, please reply to this email as soon as possible.</p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::IDLELOCKED: ?>
<?php $this->Subject = "User Locked"; ?>
<p>Hello,</p>
<p>Your account on the UnityHPC Platform has been locked due to inactivity.</p>
<p>If you believe this to be a mistake, please reply to this email as soon as possible.</p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::ADMIN: ?>
<?php $this->Subject = "User Promoted"; ?>
<p>Hello,</p>
<p>Your account on the UnityHPC Platform has been promoted to admin.</p>
<p>If you believe this to be a mistake, please reply to this email as soon as possible.</p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php default: ?>
<?php throw new \Exception("unknown flag: " . $data["flag"]); ?>
<?php endswitch; ?>
40 changes: 40 additions & 0 deletions resources/mail/user_flag_added_admin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php use UnityWebPortal\lib\UserFlag; ?>
<?php switch ($data["flag"]):
case UserFlag::QUALIFIED: ?>
<?php $this->Subject = "User Qualified"; ?>
<p>Hello,</p>
<p>User "<?php echo $data["user"] ?>" has been qualified. </p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::GHOST: ?>
<?php $this->Subject = "User Ghosted"; ?>
<p>Hello,</p>
<p>User "<?php echo $data["user"] ?>" has been marked as ghost. </p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::LOCKED: ?>
<?php $this->Subject = "User Locked"; ?>
<p>Hello,</p>
<p>User "<?php echo $data["user"] ?>" has been locked. </p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::IDLELOCKED: ?>
<?php $this->Subject = "User Idle Locked"; ?>
<p>Hello,</p>
<p>User "<?php echo $data["user"] ?>" has been idle locked. </p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php case UserFlag::ADMIN: ?>
<?php $this->Subject = "User Promoted"; ?>
<p>Hello,</p>
<p>User "<?php echo $data["user"] ?>" has been promoted to admin. </p>
<?php break; ?>

<?php /////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php default: ?>
<?php throw new \Exception("unknown flag: " . $data["flag"]); ?>
<?php endswitch; ?>
Loading