From 913dcbb8556ef6e09535d1972f144e0ddb46695b Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 11:27:00 -0500 Subject: [PATCH 01/13] prettier --- .gitignore | 2 ++ .pre-commit-config.yaml | 28 ++++++++++++++++++++++++++++ CONTRIBUTING.md | 2 ++ prettierrc.yml | 4 ++++ 4 files changed, 36 insertions(+) create mode 100644 prettierrc.yml diff --git a/.gitignore b/.gitignore index 94aa9f54..d08024d2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ deployment/**/* !deployment/**/README.md .phpunit.result.cache + +node_modules diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 97d65767..41455ad0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,6 +26,34 @@ repos: workers/.*| )$ + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.4.2 + hooks: + - id: prettier + exclude: | + (?x)^( + composer\.json| + composer\.lock| + )$ + + - repo: local + hooks: + - id: prettier (php) + name: prettier (php) + entry: npx + args: [prettier, --plugin=@prettier/plugin-php, --write] + language: system + files: \.php$ + # prettier doesn't do well with the html files with Date: Fri, 7 Nov 2025 11:41:43 -0500 Subject: [PATCH 02/13] no more psr12 --- .phpcs-ruleset.xml | 11 ----------- .pre-commit-config.yaml | 29 +++-------------------------- CONTRIBUTING.md | 5 ++--- 3 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 .phpcs-ruleset.xml diff --git a/.phpcs-ruleset.xml b/.phpcs-ruleset.xml deleted file mode 100644 index 13faff04..00000000 --- a/.phpcs-ruleset.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - PSR12, with max line length = 100 characters - - - - - - - - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 41455ad0..61ff708f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,21 +10,9 @@ repos: rev: v5.0.0 hooks: - id: trailing-whitespace - - id: end-of-file-fixer - - - repo: local - hooks: - - id: phpcbf - name: PHP Code Beautifier and Fixer - entry: phpcbf - language: system - files: \.php$ - args: [--standard=./.phpcs-ruleset.xml, --colors] - exclude: | - (?x)^( - test/.*| - workers/.*| - )$ + # prettier should take care of this, but it has this one problem with shebang files + # https://github.com/prettier/plugin-php/issues/2430 + # - id: end-of-file-fixer - repo: https://github.com/rbubley/mirrors-prettier rev: v3.4.2 @@ -71,17 +59,6 @@ repos: - repo: local hooks: - - id: phpcs - name: PHP CodeSniffer - entry: phpcs - language: system - files: \.php$ - args: [--standard=./.phpcs-ruleset.xml, --colors] - exclude: | - (?x)^( - test/.*| - workers/.*| - )$ - id: php-l name: php -l entry: php diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59b83eb9..4bc035a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,9 +3,8 @@ ## Conventions * PHP version 8.3. -* All files are required to be linted with PSR-12 standard. -* All files are required to be formatted with Prettier and then PHPCBF. -* The maximum line length for any PHP file is 100 characters, instead of PSR-12's 120 characters. +* All files are required to be formatted with Prettier. +* The maximum line length for any PHP file is 100 characters. * Comments should be used sparingly. * Empty lines should be used sparingly. * No code should fail quietly, instead exceptions should be thrown. From ea174a92d3a4a3c2741c4bdf7e191bbbf79731e1 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 11:52:48 -0500 Subject: [PATCH 03/13] format everything with prettier --- .github/workflows/phpunit.yml | 32 +- .github/workflows/pre-commit.yml | 20 +- CONTRIBUTING.md | 43 +- README.md | 181 ++++---- coverage.php | 6 +- resources/config.php | 8 +- resources/init.php | 30 +- resources/lib/UnityConfig.php | 16 +- resources/lib/UnityGithub.php | 10 +- resources/lib/UnityGroup.php | 214 +++++----- resources/lib/UnityHTTPD.php | 62 ++- resources/lib/UnityLDAP.php | 170 +++++--- resources/lib/UnityMailer.php | 44 +- resources/lib/UnityOrg.php | 4 +- resources/lib/UnityPerms.php | 54 ++- resources/lib/UnityRedis.php | 6 +- resources/lib/UnitySQL.php | 112 +++-- resources/lib/UnitySSO.php | 41 +- resources/lib/UnityUser.php | 128 +++--- resources/lib/UnityWebhook.php | 22 +- .../lib/exceptions/ArrayKeyException.php | 4 +- .../EncodingConversionException.php | 4 +- .../exceptions/EncodingUnknownException.php | 4 +- resources/lib/exceptions/EnsureException.php | 4 +- resources/lib/exceptions/NoDieException.php | 4 +- resources/lib/exceptions/SSOException.php | 4 +- resources/lib/utils.php | 15 +- .../mail/account_deletion_request_admin.php | 3 +- resources/mail/group_created.php | 3 +- resources/mail/group_denied.php | 3 +- resources/mail/group_disband.php | 7 +- .../mail/group_join_request_cancelled.php | 8 +- resources/mail/group_request.php | 3 +- resources/mail/group_request_admin.php | 3 +- resources/mail/group_request_cancelled.php | 7 +- resources/mail/group_user_added.php | 3 +- resources/mail/group_user_added_owner.php | 5 +- resources/mail/group_user_denied.php | 7 +- resources/mail/group_user_denied_owner.php | 7 +- resources/mail/group_user_removed.php | 3 +- resources/mail/group_user_removed_owner.php | 5 +- resources/mail/group_user_request.php | 3 +- resources/mail/group_user_request_owner.php | 5 +- resources/mail/user_created.php | 3 +- resources/mail/user_loginshell.php | 7 +- resources/mail/user_sshkey.php | 9 +- .../functional/AccountDeletionRequestTest.php | 27 +- test/functional/InvalidEPPNTest.php | 7 +- test/functional/LoginShellSetTest.php | 21 +- test/functional/NewUserTest.php | 155 ++++--- test/functional/PIMemberRequestTest.php | 25 +- test/functional/PageLoadTest.php | 5 +- test/functional/PiBecomeRequestTest.php | 47 +- test/functional/PiMemberApproveTest.php | 47 +- test/functional/PiMemberDenyTest.php | 33 +- test/functional/PiRemoveUserTest.php | 22 +- test/functional/SSHKeyAddTest.php | 79 ++-- test/functional/SSHKeyDeleteTest.php | 21 +- test/functional/ViewAsUserTest.php | 29 +- test/phpunit-bootstrap.php | 113 +++-- test/unit/AjaxSshValidateTest.php | 5 +- test/unit/UnityGithubTest.php | 7 +- test/unit/UnitySSOTest.php | 12 +- test/unit/UtilsTest.php | 20 +- tools/docker-dev/docker-compose.yml | 18 +- .../identity/phpldapadmin-config.php | 25 +- tools/docker-dev/sql/phpmyadmin-config.php | 16 +- webroot/css/filters.css | 16 +- webroot/css/global.css | 400 +++++++++--------- webroot/css/modal.css | 75 ++-- webroot/css/navbar.css | 162 +++---- webroot/css/tables.css | 124 +++--- webroot/js/filter.js | 146 ++++--- webroot/js/global.js | 38 +- webroot/js/modal.js | 23 +- webroot/js/sort.js | 138 +++--- webroot/js/tables.js | 91 ++-- workers/clear-audit-log.php | 10 +- workers/group_user_request_owner_reminder.php | 20 +- workers/remove-users-from-group.php | 13 +- workers/update-ldap-cache.php | 55 ++- 81 files changed, 1893 insertions(+), 1488 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 84702a15..79c16afa 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -9,19 +9,19 @@ jobs: docker-compose-phpunit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: "8.3" - tools: composer - - name: install composer dependencies - run: composer update - - name: Run docker compose - uses: hoverkraft-tech/compose-action@v2.0.1 - with: - compose-file: "./tools/docker-dev/docker-compose.yml" - - name: Execute tests in the running services - run: docker compose -f ./tools/docker-dev/docker-compose.yml exec -w '/var/www/unity-web-portal' web bash -c 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover="$(mktemp --suffix=.xml)" -d --min-coverage=./coverage.php' + - uses: actions/checkout@v3 + with: + submodules: true + - name: setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.3" + tools: composer + - name: install composer dependencies + run: composer update + - name: Run docker compose + uses: hoverkraft-tech/compose-action@v2.0.1 + with: + compose-file: "./tools/docker-dev/docker-compose.yml" + - name: Execute tests in the running services + run: docker compose -f ./tools/docker-dev/docker-compose.yml exec -w '/var/www/unity-web-portal' web bash -c 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover="$(mktemp --suffix=.xml)" -d --min-coverage=./coverage.php' diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 929a14b2..3a35b365 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -9,13 +9,13 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: actions/setup-python@v3 - - name: setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: "8.3" - tools: composer, phpcs, phpcbf - - uses: pre-commit/action@v3.0.1 + - uses: actions/checkout@v3 + with: + submodules: true + - uses: actions/setup-python@v3 + - name: setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.3" + tools: composer, phpcs, phpcbf + - uses: pre-commit/action@v3.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4bc035a3..eb6f717e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,24 +2,24 @@ ## Conventions -* PHP version 8.3. -* All files are required to be formatted with Prettier. -* The maximum line length for any PHP file is 100 characters. -* Comments should be used sparingly. -* Empty lines should be used sparingly. -* No code should fail quietly, instead exceptions should be thrown. +- PHP version 8.3. +- All files are required to be formatted with Prettier. +- The maximum line length for any PHP file is 100 characters. +- Comments should be used sparingly. +- Empty lines should be used sparingly. +- No code should fail quietly, instead exceptions should be thrown. PHP builtin functions that fail quietly (ex: `json_encode`) should be replaced with a wrapper in `resources/utils.php`. -* No code should call `die()` or `exit()`, instead `UnityHTTPD::die()`. +- No code should call `die()` or `exit()`, instead `UnityHTTPD::die()`. This will avoid the premature death of our automated testing processes. -* No code should call `assert()`, instead `\ensure()`. +- No code should call `assert()`, instead `\ensure()`. This will enforce conditions even in production. -* No code should call `json_encode()`, instead `\jsonEncode()`. +- No code should call `json_encode()`, instead `\jsonEncode()`. This will throw errors and escape slashes by default. -* No code should call `mb_convert_encoding()`, instead `\mbConvertEncoding()`. +- No code should call `mb_convert_encoding()`, instead `\mbConvertEncoding()`. This will throw an exception rather than returning `false`. -* No code should call `mb_detect_encoding()`, instead `\mbDetectEncoding()`. +- No code should call `mb_detect_encoding()`, instead `\mbDetectEncoding()`. This will enable strict mode and throw an exception rather than returning `false`. -* `UnityHTTPD`'s user-facing error functionality (ex: `badRequest`) should only be called from `webroot/**/*.php`. +- `UnityHTTPD`'s user-facing error functionality (ex: `badRequest`) should only be called from `webroot/**/*.php`. `resources/**/*.php` should throw exceptions instead. This repository will automatically check PRs for linting compliance. @@ -44,10 +44,10 @@ This repository will automatically check PRs for linting compliance. While the environment is running, the following is accessible: -* http://127.0.0.1:8000 - Web Portal -* http://127.0.0.1:8010 - PHPLDAPAdmin Portal -* http://127.0.0.1:8020 - PHPMyAdmin Portal -* http://127.0.0.1:8030 - Mailcatcher Portal +- http://127.0.0.1:8000 - Web Portal +- http://127.0.0.1:8010 - PHPLDAPAdmin Portal +- http://127.0.0.1:8020 - PHPMyAdmin Portal +- http://127.0.0.1:8030 - Mailcatcher Portal ### Test Users @@ -56,9 +56,10 @@ When accessing locked down portions of the portal, you will be asked for a usern The password is always `password`. `tools/docker-dev/web/htpasswd` contains all valid usernames. Notable users: -* `user1@org1.test` - admin, PI -* `user2@org1.test` - not admin, not PI -* `user2000@org2.test` - does not yet have an account + +- `user1@org1.test` - admin, PI +- `user2@org1.test` - not admin, not PI +- `user2000@org2.test` - does not yet have an account ### Changes to Dev Environment @@ -87,6 +88,7 @@ Once a user action has been taken, internal interfaces are used to verify the re To run `phpunit`, spawn 2 shells in differnt tabs: tab 1: + ```shell cd ./tools/docker-dev ./build.sh @@ -94,6 +96,7 @@ cd ./tools/docker-dev ``` tab 2: + ``` $ container="$(docker container ls | grep web | awk '{print $1}')" $ docker exec -it "$container" bash @@ -139,6 +142,7 @@ When writing a test, it may be tempting to use the PHP API directly, but the HTT Example: using the PHP API: + ```php private function requestGroupCreation() { @@ -147,6 +151,7 @@ private function requestGroupCreation() ``` using the HTTP API: + ```php private function requestGroupCreation() { diff --git a/README.md b/README.md index d3f1b054..ab20b422 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,84 @@ ![UNITY](https://user-images.githubusercontent.com/40907639/137608695-2d914da2-1ecc-480b-a47e-a9e33b2b1b45.png) # Unity Web Portal + Unity Web Portal is a PHP application built in top of MariaDB and LDAP which acts as a central user portal for high-performance-computing clusters. Basic Features: - * User signs up - * PIs require admin approval, users require PI approval - * User manages SSH public keys - * no passwords - * Github import, upload file, paste, generate and download private key - * User changes login shell - * User requests their own PI group - * PI approves/denies requests to join their PI group - * PI removes members from their group + +- User signs up + - PIs require admin approval, users require PI approval +- User manages SSH public keys + - no passwords + - Github import, upload file, paste, generate and download private key +- User changes login shell +- User requests their own PI group +- PI approves/denies requests to join their PI group +- PI removes members from their group Admin features: - * Automatic updating of LDAP to reflect current state of users, groups, organizations, PI groups - * Cluster notices - * Added to front page, mailed, and exposed via REST API - * WYSIWYG HTML editor for webpage contents, cluster notices - * Branding customization for multiple domains simultaneously - * Custom UIDNumber / GIDNumber mappings for specific users - * Login as another user - * Mailing + +- Automatic updating of LDAP to reflect current state of users, groups, organizations, PI groups +- Cluster notices + - Added to front page, mailed, and exposed via REST API +- WYSIWYG HTML editor for webpage contents, cluster notices +- Branding customization for multiple domains simultaneously +- Custom UIDNumber / GIDNumber mappings for specific users +- Login as another user +- Mailing ## Installation/Deployment See the Docker Compose environment (`tools/docker-dev/`) for an (unsafe for production) example. 1. OpenLDAP server - * Structure should be similar to `tools/docker-dev/identity/bootstrap.ldif` - * Also see `tools/docker-dev/identity/{config,ssh}.ldif` - * recommended openldap modules/overlays: - * `unique`: prevent UIDNumber, GIDNumber conflicts - * `pw-sha2`: allow the use of sha2 password hashing algorithms for bind + - Structure should be similar to `tools/docker-dev/identity/bootstrap.ldif` + - Also see `tools/docker-dev/identity/{config,ssh}.ldif` + - recommended openldap modules/overlays: + - `unique`: prevent UIDNumber, GIDNumber conflicts + - `pw-sha2`: allow the use of sha2 password hashing algorithms for bind 1. MySQL / MariaDB server - * Structure should be similar to `tools/docker-dev/sql/bootstrap.sql` + - Structure should be similar to `tools/docker-dev/sql/bootstrap.sql` 1. SMTP server 1. Web server - * This repository cloned - * `deployment/config/config.ini` should be owned by the apache user (`www-data` on Ubuntu), with mode `0600` - * Submodules checked out (`git submodule update --checkout --init`) - * Composer (`apt install composer` on Ubuntu) - * Dependencies: - * PHP extensions - * curl, intl, ldap, mbstring, mysql, pdo, redis, xml (`apt install php-` on Ubuntu) - * Libraries - * `COMPOSER_ALLOW_SUPERUSER=1 composer --no-dev --no-scripts --no-plugins install` - * `httpd` `DocumentRoot` set to `webroot/` - * `httpd` Authentication - * Any authentication will do as long as it defines `REMOTE_USER`, `givenName`, `sn`, and `mail` - * `REMOTE_USER` must also be unique, non-reassignable, and persistent - * Unity uses Shibboleth SP and the Apache Shibboleth module (`apt install shibboleth-sp-utils libapache2-mod-shib` on Ubuntu) - * `httpd` Authorization - * Global access to `webroot/panel/` - * Restricted access to `webroot/admin/` - * No access anywhere else + - This repository cloned + - `deployment/config/config.ini` should be owned by the apache user (`www-data` on Ubuntu), with mode `0600` + - Submodules checked out (`git submodule update --checkout --init`) + - Composer (`apt install composer` on Ubuntu) + - Dependencies: + - PHP extensions + - curl, intl, ldap, mbstring, mysql, pdo, redis, xml (`apt install php-` on Ubuntu) + - Libraries + - `COMPOSER_ALLOW_SUPERUSER=1 composer --no-dev --no-scripts --no-plugins install` + - `httpd` `DocumentRoot` set to `webroot/` + - `httpd` Authentication + - Any authentication will do as long as it defines `REMOTE_USER`, `givenName`, `sn`, and `mail` + - `REMOTE_USER` must also be unique, non-reassignable, and persistent + - Unity uses Shibboleth SP and the Apache Shibboleth module (`apt install shibboleth-sp-utils libapache2-mod-shib` on Ubuntu) + - `httpd` Authorization + - Global access to `webroot/panel/` + - Restricted access to `webroot/admin/` + - No access anywhere else ## Configuration + 1. Create `deployment/config/config.ini` using `/deployment/defaults/config.ini` as a reference - * Make sure this file is not world readable! + - Make sure this file is not world readable! 1. If using mulitple domains, create `deployment/overrides//config/config.ini` 1. If using custom UIDNumber/GIDNumber mappings, create `deployment/custom_user_mappings/*.csv` - * The 1st column is UID, the 2nd column is both UIDNumber and GIDNumber + - The 1st column is UID, the 2nd column is both UIDNumber and GIDNumber 1. Add logos to `webroot/assets/footer_logos/` ## Integration + The scope of this project ends at being responsible for the LDAP user database. We recommend production deployments to set up scripts which detect changes in LDAP and then perform further actions. For example, Unity uses such scripts to create home directories and add records to the Slurm account database. ## Updating + We recommend a deployment where each version of the portal is its own clone, then just change a symlink to point to the new version. This way a rollback is much easier. Example folder structure, where `->` indicates a symlink: + ``` /srv/www/ unity-web-portal -> unity-web-portal-1.1.0 @@ -107,51 +114,55 @@ rm "$prod" && ln -s "$old" "$prod" ### Version-specific update instructions: ### 1.2 -> 1.3 -* SQL: - * remove the `sitevars` table -* `defaults/config.ini.default` has some new fields that need to be overriden: - * `offset_UIDGID` - * `offset_PIGID` - * `offset_ORGGID` -* `custom_user_mappings` can no longer match with just the 1st segment of the logged in user's UID, an exact match is required -* LDAP: - * create the `gecos` attribute for all users by concatenating `givenName` and `sn` + +- SQL: + - remove the `sitevars` table +- `defaults/config.ini.default` has some new fields that need to be overriden: + - `offset_UIDGID` + - `offset_PIGID` + - `offset_ORGGID` +- `custom_user_mappings` can no longer match with just the 1st segment of the logged in user's UID, an exact match is required +- LDAP: + - create the `gecos` attribute for all users by concatenating `givenName` and `sn` ### 1.2.0 -> 1.2.1 -* SQL: - * Add new columns to the `requests` table: - ```sql - ALTER TABLE `requests` - ADD `firstname` VARCHAR(768) NOT NULL AFTER `timestamp`, - ADD `lastname` VARCHAR(768) NOT NULL AFTER `firstname`, - ADD `email` VARCHAR(768) NOT NULL AFTER `lastname`, - ADD `org` VARCHAR(768) NOT NULL AFTER `email`; - ``` + +- SQL: + - Add new columns to the `requests` table: + ```sql + ALTER TABLE `requests` + ADD `firstname` VARCHAR(768) NOT NULL AFTER `timestamp`, + ADD `lastname` VARCHAR(768) NOT NULL AFTER `firstname`, + ADD `email` VARCHAR(768) NOT NULL AFTER `lastname`, + ADD `org` VARCHAR(768) NOT NULL AFTER `email`; + ``` ### 1.1 -> 1.2 -* SQL: - * Create the `sitevars` table (see `bootstrap.sql` for details) - * Create the `audit_log` table (see `bootstrap.sql` for details) - * Create the `account_deletion_requests` table (see `bootstrap.sql` for details) - * Create the `user_last_logins` table (see `bootstrap.sql` for details) - * Drop the `sso_log` table - * Drop the `events` table - * Reduce the size of all `varchar(1000)` columns to `varchar(768)` - * Delete the `priv` row in the `pages` table (if moving site policy to external site) - * Add the `account_policy` row in the `pages` table (if NOT moving site policy to external site) -* `defaults/config.ini.default` has some new fields that may need to be overriden: - * `ldap.user_group` - * `site.terms_of_service_url` - * example, created account policy page: `https://unity.rc.umass.edu/panel/account_policy.php` - * `site.account_policy_url` - * example, using old site policy page: `https://unity.rc.umass.edu/panel/priv.php` -* LDAP: - * Create a new group defined by `ldap.user_group` in the config + +- SQL: + - Create the `sitevars` table (see `bootstrap.sql` for details) + - Create the `audit_log` table (see `bootstrap.sql` for details) + - Create the `account_deletion_requests` table (see `bootstrap.sql` for details) + - Create the `user_last_logins` table (see `bootstrap.sql` for details) + - Drop the `sso_log` table + - Drop the `events` table + - Reduce the size of all `varchar(1000)` columns to `varchar(768)` + - Delete the `priv` row in the `pages` table (if moving site policy to external site) + - Add the `account_policy` row in the `pages` table (if NOT moving site policy to external site) +- `defaults/config.ini.default` has some new fields that may need to be overriden: + - `ldap.user_group` + - `site.terms_of_service_url` + - example, created account policy page: `https://unity.rc.umass.edu/panel/account_policy.php` + - `site.account_policy_url` + - example, using old site policy page: `https://unity.rc.umass.edu/panel/priv.php` +- LDAP: + - Create a new group defined by `ldap.user_group` in the config ### 1.0 -> 1.1 -* SQL: - * Add the `home` content management row -* `config/branding/config.ini.default` has some new fields that may need to be overriden: - * `mail.pi_approve*` - * `page.home` - * The entire `loginshell` section + +- SQL: + - Add the `home` content management row +- `config/branding/config.ini.default` has some new fields that may need to be overriden: + - `mail.pi_approve*` + - `page.home` + - The entire `loginshell` section diff --git a/coverage.php b/coverage.php index f2a48ae7..44119cb9 100644 --- a/coverage.php +++ b/coverage.php @@ -3,9 +3,5 @@ use RobinIngelbrecht\PHPUnitCoverageTools\MinCoverage\MinCoverageRule; return [ - new MinCoverageRule( - pattern: '*', - minCoverage: 62, - exitOnLowCoverage: true - ), + new MinCoverageRule(pattern: "*", minCoverage: 62, exitOnLowCoverage: true), ]; diff --git a/resources/config.php b/resources/config.php index ac43e9ed..92014ab1 100644 --- a/resources/config.php +++ b/resources/config.php @@ -2,4 +2,10 @@ use UnityWebPortal\lib\UnityConfig; -define("CONFIG", UnityConfig::getConfig(__DIR__ . "/../defaults", __DIR__ . "/../deployment")); +define( + "CONFIG", + UnityConfig::getConfig( + __DIR__ . "/../defaults", + __DIR__ . "/../deployment", + ), +); diff --git a/resources/init.php b/resources/init.php index 770e4e72..d1e08d3d 100644 --- a/resources/init.php +++ b/resources/init.php @@ -15,7 +15,10 @@ use UnityWebPortal\lib\UnityHTTPD; if (CONFIG["site"]["enable_exception_handler"]) { - set_exception_handler(["UnityWebPortal\lib\UnityHTTPD", "exceptionHandler"]); + set_exception_handler([ + "UnityWebPortal\lib\UnityHTTPD", + "exceptionHandler", + ]); } session_start(); @@ -32,15 +35,30 @@ $WEBHOOK = new UnityWebhook(); $GITHUB = new UnityGithub(); -if (isset($_SERVER["REMOTE_USER"])) { // Check if SSO is enabled on this page +if (isset($_SERVER["REMOTE_USER"])) { + // Check if SSO is enabled on this page $SSO = UnitySSO::getSSO(); $_SESSION["SSO"] = $SSO; - $OPERATOR = new UnityUser($SSO["user"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $OPERATOR = new UnityUser( + $SSO["user"], + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); $_SESSION["is_admin"] = $OPERATOR->isAdmin(); if (isset($_SESSION["viewUser"]) && $_SESSION["is_admin"]) { - $USER = new UnityUser($_SESSION["viewUser"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $USER = new UnityUser( + $_SESSION["viewUser"], + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); } else { $USER = $OPERATOR; } @@ -51,9 +69,9 @@ $SQL->addLog( $OPERATOR->uid, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "user_login", - $OPERATOR->uid + $OPERATOR->uid, ); if (!$_SESSION["user_exists"]) { diff --git a/resources/lib/UnityConfig.php b/resources/lib/UnityConfig.php index c638df8e..ce8f05b5 100644 --- a/resources/lib/UnityConfig.php +++ b/resources/lib/UnityConfig.php @@ -6,10 +6,14 @@ class UnityConfig { public static function getConfig($def_config_loc, $deploy_loc) { - $CONFIG = parse_ini_file($def_config_loc . "/config.ini.default", true, INI_SCANNER_TYPED); + $CONFIG = parse_ini_file( + $def_config_loc . "/config.ini.default", + true, + INI_SCANNER_TYPED, + ); $CONFIG = self::pullConfig($CONFIG, $deploy_loc); if (array_key_exists("HTTP_HOST", $_SERVER)) { - $cur_url = $_SERVER['HTTP_HOST']; + $cur_url = $_SERVER["HTTP_HOST"]; $url_override_path = $deploy_loc . "/overrides/" . $cur_url; if (is_dir($url_override_path)) { $CONFIG = self::pullConfig($CONFIG, $url_override_path); @@ -21,8 +25,12 @@ public static function getConfig($def_config_loc, $deploy_loc) private static function pullConfig($CONFIG, $loc) { $file_loc = $loc . "/config/config.ini"; - if (file_exists(($file_loc))) { - $CONFIG_override = parse_ini_file($file_loc, true, INI_SCANNER_TYPED); + if (file_exists($file_loc)) { + $CONFIG_override = parse_ini_file( + $file_loc, + true, + INI_SCANNER_TYPED, + ); return array_replace_recursive($CONFIG, $CONFIG_override); } else { return $CONFIG; diff --git a/resources/lib/UnityGithub.php b/resources/lib/UnityGithub.php index e9898cee..33417f77 100644 --- a/resources/lib/UnityGithub.php +++ b/resources/lib/UnityGithub.php @@ -7,9 +7,7 @@ class UnityGithub public function getSshPublicKeys($username) { $url = "https://api.github.com/users/$username/keys"; - $headers = array( - "User-Agent: Unity Cluster User Portal" - ); + $headers = ["User-Agent: Unity Cluster User Portal"]; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); @@ -21,11 +19,13 @@ public function getSshPublicKeys($username) // normally returns array of objects each with a ->key attribute // if bad URL or no such user, returns status=404 object // if no keys, returns [] - if ((!is_array($keys)) || (count($keys) == 0)) { + if (!is_array($keys) || count($keys) == 0) { return []; } // phpcs:disable - return array_map(function($x){return $x->key;}, $keys); + return array_map(function ($x) { + return $x->key; + }, $keys); // phpcs:enable } } diff --git a/resources/lib/UnityGroup.php b/resources/lib/UnityGroup.php index 34e6ef02..54f98d72 100644 --- a/resources/lib/UnityGroup.php +++ b/resources/lib/UnityGroup.php @@ -44,7 +44,9 @@ public function equals($other_group) { if (!is_a($other_group, self::class)) { throw new Exception( - "Unable to check equality because the parameter is not a " . self::class . " object" + "Unable to check equality because the parameter is not a " . + self::class . + " object", ); } @@ -72,7 +74,7 @@ public function requestGroup( $email, $org, $send_mail_to_admins, - $send_mail = true + $send_mail = true, ) { if ($this->exists()) { return; @@ -80,43 +82,35 @@ public function requestGroup( if ($this->SQL->accDeletionRequestExists($this->getOwner()->uid)) { return; } - $this->SQL->addRequest($this->getOwner()->uid, $firstname, $lastname, $email, $org); + $this->SQL->addRequest( + $this->getOwner()->uid, + $firstname, + $lastname, + $email, + $org, + ); if ($send_mail) { - $this->MAILER->sendMail( - $email, - "group_request" - ); - $this->WEBHOOK->sendWebhook( - "group_request_admin", - array( - "user" => $this->getOwner()->uid, - "org" => $org, - "name" => "$firstname $lastname", - "email" => $email - ) - ); + $this->MAILER->sendMail($email, "group_request"); + $this->WEBHOOK->sendWebhook("group_request_admin", [ + "user" => $this->getOwner()->uid, + "org" => $org, + "name" => "$firstname $lastname", + "email" => $email, + ]); if ($send_mail_to_admins) { - $this->MAILER->sendMail( - "admin", - "group_request_admin", - array( - "user" => $this->getOwner()->uid, - "org" => $org, - "name" => "$firstname $lastname", - "email" => $email - ) - ); - } - $this->MAILER->sendMail( - "pi_approve", - "group_request_admin", - array( + $this->MAILER->sendMail("admin", "group_request_admin", [ "user" => $this->getOwner()->uid, "org" => $org, "name" => "$firstname $lastname", - "email" => $email - ) - ); + "email" => $email, + ]); + } + $this->MAILER->sendMail("pi_approve", "group_request_admin", [ + "user" => $this->getOwner()->uid, + "org" => $org, + "name" => "$firstname $lastname", + "email" => $email, + ]); } } @@ -136,23 +130,22 @@ public function approveGroup($operator = null, $send_mail = true) $request["lastname"], $request["email"], $request["org"], - $send_mail + $send_mail, ); } $this->init(); $this->SQL->removeRequest($this->getOwner()->uid); - $operator = is_null($operator) ? $this->getOwner()->uid : $operator->uid; + $operator = is_null($operator) + ? $this->getOwner()->uid + : $operator->uid; $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "approved_group", - $this->getOwner()->uid + $this->getOwner()->uid, ); if ($send_mail) { - $this->MAILER->sendMail( - $request["email"], - "group_created" - ); + $this->MAILER->sendMail($request["email"], "group_created"); } } @@ -161,23 +154,25 @@ public function approveGroup($operator = null, $send_mail = true) */ public function denyGroup($operator = null, $send_mail = true) { - $request = $this->SQL->getRequest($this->getOwner()->uid, UnitySQL::REQUEST_BECOME_PI); + $request = $this->SQL->getRequest( + $this->getOwner()->uid, + UnitySQL::REQUEST_BECOME_PI, + ); $this->SQL->removeRequest($this->getOwner()->uid); if ($this->exists()) { return; } - $operator = is_null($operator) ? $this->getOwner()->uid : $operator->uid; + $operator = is_null($operator) + ? $this->getOwner()->uid + : $operator->uid; $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "denied_group", - $this->getOwner()->uid + $this->getOwner()->uid, ); if ($send_mail) { - $this->MAILER->sendMail( - $request["email"], - "group_denied" - ); + $this->MAILER->sendMail($request["email"], "group_denied"); } } @@ -188,11 +183,9 @@ public function cancelGroupRequest($send_mail = true) } $this->SQL->removeRequest($this->getOwner()->uid); if ($send_mail) { - $this->MAILER->sendMail( - "admin", - "group_request_cancelled", - ["uid" => $this->getOwner()->uid], - ); + $this->MAILER->sendMail("admin", "group_request_cancelled", [ + "uid" => $this->getOwner()->uid, + ]); } } @@ -206,7 +199,7 @@ public function cancelGroupJoinRequest($user, $send_mail = true) $this->MAILER->sendMail( $this->getOwner()->getMail(), "group_join_request_cancelled", - ["uid" => $user->uid] + ["uid" => $user->uid], ); } } @@ -266,21 +259,20 @@ public function approveUser($new_user, $send_mail = true) $this->addUserToGroup($new_user); $this->SQL->removeRequest($new_user->uid, $this->gid); if ($send_mail) { - $this->MAILER->sendMail( - $new_user->getMail(), - "group_user_added", - array("group" => $this->gid) - ); + $this->MAILER->sendMail($new_user->getMail(), "group_user_added", [ + "group" => $this->gid, + ]); $this->MAILER->sendMail( $this->getOwner()->getMail(), "group_user_added_owner", - array( + [ "group" => $this->gid, "user" => $new_user->uid, - "name" => $request["firstname"] . " " . $request["lastname"], + "name" => + $request["firstname"] . " " . $request["lastname"], "email" => $request["email"], "org" => $request["org"], - ) + ], ); } } @@ -291,21 +283,19 @@ public function denyUser($new_user, $send_mail = true) // remove request, this will fail silently if the request doesn't exist $this->SQL->removeRequest($new_user->uid, $this->gid); if ($send_mail) { - $this->MAILER->sendMail( - $request["email"], - "group_user_denied", - array("group" => $this->gid) - ); + $this->MAILER->sendMail($request["email"], "group_user_denied", [ + "group" => $this->gid, + ]); $this->MAILER->sendMail( $this->getOwner()->getMail(), "group_user_denied_owner", - array( + [ "group" => $this->gid, "user" => $new_user->uid, "name" => $new_user->getFullName(), "email" => $new_user->getMail(), - "org" => $new_user->getOrg() - ) + "org" => $new_user->getOrg(), + ], ); } } @@ -316,7 +306,9 @@ public function removeUser($new_user, $send_mail = true) return; } if ($new_user->uid == $this->getOwner()->uid) { - throw new Exception("Cannot delete group owner from group. Disband group instead"); + throw new Exception( + "Cannot delete group owner from group. Disband group instead", + ); } // remove request, this will fail silently if the request doesn't exist $this->removeUserFromGroup($new_user); @@ -324,18 +316,18 @@ public function removeUser($new_user, $send_mail = true) $this->MAILER->sendMail( $new_user->getMail(), "group_user_removed", - array("group" => $this->gid) + ["group" => $this->gid], ); $this->MAILER->sendMail( $this->getOwner()->getMail(), "group_user_removed_owner", - array( + [ "group" => $this->gid, "user" => $new_user->uid, "name" => $new_user->getFullName(), "email" => $new_user->getMail(), - "org" => $new_user->getOrg() - ) + "org" => $new_user->getOrg(), + ], ); } } @@ -346,14 +338,20 @@ public function newUserRequest( $lastname, $email, $org, - $send_mail = true + $send_mail = true, ) { if ($this->memberExists($new_user)) { - UnityHTTPD::errorLog("warning", "user '$new_user' already in group"); + UnityHTTPD::errorLog( + "warning", + "user '$new_user' already in group", + ); return; } if ($this->requestExists($new_user)) { - UnityHTTPD::errorLog("warning", "user '$new_user' already requested group membership"); + UnityHTTPD::errorLog( + "warning", + "user '$new_user' already requested group membership", + ); return; } if ($this->SQL->accDeletionRequestExists($new_user->uid)) { @@ -362,21 +360,19 @@ public function newUserRequest( } $this->addRequest($new_user->uid, $firstname, $lastname, $email, $org); if ($send_mail) { - $this->MAILER->sendMail( - $email, - "group_user_request", - array("group" => $this->gid) - ); + $this->MAILER->sendMail($email, "group_user_request", [ + "group" => $this->gid, + ]); $this->MAILER->sendMail( $this->getOwner()->getMail(), "group_user_request_owner", - array( + [ "group" => $this->gid, "user" => $new_user->uid, "name" => "$firstname $lastname", "email" => $email, "org" => $org, - ) + ], ); } } @@ -384,7 +380,7 @@ public function newUserRequest( public function getRequests() { $requests = $this->SQL->getRequests($this->gid); - $out = array(); + $out = []; foreach ($requests as $request) { $user = new UnityUser( $request["uid"], @@ -392,19 +388,16 @@ public function getRequests() $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK - ); - array_push( - $out, - [ - $user, - $request["timestamp"], - $request["firstname"], - $request["lastname"], - $request["email"], - $request["org"], - ] + $this->WEBHOOK, ); + array_push($out, [ + $user, + $request["timestamp"], + $request["firstname"], + $request["lastname"], + $request["email"], + $request["org"], + ]); } return $out; } @@ -412,7 +405,7 @@ public function getRequests() public function getGroupMembers($ignorecache = false) { $members = $this->getGroupMemberUIDs($ignorecache); - $out = array(); + $out = []; foreach ($members as $member) { $user_obj = new UnityUser( $member, @@ -420,7 +413,7 @@ public function getGroupMembers($ignorecache = false) $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); array_push($out, $user_obj); } @@ -467,7 +460,7 @@ private function init() $nextGID = $this->LDAP->getNextPIGIDNumber(); $this->entry->setAttribute("objectclass", UnityLDAP::POSIX_GROUP_CLASS); $this->entry->setAttribute("gidnumber", strval($nextGID)); - $this->entry->setAttribute("memberuid", array($owner->uid)); + $this->entry->setAttribute("memberuid", [$owner->uid]); $this->entry->write(); $this->REDIS->appendCacheArray("sorted_groups", "", $this->gid); // TODO if we ever make this project based, @@ -497,7 +490,14 @@ public function memberExists($user) private function addRequest($uid, $firstname, $lastname, $email, $org) { - $this->SQL->addRequest($uid, $firstname, $lastname, $email, $org, $this->gid); + $this->SQL->addRequest( + $uid, + $firstname, + $lastname, + $email, + $org, + $this->gid, + ); } public function getOwner() @@ -508,7 +508,7 @@ public function getOwner() $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); } @@ -520,7 +520,9 @@ public static function ownerUID2GID($uid) public static function GID2OwnerUID($gid) { if (substr($gid, 0, strlen(self::PI_PREFIX)) != self::PI_PREFIX) { - throw new Exception("PI group GID doesn't have the correct prefix."); + throw new Exception( + "PI group GID doesn't have the correct prefix.", + ); } return substr($gid, strlen(self::PI_PREFIX)); } diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 61585a7c..688f42bc 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -27,7 +27,10 @@ public static function die($x = null, $show_user = false) public static function redirect($dest) { header("Location: $dest"); - self::errorToUser("Redirect failed, click here to continue.", 302); + self::errorToUser( + "Redirect failed, click here to continue.", + 302, + ); self::die(); } @@ -49,7 +52,8 @@ public static function errorLog( \jsonEncode($data); $output["data"] = $data; } catch (\JsonException $e) { - $output["data"] = "data could not be JSON encoded: " . $e->getMessage(); + $output["data"] = + "data could not be JSON encoded: " . $e->getMessage(); } } $output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null; @@ -61,7 +65,10 @@ public static function errorLog( $output["error"] = self::throwableToArray($error); } else { // newlines are bad for error log, but getTrace() is too verbose - $output["trace"] = explode("\n", (new \Exception())->getTraceAsString()); + $output["trace"] = explode( + "\n", + new \Exception()->getTraceAsString(), + ); } error_log("$title: " . \jsonEncode($output)); } @@ -85,12 +92,13 @@ private static function throwableToArray(\Throwable $t): array private static function errorToUser( string $msg, int $http_response_code, - ?string $errorid = null + ?string $errorid = null, ) { if (!CONFIG["site"]["enable_error_to_user"]) { return; } - $notes = "Please notify a Unity admin at " . CONFIG["mail"]["support"] . "."; + $notes = + "Please notify a Unity admin at " . CONFIG["mail"]["support"] . "."; if (!is_null($errorid)) { $notes = $notes . " Error ID: $errorid."; } @@ -105,7 +113,11 @@ private static function errorToUser( public static function badRequest($message, $error = null, $data = null) { $errorid = uniqid(); - self::errorToUser("Invalid requested action or submitted data.", 400, $errorid); + self::errorToUser( + "Invalid requested action or submitted data.", + 400, + $errorid, + ); self::errorLog("bad request", $message, $errorid, $error, $data); self::die($message); } @@ -118,11 +130,24 @@ public static function forbidden($message, $error = null, $data = null) self::die($message); } - public static function internalServerError($message, $error = null, $data = null) - { + public static function internalServerError( + $message, + $error = null, + $data = null, + ) { $errorid = uniqid(); - self::errorToUser("An internal server error has occurred.", 500, $errorid); - self::errorLog("internal server error", $message, $errorid, $error, $data); + self::errorToUser( + "An internal server error has occurred.", + 500, + $errorid, + ); + self::errorLog( + "internal server error", + $message, + $errorid, + $error, + $data, + ); self::die($message); } @@ -130,7 +155,10 @@ public static function internalServerError($message, $error = null, $data = null public static function exceptionHandler($e) { ini_set("log_errors", true); // in case something goes wrong and error is not logged - self::internalServerError("An internal server error has occurred.", error: $e); + self::internalServerError( + "An internal server error has occurred.", + error: $e, + ); ini_set("log_errors", false); // error logged successfully } @@ -139,7 +167,9 @@ public static function getPostData(...$keys) try { return \arrayGet($_POST, ...$keys); } catch (ArrayKeyException $e) { - self::badRequest('failed to get $_POST data', $e, ['$_POST' => $_POST]); + self::badRequest('failed to get $_POST data', $e, [ + '$_POST' => $_POST, + ]); } } @@ -151,7 +181,9 @@ public static function getUploadedFileContents( try { $tmpfile_path = \arrayGet($_FILES, $filename, "tmp_name"); } catch (ArrayKeyException $e) { - self::badRequest('no such uploaded file', $e, ['$_FILES' => $_FILES]); + self::badRequest("no such uploaded file", $e, [ + '$_FILES' => $_FILES, + ]); } $contents = file_get_contents($tmpfile_path); if ($contents === false) { @@ -169,6 +201,8 @@ public static function getUploadedFileContents( public static function alert(string $message) { // jsonEncode escapes quotes - echo ""; + echo ""; } } diff --git a/resources/lib/UnityLDAP.php b/resources/lib/UnityLDAP.php index 74e30c08..940f75a5 100644 --- a/resources/lib/UnityLDAP.php +++ b/resources/lib/UnityLDAP.php @@ -10,25 +10,19 @@ */ class UnityLDAP extends ldapConn { - private const RDN = "cn"; // The defauls RDN for LDAP entries is set to "common name" - - public const POSIX_ACCOUNT_CLASS = array( - "inetorgperson", - "posixAccount", - "top", - "ldapPublicKey" - ); - - public const POSIX_GROUP_CLASS = array( - "posixGroup", - "top" - ); - - private $custom_mappings_path = ( - __DIR__ - . "/../../" - . CONFIG["ldap"]["custom_user_mappings_dir"] - ); + private const RDN = "cn"; // The defauls RDN for LDAP entries is set to "common name" + + public const POSIX_ACCOUNT_CLASS = [ + "inetorgperson", + "posixAccount", + "top", + "ldapPublicKey", + ]; + + public const POSIX_GROUP_CLASS = ["posixGroup", "top"]; + + private $custom_mappings_path = + __DIR__ . "/../../" . CONFIG["ldap"]["custom_user_mappings_dir"]; private $def_user_shell = CONFIG["ldap"]["def_user_shell"]; private $offset_UIDGID = CONFIG["ldap"]["offset_UIDGID"]; private $offset_PIGID = CONFIG["ldap"]["offset_PIGID"]; @@ -45,7 +39,11 @@ class UnityLDAP extends ldapConn public function __construct() { - parent::__construct(CONFIG["ldap"]["uri"], CONFIG["ldap"]["user"], CONFIG["ldap"]["pass"]); + parent::__construct( + CONFIG["ldap"]["uri"], + CONFIG["ldap"]["user"], + CONFIG["ldap"]["pass"], + ); $this->baseOU = $this->getEntry(CONFIG["ldap"]["basedn"]); $this->userOU = $this->getEntry(CONFIG["ldap"]["user_ou"]); $this->groupOU = $this->getEntry(CONFIG["ldap"]["group_ou"]); @@ -92,13 +90,22 @@ public function getDefUserShell() public function getNextUIDGIDNumber($uid) { - $IDNumsInUse = array_merge($this->getAllUIDNumbersInUse(), $this->getAllGIDNumbersInUse()); + $IDNumsInUse = array_merge( + $this->getAllUIDNumbersInUse(), + $this->getAllGIDNumbersInUse(), + ); $customIDMappings = $this->getCustomIDMappings(); $customMappedID = $customIDMappings[$uid] ?? null; - if (!is_null($customMappedID) && !in_array($customMappedID, $IDNumsInUse)) { + if ( + !is_null($customMappedID) && + !in_array($customMappedID, $IDNumsInUse) + ) { return $customMappedID; } - if (!is_null($customMappedID) && in_array($customMappedID, $IDNumsInUse)) { + if ( + !is_null($customMappedID) && + in_array($customMappedID, $IDNumsInUse) + ) { UnityHTTPD::errorLog( "warning", "user '$uid' has a custom mapped IDNumber $customMappedID but it's already in use!", @@ -106,7 +113,10 @@ public function getNextUIDGIDNumber($uid) } return $this->getNextIDNumber( $this->offset_UIDGID, - array_merge($IDNumsInUse, array_values($this->getCustomIDMappings())) + array_merge( + $IDNumsInUse, + array_values($this->getCustomIDMappings()), + ), ); } @@ -114,7 +124,10 @@ public function getNextPIGIDNumber() { return $this->getNextIDNumber( $this->offset_PIGID, - array_merge($this->getAllGIDNumbersInUse(), array_values($this->getCustomIDMappings())) + array_merge( + $this->getAllGIDNumbersInUse(), + array_values($this->getCustomIDMappings()), + ), ); } @@ -122,7 +135,10 @@ public function getNextOrgGIDNumber() { return $this->getNextIDNumber( $this->offset_ORGGID, - array_merge($this->getAllGIDNumbersInUse(), array_values($this->getCustomIDMappings())) + array_merge( + $this->getAllGIDNumbersInUse(), + array_values($this->getCustomIDMappings()), + ), ); } @@ -130,13 +146,16 @@ private function isIDNumberForbidden($id) { // 0-99 are probably going to be used for local system accounts instead of LDAP accounts // 100-999, 60000-64999 are reserved for debian packages - return (($id <= 999) || ($id >= 60000 && $id <= 64999)); + return $id <= 999 || ($id >= 60000 && $id <= 64999); } private function getNextIDNumber($start, $IDsToSkip) { $new_id = $start; - while ($this->isIDNumberForbidden($new_id) || in_array($new_id, $IDsToSkip)) { + while ( + $this->isIDNumberForbidden($new_id) || + in_array($new_id, $IDsToSkip) + ) { $new_id++; } return $new_id; @@ -148,7 +167,7 @@ private function getCustomIDMappings() $dir = new \DirectoryIterator($this->custom_mappings_path); foreach ($dir as $fileinfo) { $filename = $fileinfo->getFilename(); - if ($fileinfo->isDot() || ($filename == "README.md")) { + if ($fileinfo->isDot() || $filename == "README.md") { continue; } if ($fileinfo->getExtension() == "csv") { @@ -175,7 +194,11 @@ private function getAllUIDNumbersInUse() // use baseOU for awareness of externally managed entries return array_map( fn($x) => $x["uidnumber"][0], - $this->baseOU->getChildrenArray(["uidNumber"], true, "(objectClass=posixAccount)"), + $this->baseOU->getChildrenArray( + ["uidNumber"], + true, + "(objectClass=posixAccount)", + ), ); } @@ -184,7 +207,11 @@ private function getAllGIDNumbersInUse() // use baseOU for awareness of externally managed entries return array_map( fn($x) => $x["gidnumber"][0], - $this->baseOU->getChildrenArray(["gidNumber"], true, "(objectClass=posixGroup)"), + $this->baseOU->getChildrenArray( + ["gidNumber"], + true, + "(objectClass=posixGroup)", + ), ); } @@ -200,9 +227,9 @@ public function getAllUsers( $UnityMailer, $UnityRedis, $UnityWebhook, - $ignorecache = false + $ignorecache = false, ) { - $out = array(); + $out = []; if (!$ignorecache) { $users = $UnityRedis->getCache("sorted_users", ""); @@ -216,8 +243,8 @@ public function getAllUsers( $UnitySQL, $UnityMailer, $UnityRedis, - $UnityWebhook - ) + $UnityWebhook, + ), ); } return $out; @@ -227,7 +254,14 @@ public function getAllUsers( $users = $this->getAllUsersUIDs(); sort($users); foreach ($users as $user) { - $params = array($user, $this, $UnitySQL, $UnityMailer, $UnityRedis, $UnityWebhook); + $params = [ + $user, + $this, + $UnitySQL, + $UnityMailer, + $UnityRedis, + $UnityWebhook, + ]; array_push($out, new UnityUser(...$params)); } return $out; @@ -239,7 +273,7 @@ public function getAllUsersAttributes($attributes) $user_attributes = $this->baseOU->getChildrenArray( $attributes, true, // recursive - "(objectClass=posixAccount)" + "(objectClass=posixAccount)", ); foreach ($user_attributes as $i => $attributes) { if (!in_array($attributes["uid"][0], $include_uids)) { @@ -254,17 +288,22 @@ public function getAllPIGroups( $UnityMailer, $UnityRedis, $UnityWebhook, - $ignorecache = false + $ignorecache = false, ) { - $out = array(); + $out = []; if (!$ignorecache) { $groups = $UnityRedis->getCache("sorted_groups", ""); if (!is_null($groups)) { foreach ($groups as $group) { - $params = array( - $group, $this, $UnitySQL, $UnityMailer, $UnityRedis, $UnityWebhook - ); + $params = [ + $group, + $this, + $UnitySQL, + $UnityMailer, + $UnityRedis, + $UnityWebhook, + ]; array_push($out, new UnityGroup(...$params)); } @@ -283,8 +322,8 @@ public function getAllPIGroups( $UnitySQL, $UnityMailer, $UnityRedis, - $UnityWebhook - ) + $UnityWebhook, + ), ); } @@ -304,7 +343,7 @@ public function getPIGroupGIDsWithMemberUID($uid) ["cn"], false, "(memberuid=" . ldap_escape($uid, LDAP_ESCAPE_FILTER) . ")", - ) + ), ); } @@ -327,12 +366,14 @@ public function getAllPIGroupOwnerAttributes($attributes) $owner_attributes = array_values($owner_attributes); // reindex $owners_not_found = array_diff( $owner_uids, - array_map(fn($x) => $x["uid"][0], $owner_attributes) + array_map(fn($x) => $x["uid"][0], $owner_attributes), ); if (count($owners_not_found) > 0) { UnityHTTPD::errorLog( "warning", - "PI group owners not found: " . \jsonEncode($owners_not_found) . "\n" + "PI group owners not found: " . + \jsonEncode($owners_not_found) . + "\n", ); } return $owner_attributes; @@ -347,7 +388,10 @@ public function getAllUID2PIGIDs() $uids = $this->getAllUsersUIDs(); $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 ($this->getAllPIGroupsAttributes(["cn", "memberuid"]) as $array) { + foreach ( + $this->getAllPIGroupsAttributes(["cn", "memberuid"]) + as $array + ) { $gid = $array["cn"][0]; foreach ($array["memberuid"] as $uid) { if (array_key_exists($uid, $uid2pigids)) { @@ -355,7 +399,7 @@ public function getAllUID2PIGIDs() } else { UnityHTTPD::errorLog( "warning", - "user '$uid' is a member of a PI group but is not a Unity user!" + "user '$uid' is a member of a PI group but is not a Unity user!", ); } } @@ -368,9 +412,9 @@ public function getAllOrgGroups( $UnityMailer, $UnityRedis, $UnityWebhook, - $ignorecache = false + $ignorecache = false, ) { - $out = array(); + $out = []; if (!$ignorecache) { $orgs = $UnityRedis->getCache("sorted_orgs", ""); @@ -384,8 +428,8 @@ public function getAllOrgGroups( $UnitySQL, $UnityMailer, $UnityRedis, - $UnityWebhook - ) + $UnityWebhook, + ), ); } return $out; @@ -403,8 +447,8 @@ public function getAllOrgGroups( $UnitySQL, $UnityMailer, $UnityRedis, - $UnityWebhook - ) + $UnityWebhook, + ), ); } @@ -419,24 +463,32 @@ public function getAllOrgGroupsAttributes($attributes) public function getUserEntry($uid) { $uid = ldap_escape($uid, "", LDAP_ESCAPE_DN); - return $this->getEntry(unityLDAP::RDN . "=$uid," . CONFIG["ldap"]["user_ou"]); + return $this->getEntry( + unityLDAP::RDN . "=$uid," . CONFIG["ldap"]["user_ou"], + ); } public function getGroupEntry($gid) { $gid = ldap_escape($gid, "", LDAP_ESCAPE_DN); - return $this->getEntry(unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["group_ou"]); + return $this->getEntry( + unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["group_ou"], + ); } public function getPIGroupEntry($gid) { $gid = ldap_escape($gid, "", LDAP_ESCAPE_DN); - return $this->getEntry(unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["pigroup_ou"]); + return $this->getEntry( + unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["pigroup_ou"], + ); } public function getOrgGroupEntry($gid) { $gid = ldap_escape($gid, "", LDAP_ESCAPE_DN); - return $this->getEntry(unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["orggroup_ou"]); + return $this->getEntry( + unityLDAP::RDN . "=$gid," . CONFIG["ldap"]["orggroup_ou"], + ); } } diff --git a/resources/lib/UnityMailer.php b/resources/lib/UnityMailer.php index 9a8d4756..910293fd 100644 --- a/resources/lib/UnityMailer.php +++ b/resources/lib/UnityMailer.php @@ -11,7 +11,8 @@ class UnityMailer extends PHPMailer { private $template_dir = __DIR__ . "/../mail"; // location of all email templates - private $override_template_dir = __DIR__ . "/../../deployment/mail_overrides"; + private $override_template_dir = + __DIR__ . "/../../deployment/mail_overrides"; private $MSG_LINKREF; private $MSG_SENDER_EMAIL; @@ -48,10 +49,11 @@ public function __construct() $this->Port = CONFIG["smtp"]["port"]; $security = CONFIG["smtp"]["security"]; - $security_conf_valid = empty($security) || $security == "tls" || $security == "ssl"; + $security_conf_valid = + empty($security) || $security == "tls" || $security == "ssl"; if (!$security_conf_valid) { throw new Exception( - "SMTP security is not set correctly, leave empty, use 'tls', or 'ssl'" + "SMTP security is not set correctly, leave empty, use 'tls', or 'ssl'", ); } $this->SMTPSecure = $security; @@ -68,13 +70,13 @@ public function __construct() } if (CONFIG["smtp"]["ssl_verify"] == "false") { - $this->SMTPOptions = array( - 'ssl' => array( - 'verify_peer' => false, - 'verify_peer_name' => false, - 'allow_self_signed' => true - ) - ); + $this->SMTPOptions = [ + "ssl" => [ + "verify_peer" => false, + "verify_peer_name" => false, + "allow_self_signed" => true, + ], + ]; } } @@ -82,17 +84,26 @@ public function sendMail($recipients, $template = null, $data = null) { if (isset($template)) { $this->setFrom($this->MSG_SENDER_EMAIL, $this->MSG_SENDER_NAME); - $this->addReplyTo($this->MSG_SUPPORT_EMAIL, $this->MSG_SUPPORT_NAME); + $this->addReplyTo( + $this->MSG_SUPPORT_EMAIL, + $this->MSG_SUPPORT_NAME, + ); $template_filename = $template . ".php"; - if (file_exists($this->override_template_dir . "/" . $template_filename)) { - $template_path = $this->override_template_dir . "/" . $template_filename; + if ( + file_exists( + $this->override_template_dir . "/" . $template_filename, + ) + ) { + $template_path = + $this->override_template_dir . "/" . $template_filename; } else { $template_path = $this->template_dir . "/" . $template_filename; } if (file_exists($this->override_template_dir . "/footer.php")) { - $footer_template_path = $this->override_template_dir . "/footer.php"; + $footer_template_path = + $this->override_template_dir . "/footer.php"; } else { $footer_template_path = $this->template_dir . "/footer.php"; } @@ -106,7 +117,10 @@ public function sendMail($recipients, $template = null, $data = null) if ($recipients == "admin") { $this->addBCC($this->MSG_ADMIN_EMAIL, $this->MSG_ADMIN_NAME); } elseif ($recipients == "pi_approve") { - $this->addBCC($this->MSG_PI_APPROVAL_EMAIL, $this->MSG_PI_APPROVAL_NAME); + $this->addBCC( + $this->MSG_PI_APPROVAL_EMAIL, + $this->MSG_PI_APPROVAL_NAME, + ); } else { if (is_array($recipients)) { foreach ($recipients as $addr) { diff --git a/resources/lib/UnityOrg.php b/resources/lib/UnityOrg.php index eef5f76c..5753e73b 100644 --- a/resources/lib/UnityOrg.php +++ b/resources/lib/UnityOrg.php @@ -51,7 +51,7 @@ public function inOrg($user, $ignorecache = false) public function getOrgMembers($ignorecache = false) { $members = $this->getGroupMemberUIDs($ignorecache); - $out = array(); + $out = []; foreach ($members as $member) { $user_obj = new UnityUser( $member, @@ -59,7 +59,7 @@ public function getOrgMembers($ignorecache = false) $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); array_push($out, $user_obj); } diff --git a/resources/lib/UnityPerms.php b/resources/lib/UnityPerms.php index fe5d4981..0c624408 100644 --- a/resources/lib/UnityPerms.php +++ b/resources/lib/UnityPerms.php @@ -22,19 +22,22 @@ public function checkApproveUser($uid, $operated_on, $group) $role = $this->SQL->getRole($uid, $group); if ( - $this->SQL->hasPerm($role, 'unity.admin') - || $this->SQL->hasPerm($role, 'unity.admin_no_grant') + $this->SQL->hasPerm($role, "unity.admin") || + $this->SQL->hasPerm($role, "unity.admin_no_grant") ) { return true; } - if (!$this->SQL->hasPerm($role, 'unity.approve_user')) { + if (!$this->SQL->hasPerm($role, "unity.approve_user")) { return false; } $operated_on_role = $this->SQL->getRole($operated_on, $group); - if ($this->SQL->getPriority($operated_on_role) >= $this->SQL->getPriority($role)) { + if ( + $this->SQL->getPriority($operated_on_role) >= + $this->SQL->getPriority($role) + ) { return false; } @@ -50,19 +53,22 @@ public function checkDenyUser($uid, $operated_on, $group) $role = $this->SQL->getRole($uid, $group); if ( - $this->SQL->hasPerm($role, 'unity.admin') - || $this->SQL->hasPerm($role, 'unity.admin_no_grant') + $this->SQL->hasPerm($role, "unity.admin") || + $this->SQL->hasPerm($role, "unity.admin_no_grant") ) { return true; } - if (!$this->SQL->hasPerm($role, 'unity.deny_user')) { + if (!$this->SQL->hasPerm($role, "unity.deny_user")) { return false; } $operated_on_role = $this->SQL->getRole($operated_on, $group); - if ($this->SQL->getPriority($operated_on_role) >= $this->SQL->getPriority($role)) { + if ( + $this->SQL->getPriority($operated_on_role) >= + $this->SQL->getPriority($role) + ) { return false; } @@ -81,24 +87,30 @@ public function checkGrantRole($uid, $group, $role) $user_role = $this->SQL->getRole($uid, $group); - if ($this->SQL->hasPerm($user_role, 'unity.admin_no_grant') && $role == 'unity.admin') { + if ( + $this->SQL->hasPerm($user_role, "unity.admin_no_grant") && + $role == "unity.admin" + ) { return false; } if ( - $this->SQL->hasPerm($user_role, 'unity.admin') - || $this->SQL->hasPerm($user_role, 'unity.admin_no_grant') + $this->SQL->hasPerm($user_role, "unity.admin") || + $this->SQL->hasPerm($user_role, "unity.admin_no_grant") ) { return true; } - if (!$this->SQL->hasPerm($user_role, 'unity.grant_role')) { + if (!$this->SQL->hasPerm($user_role, "unity.grant_role")) { return false; } $role_to_grant = $this->SQL->getRole($role, $group); - if ($this->SQL->getPriority($role_to_grant) >= $this->SQL->getPriority($user_role)) { + if ( + $this->SQL->getPriority($role_to_grant) >= + $this->SQL->getPriority($user_role) + ) { return false; } @@ -117,24 +129,30 @@ public function checkRevokeRole($uid, $group, $role) $user_role = $this->SQL->getRole($uid, $group); - if ($this->SQL->hasPerm($user_role, 'unity.admin_no_grant') && $role == 'unity.admin') { + if ( + $this->SQL->hasPerm($user_role, "unity.admin_no_grant") && + $role == "unity.admin" + ) { return false; } if ( - $this->SQL->hasPerm($user_role, 'unity.admin') - || $this->SQL->hasPerm($user_role, 'unity.admin_no_grant') + $this->SQL->hasPerm($user_role, "unity.admin") || + $this->SQL->hasPerm($user_role, "unity.admin_no_grant") ) { return true; } - if (!$this->SQL->hasPerm($user_role, 'unity.revoke_role')) { + if (!$this->SQL->hasPerm($user_role, "unity.revoke_role")) { return false; } $role_to_revoke = $this->SQL->getRole($role, $group); - if ($this->SQL->getPriority($role_to_revoke) >= $this->SQL->getPriority($user_role)) { + if ( + $this->SQL->getPriority($role_to_revoke) >= + $this->SQL->getPriority($user_role) + ) { return false; } diff --git a/resources/lib/UnityRedis.php b/resources/lib/UnityRedis.php index e17e624d..4397d566 100644 --- a/resources/lib/UnityRedis.php +++ b/resources/lib/UnityRedis.php @@ -69,7 +69,7 @@ public function appendCacheArray($object, $key, $value) $cached_val = $this->getCache($object, $key); if (is_null($cached_val)) { - $this->setCache($object, $key, array($value)); + $this->setCache($object, $key, [$value]); } else { if (!is_array($cached_val)) { throw new Exception("This cache value is not an array"); @@ -89,13 +89,13 @@ public function removeCacheArray($object, $key, $value) $cached_val = $this->getCache($object, $key); if (is_null($cached_val)) { - $this->setCache($object, $key, array()); + $this->setCache($object, $key, []); } else { if (!is_array($cached_val)) { throw new Exception("This cache value is not an array"); } - $cached_val = array_diff($cached_val, array($value)); + $cached_val = array_diff($cached_val, [$value]); $this->setCache($object, $key, $cached_val); } } diff --git a/resources/lib/UnitySQL.php b/resources/lib/UnitySQL.php index e7291dab..b5a69106 100644 --- a/resources/lib/UnitySQL.php +++ b/resources/lib/UnitySQL.php @@ -17,7 +17,6 @@ class UnitySQL private const TABLE_GROUP_REQUESTS = "groupRequests"; private const TABLE_GROUP_JOIN_REQUESTS = "groupJoinRequests"; - // FIXME this string should be changed to something more intuitive, requires production change public const REQUEST_BECOME_PI = "admin"; @@ -26,9 +25,12 @@ class UnitySQL public function __construct() { $this->conn = new PDO( - "mysql:host=" . CONFIG["sql"]["host"] . ";dbname=" . CONFIG["sql"]["dbname"], + "mysql:host=" . + CONFIG["sql"]["host"] . + ";dbname=" . + CONFIG["sql"]["dbname"], CONFIG["sql"]["user"], - CONFIG["sql"]["pass"] + CONFIG["sql"]["pass"], ); $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } @@ -47,16 +49,18 @@ public function addRequest( $lastname, $email, $org, - $dest = self::REQUEST_BECOME_PI + $dest = self::REQUEST_BECOME_PI, ) { if ($this->requestExists($requestor, $dest)) { return; } $stmt = $this->conn->prepare( - "INSERT INTO " . self::TABLE_REQS . " " . - "(uid, firstname, lastname, email, org, request_for) VALUES " . - "(:uid, :firstname, :lastname, :email, :org, :request_for)" + "INSERT INTO " . + self::TABLE_REQS . + " " . + "(uid, firstname, lastname, email, org, request_for) VALUES " . + "(:uid, :firstname, :lastname, :email, :org, :request_for)", ); $stmt->bindParam(":uid", $requestor); $stmt->bindParam(":request_for", $dest); @@ -75,7 +79,9 @@ public function removeRequest($requestor, $dest = self::REQUEST_BECOME_PI) } $stmt = $this->conn->prepare( - "DELETE FROM " . self::TABLE_REQS . " WHERE uid=:uid and request_for=:request_for" + "DELETE FROM " . + self::TABLE_REQS . + " WHERE uid=:uid and request_for=:request_for", ); $stmt->bindParam(":uid", $requestor); $stmt->bindParam(":request_for", $dest); @@ -86,7 +92,9 @@ public function removeRequest($requestor, $dest = self::REQUEST_BECOME_PI) public function removeRequests($dest = self::REQUEST_BECOME_PI) { $stmt = $this->conn->prepare( - "DELETE FROM " . self::TABLE_REQS . " WHERE request_for=:request_for" + "DELETE FROM " . + self::TABLE_REQS . + " WHERE request_for=:request_for", ); $stmt->bindParam(":request_for", $dest); @@ -96,17 +104,23 @@ public function removeRequests($dest = self::REQUEST_BECOME_PI) public function getRequest($user, $dest) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_REQS . " WHERE uid=:uid and request_for=:request_for" + "SELECT * FROM " . + self::TABLE_REQS . + " WHERE uid=:uid and request_for=:request_for", ); $stmt->bindParam(":uid", $user); $stmt->bindParam(":request_for", $dest); $stmt->execute(); $result = $stmt->fetchAll(); if (count($result) == 0) { - throw new \Exception("no such request: uid='$user' request_for='$dest'"); + throw new \Exception( + "no such request: uid='$user' request_for='$dest'", + ); } if (count($result) > 1) { - throw new \Exception("multiple requests for uid='$user' request_for='$dest'"); + throw new \Exception( + "multiple requests for uid='$user' request_for='$dest'", + ); } return $result[0]; } @@ -132,7 +146,9 @@ public function getAllRequests() public function getRequests($dest = self::REQUEST_BECOME_PI) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_REQS . " WHERE request_for=:request_for" + "SELECT * FROM " . + self::TABLE_REQS . + " WHERE request_for=:request_for", ); $stmt->bindParam(":request_for", $dest); @@ -144,7 +160,7 @@ public function getRequests($dest = self::REQUEST_BECOME_PI) public function getRequestsByUser($user) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_REQS . " WHERE uid=:uid" + "SELECT * FROM " . self::TABLE_REQS . " WHERE uid=:uid", ); $stmt->bindParam(":uid", $user); @@ -156,7 +172,7 @@ public function getRequestsByUser($user) public function deleteRequestsByUser($user) { $stmt = $this->conn->prepare( - "DELETE FROM " . self::TABLE_REQS . " WHERE uid=:uid" + "DELETE FROM " . self::TABLE_REQS . " WHERE uid=:uid", ); $stmt->bindParam(":uid", $user); @@ -167,7 +183,7 @@ public function addNotice($title, $date, $content, $operator) { $table = self::TABLE_NOTICES; $stmt = $this->conn->prepare( - "INSERT INTO $table (date, title, message) VALUES (:date, :title, :message)" + "INSERT INTO $table (date, title, message) VALUES (:date, :title, :message)", ); $stmt->bindParam(":date", $date); $stmt->bindParam(":title", $title); @@ -177,9 +193,9 @@ public function addNotice($title, $date, $content, $operator) $this->addLog( $operator->uid, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "added_cluster_notice", - $operator + $operator, ); } @@ -187,7 +203,7 @@ public function editNotice($id, $title, $date, $content) { $table = self::TABLE_NOTICES; $stmt = $this->conn->prepare( - "UPDATE $table SET date=:date, title=:title, message=:message WHERE id=:id" + "UPDATE $table SET date=:date, title=:title, message=:message WHERE id=:id", ); $stmt->bindParam(":date", $date); $stmt->bindParam(":title", $title); @@ -200,7 +216,7 @@ public function editNotice($id, $title, $date, $content) public function deleteNotice($id) { $stmt = $this->conn->prepare( - "DELETE FROM " . self::TABLE_NOTICES . " WHERE id=:id" + "DELETE FROM " . self::TABLE_NOTICES . " WHERE id=:id", ); $stmt->bindParam(":id", $id); @@ -210,7 +226,7 @@ public function deleteNotice($id) public function getNotice($id) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_NOTICES . " WHERE id=:id" + "SELECT * FROM " . self::TABLE_NOTICES . " WHERE id=:id", ); $stmt->bindParam(":id", $id); @@ -222,7 +238,7 @@ public function getNotice($id) public function getNotices() { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_NOTICES . " ORDER BY date DESC" + "SELECT * FROM " . self::TABLE_NOTICES . " ORDER BY date DESC", ); $stmt->execute(); @@ -231,9 +247,7 @@ public function getNotices() public function getPages() { - $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_PAGES - ); + $stmt = $this->conn->prepare("SELECT * FROM " . self::TABLE_PAGES); $stmt->execute(); return $stmt->fetchAll(); @@ -242,7 +256,7 @@ public function getPages() public function getPage($id) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_PAGES . " WHERE page=:id" + "SELECT * FROM " . self::TABLE_PAGES . " WHERE page=:id", ); $stmt->bindParam(":id", $id); @@ -254,7 +268,9 @@ public function getPage($id) public function editPage($id, $content, $operator) { $stmt = $this->conn->prepare( - "UPDATE " . self::TABLE_PAGES . " SET content=:content WHERE page=:id" + "UPDATE " . + self::TABLE_PAGES . + " SET content=:content WHERE page=:id", ); $stmt->bindParam(":id", $id); $stmt->bindParam(":content", $content); @@ -263,9 +279,9 @@ public function editPage($id, $content, $operator) $this->addLog( $operator->uid, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "edited_page", - $operator + $operator, ); } @@ -274,7 +290,7 @@ public function addLog($operator, $operator_ip, $action_type, $recipient) $table = self::TABLE_AUDIT_LOG; $stmt = $this->conn->prepare( "INSERT INTO $table (operator, operator_ip, action_type, recipient) - VALUE (:operator, :operator_ip, :action_type, :recipient)" + VALUE (:operator, :operator_ip, :action_type, :recipient)", ); $stmt->bindParam(":operator", $operator); $stmt->bindParam(":operator_ip", $operator_ip); @@ -287,7 +303,9 @@ public function addLog($operator, $operator_ip, $action_type, $recipient) public function addAccountDeletionRequest($uid) { $stmt = $this->conn->prepare( - "INSERT INTO " . self::TABLE_ACCOUNT_DELETION_REQUESTS . " (uid) VALUE (:uid)" + "INSERT INTO " . + self::TABLE_ACCOUNT_DELETION_REQUESTS . + " (uid) VALUE (:uid)", ); $stmt->bindParam(":uid", $uid); @@ -297,7 +315,9 @@ public function addAccountDeletionRequest($uid) public function accDeletionRequestExists($uid) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_ACCOUNT_DELETION_REQUESTS . " WHERE uid=:uid" + "SELECT * FROM " . + self::TABLE_ACCOUNT_DELETION_REQUESTS . + " WHERE uid=:uid", ); $stmt->bindParam(":uid", $uid); @@ -312,7 +332,9 @@ public function deleteAccountDeletionRequest($uid) return; } $stmt = $this->conn->prepare( - "DELETE FROM " . self::TABLE_ACCOUNT_DELETION_REQUESTS . " WHERE uid=:uid" + "DELETE FROM " . + self::TABLE_ACCOUNT_DELETION_REQUESTS . + " WHERE uid=:uid", ); $stmt->bindParam(":uid", $uid); $stmt->execute(); @@ -321,63 +343,67 @@ public function deleteAccountDeletionRequest($uid) public function getRole($uid, $group) { $table = self::TABLE_GROUP_ROLE_ASSIGNMENTS; - $stmt = $this->conn->prepare("SELECT * FROM $table WHERE user=:uid AND `group`=:group"); + $stmt = $this->conn->prepare( + "SELECT * FROM $table WHERE user=:uid AND `group`=:group", + ); $stmt->bindParam(":uid", $uid); $stmt->bindParam(":group", $group); $stmt->execute(); - return $stmt->fetchAll()[0]['role']; + return $stmt->fetchAll()[0]["role"]; } public function hasPerm($role, $perm) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_GROUP_ROLES . " WHERE slug=:role" + "SELECT * FROM " . self::TABLE_GROUP_ROLES . " WHERE slug=:role", ); $stmt->bindParam(":role", $role); $stmt->execute(); $row = $stmt->fetchAll()[0]; - $perms = explode(",", $row['perms']); + $perms = explode(",", $row["perms"]); return in_array($perm, $perms); } public function getPriority($role) { $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_GROUP_ROLES . " WHERE slug=:role" + "SELECT * FROM " . self::TABLE_GROUP_ROLES . " WHERE slug=:role", ); $stmt->bindParam(":role", $role); $stmt->execute(); $row = $stmt->fetchAll()[0]; - return $row['priority']; + return $row["priority"]; } public function roleAvailableInGroup($uid, $group, $role) { $table = self::TABLE_GROUP_ROLE_ASSIGNMENTS; - $stmt = $this->conn->prepare("SELECT * FROM $table WHERE user=:uid AND `group`=:group"); + $stmt = $this->conn->prepare( + "SELECT * FROM $table WHERE user=:uid AND `group`=:group", + ); $stmt->bindParam(":uid", $uid); $stmt->bindParam(":group", $group); $stmt->execute(); $row = $stmt->fetchAll()[0]; - $group_slug = $row['group']; + $group_slug = $row["group"]; $stmt = $this->conn->prepare( - "SELECT * FROM " . self::TABLE_GROUP_TYPES . " WHERE slug=:slug" + "SELECT * FROM " . self::TABLE_GROUP_TYPES . " WHERE slug=:slug", ); $stmt->bindParam(":slug", $group_slug); $stmt->execute(); $row = $stmt->fetchAll()[0]; - $roles = explode(",", $row['roles']); + $roles = explode(",", $row["roles"]); return in_array($role, $roles); } diff --git a/resources/lib/UnitySSO.php b/resources/lib/UnitySSO.php index 0495a164..6f98881f 100644 --- a/resources/lib/UnitySSO.php +++ b/resources/lib/UnitySSO.php @@ -30,26 +30,38 @@ private static function eppnToOrg($eppn) // shibboleth service provider does not garuntee attributes are set, even REMOTE_USER // https://shibboleth.atlassian.net/wiki/spaces/SP3/pages/2065335257/AttributeAccess // I have observed attributes to be set to empty strings while shibd complains of bad config - private static function getAttributeRaw($attributeName, $fallbackAttributeName = null) - { + private static function getAttributeRaw( + $attributeName, + $fallbackAttributeName = null, + ) { if (isset($_SERVER[$attributeName]) && $_SERVER[$attributeName] != "") { return $_SERVER[$attributeName]; } if (is_null($fallbackAttributeName)) { - throw new SSOException("\$_SERVER[\"$attributeName\"] is unset or empty!"); + throw new SSOException( + "\$_SERVER[\"$attributeName\"] is unset or empty!", + ); } - if (isset($_SERVER[$fallbackAttributeName]) && $_SERVER[$fallbackAttributeName] != "") { + if ( + isset($_SERVER[$fallbackAttributeName]) && + $_SERVER[$fallbackAttributeName] != "" + ) { return $_SERVER[$fallbackAttributeName]; } throw new SSOException( - "\$_SERVER[\"$attributeName\"] and \$_SERVER[\"$fallbackAttributeName\"]" - . " are both unset or empty!" + "\$_SERVER[\"$attributeName\"] and \$_SERVER[\"$fallbackAttributeName\"]" . + " are both unset or empty!", ); } - private static function getAttribute($attributeName, $fallbackAttributeName = null) - { - $attribute_raw = self::getAttributeRaw($attributeName, $fallbackAttributeName); + private static function getAttribute( + $attributeName, + $fallbackAttributeName = null, + ) { + $attribute_raw = self::getAttributeRaw( + $attributeName, + $fallbackAttributeName, + ); // attributes may have multiple values, by default they are split by ';' // see SPConfig setting attributeValueDelimiter return explode(";", $attribute_raw)[0]; @@ -57,13 +69,16 @@ private static function getAttribute($attributeName, $fallbackAttributeName = nu public static function getSSO() { - return array( + return [ "user" => self::eppnToUID(self::getAttribute("REMOTE_USER")), "org" => self::eppnToOrg(self::getAttribute("REMOTE_USER")), "firstname" => self::getAttribute("givenName"), "lastname" => self::getAttribute("sn"), - "name" => self::getAttribute("givenName") . " " . self::getAttribute("sn"), - "mail" => self::getAttribute("mail", "eppn") - ); + "name" => + self::getAttribute("givenName") . + " " . + self::getAttribute("sn"), + "mail" => self::getAttribute("mail", "eppn"), + ]; } } diff --git a/resources/lib/UnityUser.php b/resources/lib/UnityUser.php index f13352c6..a9e0693b 100644 --- a/resources/lib/UnityUser.php +++ b/resources/lib/UnityUser.php @@ -35,7 +35,9 @@ public function equals($other_user) { if (!is_a($other_user, self::class)) { throw new Exception( - "Unable to check equality because the parameter is not a " . self::class . " object" + "Unable to check equality because the parameter is not a " . + self::class . + " object", ); } @@ -62,23 +64,38 @@ public function init($firstname, $lastname, $email, $org, $send_mail = true) $ldapGroupEntry = $this->getGroupEntry(); $id = $this->LDAP->getNextUIDGIDNumber($this->uid); \ensure(!$ldapGroupEntry->exists()); - $ldapGroupEntry->setAttribute("objectclass", UnityLDAP::POSIX_GROUP_CLASS); + $ldapGroupEntry->setAttribute( + "objectclass", + UnityLDAP::POSIX_GROUP_CLASS, + ); $ldapGroupEntry->setAttribute("gidnumber", strval($id)); $ldapGroupEntry->write(); \ensure(!$this->entry->exists()); - $this->entry->setAttribute("objectclass", UnityLDAP::POSIX_ACCOUNT_CLASS); + $this->entry->setAttribute( + "objectclass", + UnityLDAP::POSIX_ACCOUNT_CLASS, + ); $this->entry->setAttribute("uid", $this->uid); $this->entry->setAttribute("givenname", $firstname); $this->entry->setAttribute("sn", $lastname); $this->entry->setAttribute( "gecos", - \transliterator_transliterate("Latin-ASCII", "$firstname $lastname") + \transliterator_transliterate( + "Latin-ASCII", + "$firstname $lastname", + ), ); $this->entry->setAttribute("mail", $email); $this->entry->setAttribute("o", $org); - $this->entry->setAttribute("homedirectory", self::HOME_DIR . $this->uid); - $this->entry->setAttribute("loginshell", $this->LDAP->getDefUserShell()); + $this->entry->setAttribute( + "homedirectory", + self::HOME_DIR . $this->uid, + ); + $this->entry->setAttribute( + "loginshell", + $this->LDAP->getDefUserShell(), + ); $this->entry->setAttribute("uidnumber", strval($id)); $this->entry->setAttribute("gidnumber", strval($id)); $this->entry->write(); @@ -87,9 +104,17 @@ public function init($firstname, $lastname, $email, $org, $send_mail = true) $this->REDIS->setCache($this->uid, "lastname", $lastname); $this->REDIS->setCache($this->uid, "mail", $email); $this->REDIS->setCache($this->uid, "org", $org); - $this->REDIS->setCache($this->uid, "homedir", self::HOME_DIR . $this->uid); - $this->REDIS->setCache($this->uid, "loginshell", $this->LDAP->getDefUserShell()); - $this->REDIS->setCache($this->uid, "sshkeys", array()); + $this->REDIS->setCache( + $this->uid, + "homedir", + self::HOME_DIR . $this->uid, + ); + $this->REDIS->setCache( + $this->uid, + "loginshell", + $this->LDAP->getDefUserShell(), + ); + $this->REDIS->setCache($this->uid, "sshkeys", []); $org = $this->getOrgGroup(); if (!$org->exists()) { @@ -107,17 +132,16 @@ public function init($firstname, $lastname, $email, $org, $send_mail = true) $this->SQL->addLog( $this->uid, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "user_added", - $this->uid + $this->uid, ); if ($send_mail) { - $this->MAILER->sendMail( - $this->getMail(), - "user_created", - array("user" => $this->uid, "org" => $this->getOrg()) - ); + $this->MAILER->sendMail($this->getMail(), "user_created", [ + "user" => $this->uid, + "org" => $this->getOrg(), + ]); } } @@ -178,9 +202,9 @@ public function setFirstname($firstname, $operator = null) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "firstname_changed", - $this->uid + $this->uid, ); $this->entry->write(); @@ -227,9 +251,9 @@ public function setLastname($lastname, $operator = null) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "lastname_changed", - $this->uid + $this->uid, ); $this->entry->write(); @@ -282,9 +306,9 @@ public function setMail($email, $operator = null) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "email_changed", - $this->uid + $this->uid, ); $this->entry->write(); @@ -336,17 +360,15 @@ public function setSSHKeys($keys, $operator = null, $send_mail = true) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "sshkey_modify", - $this->uid + $this->uid, ); if ($send_mail) { - $this->MAILER->sendMail( - $this->getMail(), - "user_sshkey", - array("keys" => $this->getSSHKeys()) - ); + $this->MAILER->sendMail($this->getMail(), "user_sshkey", [ + "keys" => $this->getSSHKeys(), + ]); } } @@ -368,7 +390,7 @@ public function getSSHKeys($ignorecache = false) if ($this->exists()) { $result = $this->entry->getAttribute("sshpublickey"); if (is_null($result)) { - $keys = array(); + $keys = []; } else { $keys = $result; } @@ -391,11 +413,15 @@ public function getSSHKeys($ignorecache = false) public function setLoginShell($shell, $operator = null, $send_mail = true) { // ldap schema syntax is "IA5 String (1.3.6.1.4.1.1466.115.121.1.26)" - if (!mb_check_encoding($shell, 'ASCII')) { - throw new Exception("non ascii characters are not allowed in a login shell!"); + if (!mb_check_encoding($shell, "ASCII")) { + throw new Exception( + "non ascii characters are not allowed in a login shell!", + ); } if ($shell != trim($shell)) { - throw new Exception("leading/trailing whitespace is not allowed in a login shell!"); + throw new Exception( + "leading/trailing whitespace is not allowed in a login shell!", + ); } if (empty($shell)) { throw new Exception("login shell must not be empty!"); @@ -408,19 +434,17 @@ public function setLoginShell($shell, $operator = null, $send_mail = true) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "loginshell_changed", - $this->uid + $this->uid, ); $this->REDIS->setCache($this->uid, "loginshell", $shell); if ($send_mail) { - $this->MAILER->sendMail( - $this->getMail(), - "user_loginshell", - array("new_shell" => $this->getLoginShell()) - ); + $this->MAILER->sendMail($this->getMail(), "user_loginshell", [ + "new_shell" => $this->getLoginShell(), + ]); } } @@ -461,9 +485,9 @@ public function setHomeDir($home, $operator = null) $this->SQL->addLog( $operator, - $_SERVER['REMOTE_ADDR'], + $_SERVER["REMOTE_ADDR"], "homedir_changed", - $this->uid + $this->uid, ); $this->REDIS->setCache($this->uid, "homedir", $home); @@ -526,7 +550,7 @@ public function getPIGroup() $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); } @@ -538,7 +562,7 @@ public function getOrgGroup() $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); } @@ -569,15 +593,11 @@ public function getPIGroupGIDs($ignorecache = false) public function requestAccountDeletion() { $this->SQL->addAccountDeletionRequest($this->uid); - $this->MAILER->sendMail( - "admin", - "account_deletion_request_admin", - array( - "user" => $this->uid, - "name" => $this->getFullname(), - "email" => $this->getMail() - ) - ); + $this->MAILER->sendMail("admin", "account_deletion_request_admin", [ + "user" => $this->uid, + "name" => $this->getFullname(), + "email" => $this->getMail(), + ]); } /** @@ -606,7 +626,7 @@ public function isInGroup($uid, $group) $this->SQL, $this->MAILER, $this->REDIS, - $this->WEBHOOK + $this->WEBHOOK, ); } else { $group_checked = $group; diff --git a/resources/lib/UnityWebhook.php b/resources/lib/UnityWebhook.php index 61c9dd1d..5a49c319 100644 --- a/resources/lib/UnityWebhook.php +++ b/resources/lib/UnityWebhook.php @@ -5,7 +5,8 @@ class UnityWebhook { private $template_dir = __DIR__ . "/../mail"; - private $override_template_dir = __DIR__ . "/../../deployment/mail_overrides"; + private $override_template_dir = + __DIR__ . "/../../deployment/mail_overrides"; private $url = CONFIG["webhook"]["url"]; private $MSG_LINKREF; private $Subject; // set by template @@ -17,9 +18,9 @@ public function __construct() public function htmlToMarkdown($html) { // Define regex patterns for each markdown format - $bold = '/<(b|strong)\b[^>]*>(.*?)<\/(b|strong)>/s'; - $italic = '/]*>(.*?)<\/i>/s'; - $strikethrough = '/]*>(.*?)<\/del>/s'; + $bold = "/<(b|strong)\b[^>]*>(.*?)<\/(b|strong)>/s"; + $italic = "/]*>(.*?)<\/i>/s"; + $strikethrough = "/]*>(.*?)<\/del>/s"; $link = '/]*href=["\']?([^"\'\s]*)[^>]*>(.*?)<\/a>/s'; // Replace each HTML tag with its corresponding markdown format @@ -37,8 +38,11 @@ public function htmlToMarkdown($html) public function sendWebhook($template = null, $data = null) { $template_filename = $template . ".php"; - if (file_exists($this->override_template_dir . "/" . $template_filename)) { - $template_path = $this->override_template_dir . "/" . $template_filename; + if ( + file_exists($this->override_template_dir . "/" . $template_filename) + ) { + $template_path = + $this->override_template_dir . "/" . $template_filename; } else { $template_path = $this->template_dir . "/" . $template_filename; } @@ -52,9 +56,11 @@ public function sendWebhook($template = null, $data = null) $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Content-Type: application/json", + ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, \jsonEncode(array('text' => $message))); + curl_setopt($ch, CURLOPT_POSTFIELDS, \jsonEncode(["text" => $message])); $result = curl_exec($ch); curl_close($ch); return $result; diff --git a/resources/lib/exceptions/ArrayKeyException.php b/resources/lib/exceptions/ArrayKeyException.php index 7b591ac8..722878c2 100644 --- a/resources/lib/exceptions/ArrayKeyException.php +++ b/resources/lib/exceptions/ArrayKeyException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class ArrayKeyException extends \Exception -{ -} +class ArrayKeyException extends \Exception {} diff --git a/resources/lib/exceptions/EncodingConversionException.php b/resources/lib/exceptions/EncodingConversionException.php index ac316d89..aeab6ef5 100644 --- a/resources/lib/exceptions/EncodingConversionException.php +++ b/resources/lib/exceptions/EncodingConversionException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class EncodingConversionException extends \Exception -{ -} +class EncodingConversionException extends \Exception {} diff --git a/resources/lib/exceptions/EncodingUnknownException.php b/resources/lib/exceptions/EncodingUnknownException.php index 63725d01..d3520183 100644 --- a/resources/lib/exceptions/EncodingUnknownException.php +++ b/resources/lib/exceptions/EncodingUnknownException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class EncodingUnknownException extends \Exception -{ -} +class EncodingUnknownException extends \Exception {} diff --git a/resources/lib/exceptions/EnsureException.php b/resources/lib/exceptions/EnsureException.php index f184f903..178142d9 100644 --- a/resources/lib/exceptions/EnsureException.php +++ b/resources/lib/exceptions/EnsureException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class EnsureException extends \Exception -{ -} +class EnsureException extends \Exception {} diff --git a/resources/lib/exceptions/NoDieException.php b/resources/lib/exceptions/NoDieException.php index 49d8b2d8..9a069dca 100644 --- a/resources/lib/exceptions/NoDieException.php +++ b/resources/lib/exceptions/NoDieException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class NoDieException extends \Exception -{ -} +class NoDieException extends \Exception {} diff --git a/resources/lib/exceptions/SSOException.php b/resources/lib/exceptions/SSOException.php index 2551180e..ce631639 100644 --- a/resources/lib/exceptions/SSOException.php +++ b/resources/lib/exceptions/SSOException.php @@ -2,6 +2,4 @@ namespace UnityWebPortal\lib\exceptions; -class SSOException extends \Exception -{ -} +class SSOException extends \Exception {} diff --git a/resources/lib/utils.php b/resources/lib/utils.php index c0ae48ab..42acfed9 100644 --- a/resources/lib/utils.php +++ b/resources/lib/utils.php @@ -15,8 +15,11 @@ function arrayGet($array, ...$keys) if (!isset($cursor[$key])) { throw new ArrayKeyException( "key not found: \$array" . - // [1, 2, "foo"] => [1][2]["foo"] - implode("", array_map(fn($x) => jsonEncode([$x]), $keysTraversed)) + // [1, 2, "foo"] => [1][2]["foo"] + implode( + "", + array_map(fn($x) => jsonEncode([$x]), $keysTraversed), + ), ); } $cursor = $cursor[$key]; @@ -67,9 +70,11 @@ function mbConvertEncoding($string, $to_encoding, $from_encoding = null) $output = mb_convert_encoding($string, $to_encoding, $from_encoding); if ($output === false) { throw new EncodingConversionException( - jsonEncode( - ["to" => $to_encoding, "from" => $from_encoding, "base64" => base64_encode($string)] - ) + jsonEncode([ + "to" => $to_encoding, + "from" => $from_encoding, + "base64" => base64_encode($string), + ]), ); } return $output; diff --git a/resources/mail/account_deletion_request_admin.php b/resources/mail/account_deletion_request_admin.php index c9170d93..6d8fde1a 100644 --- a/resources/mail/account_deletion_request_admin.php +++ b/resources/mail/account_deletion_request_admin.php @@ -1,8 +1,7 @@ Subject = "Account Deletion Request"; -?> +$this->Subject = "Account Deletion Request"; ?>

Hello,

diff --git a/resources/mail/group_created.php b/resources/mail/group_created.php index 504be1f5..618ef57f 100644 --- a/resources/mail/group_created.php +++ b/resources/mail/group_created.php @@ -1,8 +1,7 @@ Subject = "PI Account Approved"; -?> +$this->Subject = "PI Account Approved"; ?>

Hello,

diff --git a/resources/mail/group_denied.php b/resources/mail/group_denied.php index 857e58f4..7d633e7e 100644 --- a/resources/mail/group_denied.php +++ b/resources/mail/group_denied.php @@ -1,8 +1,7 @@ Subject = "PI Account Denied"; -?> +$this->Subject = "PI Account Denied"; ?>

Hello,

diff --git a/resources/mail/group_disband.php b/resources/mail/group_disband.php index defa3b93..c26b3eb1 100644 --- a/resources/mail/group_disband.php +++ b/resources/mail/group_disband.php @@ -1,12 +1,13 @@ Subject = "PI Group Disbanded"; -?> +$this->Subject = "PI Group Disbanded"; ?>

Hello,

-

Your PI group, , has been disbanded on the Unity +

Your PI group, , has been disbanded on the Unity cluster. Any jobs associated with this PI account have been killed.

If you believe this to be a mistake, please reply to this email

diff --git a/resources/mail/group_join_request_cancelled.php b/resources/mail/group_join_request_cancelled.php index 3421f198..331350ca 100644 --- a/resources/mail/group_join_request_cancelled.php +++ b/resources/mail/group_join_request_cancelled.php @@ -1,5 +1,7 @@ Subject = "Unity PI Membership Request Cancelled: '" . $data["uid"] . "'"; -?> +$this->Subject = + "Unity PI Membership Request Cancelled: '" . $data["uid"] . "'"; ?>

Hello,

-

The user '' has cancelled their request to join your PI group.

+

The user '' has cancelled their request to join your PI group.

diff --git a/resources/mail/group_request.php b/resources/mail/group_request.php index 5828bcfd..74f6895f 100644 --- a/resources/mail/group_request.php +++ b/resources/mail/group_request.php @@ -1,8 +1,7 @@ Subject = "PI Account Requested"; -?> +$this->Subject = "PI Account Requested"; ?>

Hello,

diff --git a/resources/mail/group_request_admin.php b/resources/mail/group_request_admin.php index 2a7b7e05..8fe51027 100644 --- a/resources/mail/group_request_admin.php +++ b/resources/mail/group_request_admin.php @@ -1,8 +1,7 @@ Subject = "PI Group Request"; -?> +$this->Subject = "PI Group Request"; ?>

Hello,

diff --git a/resources/mail/group_request_cancelled.php b/resources/mail/group_request_cancelled.php index 73995b56..31012c59 100644 --- a/resources/mail/group_request_cancelled.php +++ b/resources/mail/group_request_cancelled.php @@ -1,5 +1,6 @@ Subject = "PI Request Cancelled: '" . $data["uid"] . "'"; -?> +$this->Subject = "PI Request Cancelled: '" . $data["uid"] . "'"; ?>

Hello,

-

The user '' has cancelled their request to become a PI.

+

The user '' has cancelled their request to become a PI.

diff --git a/resources/mail/group_user_added.php b/resources/mail/group_user_added.php index be11dde3..9c42b8cc 100644 --- a/resources/mail/group_user_added.php +++ b/resources/mail/group_user_added.php @@ -1,8 +1,7 @@ Subject = "Group Request Approved"; -?> +$this->Subject = "Group Request Approved"; ?>

Hello,

diff --git a/resources/mail/group_user_added_owner.php b/resources/mail/group_user_added_owner.php index 33cd7e77..a082f30e 100644 --- a/resources/mail/group_user_added_owner.php +++ b/resources/mail/group_user_added_owner.php @@ -1,14 +1,13 @@ Subject = "Group Member Approved"; -?> +$this->Subject = "Group Member Approved"; ?>

Hello,

A new user has been added to your PI group, -''. +''. The details of the new user are below:

diff --git a/resources/mail/group_user_denied.php b/resources/mail/group_user_denied.php index 7c5a864e..36dfc962 100644 --- a/resources/mail/group_user_denied.php +++ b/resources/mail/group_user_denied.php @@ -1,11 +1,12 @@ Subject = "Group Request Denied"; -?> +$this->Subject = "Group Request Denied"; ?>

Hello,

-

You have been denied from joining the PI group .

+

You have been denied from joining the PI group .

If you believe this to be a mistake, please reply to this email as soon as possible.

diff --git a/resources/mail/group_user_denied_owner.php b/resources/mail/group_user_denied_owner.php index e2cffbb9..00c2560e 100644 --- a/resources/mail/group_user_denied_owner.php +++ b/resources/mail/group_user_denied_owner.php @@ -1,12 +1,13 @@ Subject = "Group Member Denied"; -?> +$this->Subject = "Group Member Denied"; ?>

Hello,

-

A user has been denied from joining your PI group, . +

A user has been denied from joining your PI group, . The details of the denied user are below:

diff --git a/resources/mail/group_user_removed.php b/resources/mail/group_user_removed.php index bfde1f18..83a1786e 100644 --- a/resources/mail/group_user_removed.php +++ b/resources/mail/group_user_removed.php @@ -1,8 +1,7 @@ Subject = "Removed from Group"; -?> +$this->Subject = "Removed from Group"; ?>

Hello,

diff --git a/resources/mail/group_user_removed_owner.php b/resources/mail/group_user_removed_owner.php index a97d38cf..2cff0e54 100644 --- a/resources/mail/group_user_removed_owner.php +++ b/resources/mail/group_user_removed_owner.php @@ -1,14 +1,13 @@ Subject = "Group Member Removed"; -?> +$this->Subject = "Group Member Removed"; ?>

Hello,

A user has been removed from your PI group, -''. +''. The details of the removed user are below:

diff --git a/resources/mail/group_user_request.php b/resources/mail/group_user_request.php index ea88e1e1..ad40d215 100644 --- a/resources/mail/group_user_request.php +++ b/resources/mail/group_user_request.php @@ -1,8 +1,7 @@ Subject = "Request Submitted"; -?> +$this->Subject = "Request Submitted"; ?>

Hello,

diff --git a/resources/mail/group_user_request_owner.php b/resources/mail/group_user_request_owner.php index 1eac99b9..9ad4db55 100644 --- a/resources/mail/group_user_request_owner.php +++ b/resources/mail/group_user_request_owner.php @@ -1,14 +1,13 @@ Subject = "Group Member Request"; -?> +$this->Subject = "Group Member Request"; ?>

Hello,

A user has requested to join your PI group, -''. +''. The details of the user are below:

diff --git a/resources/mail/user_created.php b/resources/mail/user_created.php index 172a048c..4298e777 100644 --- a/resources/mail/user_created.php +++ b/resources/mail/user_created.php @@ -1,8 +1,7 @@ Subject = "User Created"; -?> +$this->Subject = "User Created"; ?>

Hello,

diff --git a/resources/mail/user_loginshell.php b/resources/mail/user_loginshell.php index f70eba2e..f78bf54f 100644 --- a/resources/mail/user_loginshell.php +++ b/resources/mail/user_loginshell.php @@ -1,12 +1,13 @@ Subject = "Login Shell Updated"; -?> +$this->Subject = "Login Shell Updated"; ?>

Hello,

-

You have updated your login shell on the Unity cluster to . +

You have updated your login shell on the Unity cluster to . You can view the login shell settings on the account settings page

diff --git a/resources/mail/user_sshkey.php b/resources/mail/user_sshkey.php index d5ff241b..5a4a8076 100644 --- a/resources/mail/user_sshkey.php +++ b/resources/mail/user_sshkey.php @@ -1,8 +1,7 @@ Subject = "SSH Key Modified"; -?> +$this->Subject = "SSH Key Modified"; ?>

Hello,

@@ -10,11 +9,9 @@ You have modified the SSH keys on your Unity account. These public keys are currently available:

-$key"; -} -?> +} ?>

You can view the SSH public keys attached to your account on the diff --git a/test/functional/AccountDeletionRequestTest.php b/test/functional/AccountDeletionRequestTest.php index e3530642..d8c4bb04 100644 --- a/test/functional/AccountDeletionRequestTest.php +++ b/test/functional/AccountDeletionRequestTest.php @@ -22,9 +22,9 @@ private function assertNumberAccountDeletionRequests(int $x) private function getNumberAccountDeletionRequests() { global $USER, $SQL; - $stmt = $SQL->getConn()->prepare( - "SELECT * FROM account_deletion_requests WHERE uid=:uid" - ); + $stmt = $SQL + ->getConn() + ->prepare("SELECT * FROM account_deletion_requests WHERE uid=:uid"); $uid = $USER->uid; $stmt->bindParam(":uid", $uid); $stmt->execute(); @@ -38,15 +38,13 @@ public function testRequestAccountDeletionUserHasNoGroups() $this->assertEmpty($USER->getPIGroupGIDs()); $this->assertNumberAccountDeletionRequests(0); try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "account_deletion_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "account_deletion_request", + ]); $this->assertNumberAccountDeletionRequests(1); - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "account_deletion_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "account_deletion_request", + ]); $this->assertNumberAccountDeletionRequests(1); } finally { $SQL->deleteAccountDeletionRequest($USER->uid); @@ -62,10 +60,9 @@ public function testRequestAccountDeletionUserHasGroup() $this->assertNotEmpty($USER->getPIGroupGIDs()); $this->assertNumberAccountDeletionRequests(0); try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "account_deletion_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "account_deletion_request", + ]); $this->assertNumberAccountDeletionRequests(0); } finally { $SQL->deleteAccountDeletionRequest($USER->uid); diff --git a/test/functional/InvalidEPPNTest.php b/test/functional/InvalidEPPNTest.php index 239d9d91..40cc454d 100644 --- a/test/functional/InvalidEPPNTest.php +++ b/test/functional/InvalidEPPNTest.php @@ -8,12 +8,7 @@ class InvalidEPPNTest extends TestCase { public static function provider() { - return [ - ["", false], - ["a", false], - ["a@b", true], - ["a@b@c", false], - ]; + return [["", false], ["a", false], ["a@b", true], ["a@b@c", false]]; } #[DataProvider("provider")] diff --git a/test/functional/LoginShellSetTest.php b/test/functional/LoginShellSetTest.php index 90233248..a03cebfa 100644 --- a/test/functional/LoginShellSetTest.php +++ b/test/functional/LoginShellSetTest.php @@ -23,16 +23,17 @@ public function tearDown(): void public static function getShells() { global $HTTP_HEADER_TEST_INPUTS; - return [["/bin/bash"]] + array_map(function($x){return [$x];}, $HTTP_HEADER_TEST_INPUTS); + return [["/bin/bash"]] + + array_map(function ($x) { + return [$x]; + }, $HTTP_HEADER_TEST_INPUTS); } private function isShellValid(string $shell) { - return ( - (mb_check_encoding($shell, 'ASCII')) && - ($shell == trim($shell)) && - (!empty($shell)) - ); + return mb_check_encoding($shell, "ASCII") && + $shell == trim($shell) && + !empty($shell); } #[DataProvider("getShells")] @@ -42,10 +43,10 @@ public function testSetLoginShell(string $shell): void if (!$this->isShellValid($shell)) { $this->expectException("Exception"); } - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "loginshell", "shellSelect" => $shell] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "loginshell", + "shellSelect" => $shell, + ]); $this->assertEquals($shell, $USER->getLoginShell()); } } diff --git a/test/functional/NewUserTest.php b/test/functional/NewUserTest.php index e9254ec1..dc6d8f7b 100644 --- a/test/functional/NewUserTest.php +++ b/test/functional/NewUserTest.php @@ -8,7 +8,8 @@ class NewUserTest extends TestCase { - public static function provider() { + public static function provider() + { return [ getNonExistentUserAndExpectedUIDGIDNoCustomMapping(), getNonExistentUserAndExpectedUIDGIDWithCustomMapping(), @@ -20,78 +21,68 @@ private function assertRequestedPIGroup(bool $expected) global $USER, $SQL; $this->assertEquals( $expected, - $SQL->requestExists($USER->uid, UnitySQL::REQUEST_BECOME_PI) + $SQL->requestExists($USER->uid, UnitySQL::REQUEST_BECOME_PI), ); } private function assertRequestedMembership(bool $expected, string $gid) { global $USER, $SQL; - $this->assertEquals( - $expected, - $SQL->requestExists($USER->uid, $gid) - ); + $this->assertEquals($expected, $SQL->requestExists($USER->uid, $gid)); } private function requestGroupCreation() { - http_post( - __DIR__ . "/../../webroot/panel/new_account.php", - ["new_user_sel" => "pi", "eula" => "agree", "confirm_pi" => "agree"] - ); + http_post(__DIR__ . "/../../webroot/panel/new_account.php", [ + "new_user_sel" => "pi", + "eula" => "agree", + "confirm_pi" => "agree", + ]); } private function requestGroupMembership(string $gid) { - http_post( - __DIR__ . "/../../webroot/panel/new_account.php", - ["new_user_sel" => "not_pi", "eula" => "agree", "pi" => $gid] - ); + http_post(__DIR__ . "/../../webroot/panel/new_account.php", [ + "new_user_sel" => "not_pi", + "eula" => "agree", + "pi" => $gid, + ]); } private function cancelAllRequests() { http_post( __DIR__ . "/../../webroot/panel/new_account.php", - ["cancel" => "true"] // value of cancel is arbitrary + ["cancel" => "true"], // value of cancel is arbitrary ); } private function approveUserByAdmin($gid, $uid) { - http_post( - __DIR__ . "/../../webroot/admin/pi-mgmt.php", - [ - "form_type" => "reqChild", - "action" => "Approve", - "pi" => $gid, - "uid" => $uid, - ] - ); + http_post(__DIR__ . "/../../webroot/admin/pi-mgmt.php", [ + "form_type" => "reqChild", + "action" => "Approve", + "pi" => $gid, + "uid" => $uid, + ]); } private function approveUserByPI($uid) { - http_post( - __DIR__ . "/../../webroot/panel/pi.php", - [ - "form_type" => "userReq", - "action" => "Approve", - "uid" => $uid, - ] - ); + http_post(__DIR__ . "/../../webroot/panel/pi.php", [ + "form_type" => "userReq", + "action" => "Approve", + "uid" => $uid, + ]); } private function approveGroup($uid) { - http_post( - __DIR__ . "/../../webroot/admin/pi-mgmt.php", - [ - "form_type" => "req", - "action" => "Approve", - "uid" => $uid, - ] - ); + http_post(__DIR__ . "/../../webroot/admin/pi-mgmt.php", [ + "form_type" => "req", + "action" => "Approve", + "uid" => $uid, + ]); } // delete requests made by that user @@ -124,10 +115,15 @@ private function ensureUserDoesNotExist() "memberuid", // array_diff will break the contiguity of the array indexes // ldap_mod_replace requires contiguity, array_values restores contiguity - array_values(array_diff($all_member_uids, [$USER->uid])) + array_values(array_diff($all_member_uids, [$USER->uid])), ); $all_users_group->write(); - ensure(!in_array($USER->uid, $all_users_group->getAttribute("memberuid"))); + ensure( + !in_array( + $USER->uid, + $all_users_group->getAttribute("memberuid"), + ), + ); } $REDIS->removeCacheArray("sorted_users", "", $USER->uid); } @@ -165,8 +161,10 @@ private function ensurePIGroupDoesNotExist() } #[DataProvider("provider")] - public function testCreateUserByJoinGoupByPI($user_to_create_args, $expected_uid_gid) - { + public function testCreateUserByJoinGoupByPI( + $user_to_create_args, + $expected_uid_gid, + ) { global $USER, $SSO, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK; $pi_user_args = getUserIsPIHasNoMembersNoMemberRequests(); switchUser(...$pi_user_args); @@ -174,7 +172,14 @@ public function testCreateUserByJoinGoupByPI($user_to_create_args, $expected_uid $gid = $pi_group->gid; switchUser(...$user_to_create_args); $this->assertTrue(!$USER->exists()); - $newOrg = new UnityOrg($SSO["org"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $newOrg = new UnityOrg( + $SSO["org"], + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); $this->assertTrue(!$newOrg->exists()); $this->assertTrue($pi_group->exists()); $this->assertTrue(!$pi_group->memberExists($USER)); @@ -214,8 +219,14 @@ public function testCreateUserByJoinGoupByPI($user_to_create_args, $expected_uid $user_entry = $LDAP->getUserEntry($approve_uid); $user_group_entry = $LDAP->getGroupEntry($approve_uid); - $this->assertEquals($expected_uid_gid, $user_entry->getAttribute("uidnumber")[0]); - $this->assertEquals($expected_uid_gid, $user_group_entry->getAttribute("gidnumber")[0]); + $this->assertEquals( + $expected_uid_gid, + $user_entry->getAttribute("uidnumber")[0], + ); + $this->assertEquals( + $expected_uid_gid, + $user_group_entry->getAttribute("gidnumber")[0], + ); // $third_request_failed = false; // try { @@ -271,15 +282,24 @@ public function testCreateMultipleUsersByJoinGoupByPI() } #[DataProvider("provider")] - public function testCreateUserByJoinGoupByAdmin($user_to_create_args, $expected_uid_gid) - { + public function testCreateUserByJoinGoupByAdmin( + $user_to_create_args, + $expected_uid_gid, + ) { global $USER, $SSO, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK; switchUser(...getUserIsPIHasNoMembersNoMemberRequests()); $pi_group = $USER->getPIGroup(); $gid = $pi_group->gid; switchUser(...$user_to_create_args); $this->assertTrue(!$USER->exists()); - $newOrg = new UnityOrg($SSO["org"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $newOrg = new UnityOrg( + $SSO["org"], + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); $this->assertTrue(!$newOrg->exists()); $this->assertTrue($pi_group->exists()); $this->assertTrue(!$pi_group->memberExists($USER)); @@ -319,8 +339,14 @@ public function testCreateUserByJoinGoupByAdmin($user_to_create_args, $expected_ $user_entry = $LDAP->getUserEntry($approve_uid); $user_group_entry = $LDAP->getGroupEntry($approve_uid); - $this->assertEquals($expected_uid_gid, $user_entry->getAttribute("uidnumber")[0]); - $this->assertEquals($expected_uid_gid, $user_group_entry->getAttribute("gidnumber")[0]); + $this->assertEquals( + $expected_uid_gid, + $user_entry->getAttribute("uidnumber")[0], + ); + $this->assertEquals( + $expected_uid_gid, + $user_group_entry->getAttribute("gidnumber")[0], + ); // $third_request_failed = false; // try { @@ -340,14 +366,23 @@ public function testCreateUserByJoinGoupByAdmin($user_to_create_args, $expected_ } #[DataProvider("provider")] - public function testCreateUserByCreateGroup($user_to_create_args, $expected_uid_gid) - { + public function testCreateUserByCreateGroup( + $user_to_create_args, + $expected_uid_gid, + ) { global $USER, $SSO, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK; switchuser(...$user_to_create_args); $pi_group = $USER->getPIGroup(); $this->assertTrue(!$USER->exists()); $this->assertTrue(!$pi_group->exists()); - $newOrg = new UnityOrg($SSO["org"], $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $newOrg = new UnityOrg( + $SSO["org"], + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); $this->assertTrue(!$newOrg->exists()); try { $this->requestGroupCreation(); @@ -382,8 +417,14 @@ public function testCreateUserByCreateGroup($user_to_create_args, $expected_uid_ $user_entry = $LDAP->getUserEntry($approve_uid); $user_group_entry = $LDAP->getGroupEntry($approve_uid); - $this->assertEquals($expected_uid_gid, $user_entry->getAttribute("uidnumber")[0]); - $this->assertEquals($expected_uid_gid, $user_group_entry->getAttribute("gidnumber")[0]); + $this->assertEquals( + $expected_uid_gid, + $user_entry->getAttribute("uidnumber")[0], + ); + $this->assertEquals( + $expected_uid_gid, + $user_group_entry->getAttribute("gidnumber")[0], + ); // $third_request_failed = false; // try { diff --git a/test/functional/PIMemberRequestTest.php b/test/functional/PIMemberRequestTest.php index 7b31e01b..c1a7dec0 100644 --- a/test/functional/PIMemberRequestTest.php +++ b/test/functional/PIMemberRequestTest.php @@ -5,21 +5,20 @@ class PiMemberRequestTest extends TestCase { - private function requestMembership(string $gid) { - http_post( - __DIR__ . "/../../webroot/panel/groups.php", - ["form_type" => "addPIform", "pi" => $gid], - ); + http_post(__DIR__ . "/../../webroot/panel/groups.php", [ + "form_type" => "addPIform", + "pi" => $gid, + ]); } private function cancelRequest(string $gid) { - http_post( - __DIR__ . "/../../webroot/panel/groups.php", - ["form_type" => "cancelPIForm", "pi" => $gid], - ); + http_post(__DIR__ . "/../../webroot/panel/groups.php", [ + "form_type" => "cancelPIForm", + "pi" => $gid, + ]); } public function testRequestMembership() @@ -31,12 +30,16 @@ public function testRequestMembership() $gid = $pi_group->gid; $this->assertTrue($USER->isPI()); $this->assertTrue($pi_group->exists()); - $this->assertTrue(arraysAreEqualUnOrdered([$pi], $pi_group->getGroupMembers())); + $this->assertTrue( + arraysAreEqualUnOrdered([$pi], $pi_group->getGroupMembers()), + ); $this->assertEquals([], $SQL->getRequests($gid)); switchUser(...getUserNotPiNotRequestedBecomePi()); $uid = $USER->uid; $this->assertFalse($USER->isPI()); - $this->assertFalse($SQL->requestExists($uid, UnitySQL::REQUEST_BECOME_PI)); + $this->assertFalse( + $SQL->requestExists($uid, UnitySQL::REQUEST_BECOME_PI), + ); $this->assertFalse($pi_group->memberExists($USER)); try { $this->requestMembership($gid); diff --git a/test/functional/PageLoadTest.php b/test/functional/PageLoadTest.php index 71d531dd..1d812b08 100644 --- a/test/functional/PageLoadTest.php +++ b/test/functional/PageLoadTest.php @@ -16,7 +16,10 @@ public static function provider() [$admin, __DIR__ . "/../../webroot/admin/user-mgmt.php"], [$admin, __DIR__ . "/../../webroot/admin/content.php"], [$admin, __DIR__ . "/../../webroot/admin/notices.php"], - [$nonexistent_user, __DIR__ . "/../../webroot/panel/new_account.php"], + [ + $nonexistent_user, + __DIR__ . "/../../webroot/panel/new_account.php", + ], [$normal_user, __DIR__ . "/../../webroot/panel/account.php"], [$normal_user, __DIR__ . "/../../webroot/panel/groups.php"], [$normal_user, __DIR__ . "/../../webroot/panel/support.php"], diff --git a/test/functional/PiBecomeRequestTest.php b/test/functional/PiBecomeRequestTest.php index abd446a1..6a549a5b 100644 --- a/test/functional/PiBecomeRequestTest.php +++ b/test/functional/PiBecomeRequestTest.php @@ -23,9 +23,11 @@ private function getNumberPiBecomeRequests() global $USER, $SQL; // FIXME table name, "admin" are private constants in UnitySQL // FIXME "admin" should be something else - $stmt = $SQL->getConn()->prepare( - "SELECT * FROM requests WHERE uid=:uid and request_for='admin'" - ); + $stmt = $SQL + ->getConn() + ->prepare( + "SELECT * FROM requests WHERE uid=:uid and request_for='admin'", + ); $uid = $USER->uid; $stmt->bindParam(":uid", $uid); $stmt->execute(); @@ -39,25 +41,21 @@ public function testRequestBecomePi() $this->assertFalse($USER->isPI()); $this->assertNumberPiBecomeRequests(0); try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "pi_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "pi_request", + ]); $this->assertNumberPiBecomeRequests(1); - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "cancel_pi_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "cancel_pi_request", + ]); $this->assertNumberPiBecomeRequests(0); - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "pi_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "pi_request", + ]); $this->assertNumberPiBecomeRequests(1); - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "pi_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "pi_request", + ]); $this->assertNumberPiBecomeRequests(1); } finally { if ($SQL->requestExists($USER, UnitySQL::REQUEST_BECOME_PI)) { @@ -69,15 +67,16 @@ public function testRequestBecomePi() public function testRequestBecomePiUserRequestedAccountDeletion() { global $USER, $SQL; - switchUser(...getUserNotPiNotRequestedBecomePiRequestedAccountDeletion()); + switchUser( + ...getUserNotPiNotRequestedBecomePiRequestedAccountDeletion(), + ); $this->assertFalse($USER->isPI()); $this->assertNumberPiBecomeRequests(0); $this->assertTrue($SQL->accDeletionRequestExists($USER->uid)); try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "pi_request"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "pi_request", + ]); $this->assertNumberPiBecomeRequests(0); } finally { if ($SQL->requestExists($USER, UnitySQL::REQUEST_BECOME_PI)) { diff --git a/test/functional/PiMemberApproveTest.php b/test/functional/PiMemberApproveTest.php index 020675ef..f8b3d700 100644 --- a/test/functional/PiMemberApproveTest.php +++ b/test/functional/PiMemberApproveTest.php @@ -6,42 +6,41 @@ use UnityWebPortal\lib\UnityGroup; use UnityWebPortal\lib\UnitySSO; -class PiMemberApproveTest extends TestCase { - static $userWithRequestSwitchArgs; - static $userWithoutRequestSwitchArgs; - static $piSwitchArgs; - static $pi; - static $userWithRequestUID; - static $userWithoutRequestUID; - static $piUID; - static $userWithRequest; - static $userWithoutRequest; - static $piGroup; - static $piGroupGID; +class PiMemberApproveTest extends TestCase +{ + static $userWithRequestSwitchArgs; + static $userWithoutRequestSwitchArgs; + static $piSwitchArgs; + static $pi; + static $userWithRequestUID; + static $userWithoutRequestUID; + static $piUID; + static $userWithRequest; + static $userWithoutRequest; + static $piGroup; + static $piGroupGID; private function approveUser(string $uid) { - http_post( - __DIR__ . "/../../webroot/panel/pi.php", - ["form_type" => "userReq", "action" => "Approve", "uid" => $uid] - ); + http_post(__DIR__ . "/../../webroot/panel/pi.php", [ + "form_type" => "userReq", + "action" => "Approve", + "uid" => $uid, + ]); } private function requestJoinPI(string $gid) { - http_post( - __DIR__ . "/../../webroot/panel/groups.php", - ["form_type" => "addPIform", "pi" => $gid] - ); + http_post(__DIR__ . "/../../webroot/panel/groups.php", [ + "form_type" => "addPIform", + "pi" => $gid, + ]); } private function assertGroupMembers(UnityGroup $group, array $members) { $this->assertTrue( - arraysAreEqualUnOrdered( - $members, - $group->getGroupMemberUIDs() - ) + arraysAreEqualUnOrdered($members, $group->getGroupMemberUIDs()), ); } diff --git a/test/functional/PiMemberDenyTest.php b/test/functional/PiMemberDenyTest.php index 08b944d3..373bab5b 100644 --- a/test/functional/PiMemberDenyTest.php +++ b/test/functional/PiMemberDenyTest.php @@ -4,10 +4,12 @@ use PHPUnit\Framework\Attributes\DataProvider; use UnityWebPortal\lib\UnityUser; -class PiMemberDenyTest extends TestCase { +class PiMemberDenyTest extends TestCase +{ static $requestUid; - public static function setUpBeforeClass(): void{ + public static function setUpBeforeClass(): void + { global $USER; switchUser(...getNormalUser()); self::$requestUid = $USER->uid; @@ -15,10 +17,11 @@ public static function setUpBeforeClass(): void{ private function denyUser(string $uid) { - post( - __DIR__ . "/../../webroot/panel/pi.php", - ["form_type" => "userReq", "action" => "approve", "uid" => $uid] - ); + post(__DIR__ . "/../../webroot/panel/pi.php", [ + "form_type" => "userReq", + "action" => "approve", + "uid" => $uid, + ]); } public function testDenyRequest() @@ -29,13 +32,17 @@ public function testDenyRequest() $piGroup = $USER->getPIGroup(); $this->assertTrue($piGroup->exists()); $this->assertTrue( - arraysAreEqualUnOrdered( - [$pi->uid], - $piGroup->getGroupMemberUIDs() - ) + arraysAreEqualUnOrdered([$pi->uid], $piGroup->getGroupMemberUIDs()), ); $this->assertEmpty($piGroup->getRequests()); - $requestedUser = new UnityUser(self::$requestUid, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $requestedUser = new UnityUser( + self::$requestUid, + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); try { $piGroup->newUserRequest( $requestedUser, @@ -51,8 +58,8 @@ public function testDenyRequest() $this->assertTrue( arraysAreEqualUnOrdered( [$pi->uid], - $piGroup->getGroupMemberUIDs() - ) + $piGroup->getGroupMemberUIDs(), + ), ); $this->assertFalse($piGroup->memberExists($requestedUser)); } finally { diff --git a/test/functional/PiRemoveUserTest.php b/test/functional/PiRemoveUserTest.php index dc4e7773..e6858bd5 100644 --- a/test/functional/PiRemoveUserTest.php +++ b/test/functional/PiRemoveUserTest.php @@ -4,13 +4,14 @@ use PHPUnit\Framework\Attributes\DataProvider; use UnityWebPortal\lib\UnityUser; -class PiRemoveUserTest extends TestCase { +class PiRemoveUserTest extends TestCase +{ private function removeUser(string $uid) { - http_post( - __DIR__ . "/../../webroot/panel/pi.php", - ["form_type" => "remUser", "uid" => $uid] - ); + http_post(__DIR__ . "/../../webroot/panel/pi.php", [ + "form_type" => "remUser", + "uid" => $uid, + ]); } public function testRemoveUser() @@ -29,7 +30,14 @@ public function testRemoveUser() $memberToDelete = null; foreach ($memberUIDs as $uid) { if ($uid != $piUid) { - $memberToDelete = new UnityUser($uid, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); + $memberToDelete = new UnityUser( + $uid, + $LDAP, + $SQL, + $MAILER, + $REDIS, + $WEBHOOK, + ); if ($memberToDelete->hasRequestedAccountDeletion()) { continue; } @@ -78,6 +86,6 @@ public function testRemovePIFromTheirOwnGroup() ); $piGroup->approveUser($pi); } - } + } } } diff --git a/test/functional/SSHKeyAddTest.php b/test/functional/SSHKeyAddTest.php index bd4abebb..c330ae58 100644 --- a/test/functional/SSHKeyAddTest.php +++ b/test/functional/SSHKeyAddTest.php @@ -7,30 +7,29 @@ class SSHKeyAddTest extends TestCase { - private function addSshKeysPaste(array $keys): void { + private function addSshKeysPaste(array $keys): void + { foreach ($keys as $key) { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - [ - "form_type" => "addKey", - "add_type" => "paste", - "key" => $key - ] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "addKey", + "add_type" => "paste", + "key" => $key, + ]); } } - private function addSshKeysImport(array $keys): void { + private function addSshKeysImport(array $keys): void + { foreach ($keys as $key) { $tmp = tmpfile(); $tmp_path = stream_get_meta_data($tmp)["uri"]; fwrite($tmp, $key); $_FILES["keyfile"] = ["tmp_name" => $tmp_path]; try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "addKey", "add_type" => "import"] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "addKey", + "add_type" => "import", + ]); $this->assertFalse(file_exists($tmp_path)); } finally { unset($_FILES["keyfile"]); @@ -38,46 +37,44 @@ private function addSshKeysImport(array $keys): void { } } - private function addSshKeysGenerate(array $keys): void { + private function addSshKeysGenerate(array $keys): void + { foreach ($keys as $key) { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - [ - "form_type" => "addKey", - "add_type" => "generate", - "gen_key" => $key - ] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "addKey", + "add_type" => "generate", + "gen_key" => $key, + ]); } } - private function addSshKeysGithub(array $keys): void { + private function addSshKeysGithub(array $keys): void + { global $GITHUB; $oldGithub = $GITHUB; $GITHUB = $this->createMock(UnityGithub::class); $GITHUB->method("getSshPublicKeys")->willReturn($keys); try { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - [ - "form_type" => "addKey", - "add_type" => "github", - "gh_user" => "foobar" - ] - ); + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "addKey", + "add_type" => "github", + "gh_user" => "foobar", + ]); } finally { $GITHUB = $oldGithub; } } - public static function provider() { - $validKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+XqO25MUB9x/pS04I3JQ7rMGboWyGXh0GUzkOrTi7a foobar"; + public static function provider() + { + $validKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+XqO25MUB9x/pS04I3JQ7rMGboWyGXh0GUzkOrTi7a foobar"; $invalidKey = "foobar"; $methods = [ "addSshKeysPaste", "addSshKeysImport", "addSshKeysGenerate", - "addSshKeysGithub" + "addSshKeysGithub", ]; $output = []; foreach ($methods as $method) { @@ -99,8 +96,11 @@ public function getKeyCount() } #[DataProvider("provider")] - public function testAddSshKeys(string $methodName, int $expectedKeysAdded, array $keys) - { + public function testAddSshKeys( + string $methodName, + int $expectedKeysAdded, + array $keys, + ) { global $USER; switchUser(...getUserHasNoSshKeys()); $numKeysBefore = $this->getKeyCount($USER); @@ -109,7 +109,10 @@ public function testAddSshKeys(string $methodName, int $expectedKeysAdded, array call_user_func([SSHKeyAddTest::class, $methodName], $keys); // $method($keys); $numKeysAfter = $this->getKeyCount($USER); - $this->assertEquals($expectedKeysAdded, ($numKeysAfter - $numKeysBefore)); + $this->assertEquals( + $expectedKeysAdded, + $numKeysAfter - $numKeysBefore, + ); } finally { $USER->setSSHKeys([]); } diff --git a/test/functional/SSHKeyDeleteTest.php b/test/functional/SSHKeyDeleteTest.php index d924acd3..c4a3aaab 100644 --- a/test/functional/SSHKeyDeleteTest.php +++ b/test/functional/SSHKeyDeleteTest.php @@ -3,26 +3,31 @@ use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; -class SSHKeyDeleteTest extends TestCase { +class SSHKeyDeleteTest extends TestCase +{ static $initialKeys; - public static function setUpBeforeClass(): void{ + public static function setUpBeforeClass(): void + { global $USER; switchUser(...getUserWithOneKey()); self::$initialKeys = $USER->getSSHKeys(true); } - private function deleteKey(string $index): void { - http_post( - __DIR__ . "/../../webroot/panel/account.php", - ["form_type" => "delKey", "delIndex" => $index] - ); + private function deleteKey(string $index): void + { + http_post(__DIR__ . "/../../webroot/panel/account.php", [ + "form_type" => "delKey", + "delIndex" => $index, + ]); } public static function getGarbageIndexArgs() { global $HTTP_HEADER_TEST_INPUTS; - return array_map(function($x){return [$x];}, $HTTP_HEADER_TEST_INPUTS); + return array_map(function ($x) { + return [$x]; + }, $HTTP_HEADER_TEST_INPUTS); } #[DataProvider("getGarbageIndexArgs")] diff --git a/test/functional/ViewAsUserTest.php b/test/functional/ViewAsUserTest.php index 12d6e70a..a4195e0d 100644 --- a/test/functional/ViewAsUserTest.php +++ b/test/functional/ViewAsUserTest.php @@ -14,13 +14,10 @@ public function _testViewAsUser(array $beforeUser, array $afterUser) // $this->assertTrue($USER->isAdmin()); $beforeUid = $USER->uid; // $this->assertNotEquals($afterUid, $beforeUid); - http_post( - __DIR__ . "/../../webroot/admin/user-mgmt.php", - [ - "form_type" => "viewAsUser", - "uid" => $afterUid, - ], - ); + http_post(__DIR__ . "/../../webroot/admin/user-mgmt.php", [ + "form_type" => "viewAsUser", + "uid" => $afterUid, + ]); $this->assertArrayHasKey("viewUser", $_SESSION); // redirect means that php process dies and user's browser will initiate a new one // this makes `require_once autoload.php` run again and init.php changes $USER @@ -29,10 +26,9 @@ public function _testViewAsUser(array $beforeUser, array $afterUser) // now we should be new user $this->assertEquals($afterUid, $USER->uid); // $this->assertTrue($_SESSION["user_exists"]); - http_post( - __DIR__ . "/../../resources/templates/header.php", - ["form_type" => "clearView"], - ); + http_post(__DIR__ . "/../../resources/templates/header.php", [ + "form_type" => "clearView", + ]); $this->assertArrayNotHasKey("viewUser", $_SESSION); // redirect means that php process dies and user's browser will initiate a new one // this makes `require_once autoload.php` run again and init.php changes $USER @@ -64,13 +60,10 @@ public function testNonAdminViewAsAdmin() $adminUid = $USER->uid; $this->assertTrue($USER->isAdmin()); switchUser(...getNormalUser()); - http_post( - __DIR__ . "/../../webroot/admin/user-mgmt.php", - [ - "form_type" => "viewAsUser", - "uid" => $adminUid, - ], - ); + http_post(__DIR__ . "/../../webroot/admin/user-mgmt.php", [ + "form_type" => "viewAsUser", + "uid" => $adminUid, + ]); $this->assertArrayNotHasKey("viewUser", $_SESSION); } } diff --git a/test/phpunit-bootstrap.php b/test/phpunit-bootstrap.php index 34b370d9..34f81f53 100644 --- a/test/phpunit-bootstrap.php +++ b/test/phpunit-bootstrap.php @@ -1,7 +1,9 @@ This is a paragraph

', + "", + "a", + "Hello, World!", + " Some text ", + " ", + "12345", + "abc123", + "Hello@World!", + str_repeat("a", 8190), // https://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfieldsize + "

This is a paragraph

", "'; DROP TABLE users; --", "", - 'こんにちは世界', + "こんにちは世界", "Hello 👋 World 🌍", "Line 1\nLine 2", "Column1\tColumn2", - 'MiXeD cAsE', - 'https://www.example.com', - 'user@example.com', + "MiXeD cAsE", + "https://www.example.com", + "user@example.com", '{"key": "value"}', - 'SGVsbG8sIFdvcmxkIQ==', + "SGVsbG8sIFdvcmxkIQ==", "Hello\x00World", - mbConvertEncoding("Hello, World!", "UTF-16") + mbConvertEncoding("Hello, World!", "UTF-16"), ]; function arraysAreEqualUnOrdered(array $a, array $b): bool { - return (array_diff($a, $b) == [] && array_diff($b, $a) == []); + return array_diff($a, $b) == [] && array_diff($b, $a) == []; } - function switchUser( string $eppn, string $given_name, string $sn, string $mail, - ?string $session_id = null + ?string $session_id = null, ): void { - global $REDIS, $LDAP, $SQL, $MAILER, $WEBHOOK, $GITHUB, $SITE, $SSO, $OPERATOR, $USER, $SEND_PIMESG_TO_ADMINS, $LOC_HEADER, $LOC_FOOTER; + global $REDIS, + $LDAP, + $SQL, + $MAILER, + $WEBHOOK, + $GITHUB, + $SITE, + $SSO, + $OPERATOR, + $USER, + $SEND_PIMESG_TO_ADMINS, + $LOC_HEADER, + $LOC_FOOTER; session_write_close(); if (is_null($session_id)) { session_id(str_replace(["_", "@", "."], "-", uniqid($eppn . "_"))); @@ -80,17 +95,29 @@ function switchUser( $_SERVER["eppn"] = $eppn; $_SERVER["givenName"] = $given_name; $_SERVER["sn"] = $sn; - include __DIR__ . "/../resources/autoload.php"; + include __DIR__ . "/../resources/autoload.php"; ensure(!is_null($USER)); } function http_post(string $phpfile, array $post_data): void { - global $REDIS, $LDAP, $SQL, $MAILER, $WEBHOOK, $GITHUB, $SITE, $SSO, $OPERATOR, $USER, $SEND_PIMESG_TO_ADMINS, $LOC_HEADER, $LOC_FOOTER; + global $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); - $_SERVER["REQUEST_URI"] = preg_replace("/.*webroot\//", "/", $phpfile); // Slightly imprecise because it doesn't include get parameters + $_SERVER["REQUEST_URI"] = preg_replace("/.*webroot\//", "/", $phpfile); // Slightly imprecise because it doesn't include get parameters $_POST = $post_data; ob_start(); $post_did_redirect_or_die = false; @@ -107,13 +134,25 @@ function http_post(string $phpfile, array $post_data): void ensure($post_did_redirect_or_die, "post did not redirect or die!"); } -function http_get(string $phpfile, array $get_data = array()): void +function http_get(string $phpfile, array $get_data = []): void { - global $REDIS, $LDAP, $SQL, $MAILER, $WEBHOOK, $GITHUB, $SITE, $SSO, $OPERATOR, $USER, $SEND_PIMESG_TO_ADMINS, $LOC_HEADER, $LOC_FOOTER; + global $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); - $_SERVER["REQUEST_URI"] = preg_replace("/.*webroot\//", "/", $phpfile); // Slightly imprecise because it doesn't include get parameters + $_SERVER["REQUEST_URI"] = preg_replace("/.*webroot\//", "/", $phpfile); // Slightly imprecise because it doesn't include get parameters $_GET = $get_data; ob_start(); try { @@ -192,13 +231,19 @@ function getNonExistentUserAndExpectedUIDGIDNoCustomMapping() { // defaults/config.ini.default: ldap.offset_UIDGID=1000000 // test/custom_user_mappings/test.csv has reservations for 1000000-1000004 - return [["user2002@org998.test", "foo", "bar", "user2002@org998.test"], 1000005]; + return [ + ["user2002@org998.test", "foo", "bar", "user2002@org998.test"], + 1000005, + ]; } function getNonExistentUserAndExpectedUIDGIDWithCustomMapping() { // test/custom_user_mappings/test.csv: {user2001: 555} - return [["user2001@org998.test", "foo", "bar", "user2001@org998.test"], 555]; + return [ + ["user2001@org998.test", "foo", "bar", "user2001@org998.test"], + 555, + ]; } function getMultipleValueAttributesAndExpectedSSO() @@ -214,7 +259,7 @@ function getMultipleValueAttributesAndExpectedSSO() "firstname" => "foo", "lastname" => "bar", "mail" => "user2003@org998.test", - ] + ], ]; } diff --git a/test/unit/AjaxSshValidateTest.php b/test/unit/AjaxSshValidateTest.php index 43a3e685..e22cb0b2 100644 --- a/test/unit/AjaxSshValidateTest.php +++ b/test/unit/AjaxSshValidateTest.php @@ -12,7 +12,10 @@ public static function providerTestSshValidate() // sanity check only, see UnityHTTPDTest for more comprehensive test cases return [ [false, "foobar"], - [true, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+XqO25MUB9x/pS04I3JQ7rMGboWyGXh0GUzkOrTi7a"], + [ + true, + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+XqO25MUB9x/pS04I3JQ7rMGboWyGXh0GUzkOrTi7a", + ], ]; } diff --git a/test/unit/UnityGithubTest.php b/test/unit/UnityGithubTest.php index cd62d4c8..57cd5f8f 100644 --- a/test/unit/UnityGithubTest.php +++ b/test/unit/UnityGithubTest.php @@ -17,7 +17,12 @@ public static function providerTestGetGithubKeys() # user with no keys ["sheldor1510", []], # user with 1 key - ["simonLeary42", ["ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLeHpW10CCamJtXNXJui49WM07wRnQbQTbQ2MSvF4j8vBpBuAbjiEp14qERLDs3FoWdpbiUwL9mZq6PmUSxaTnk="]] + [ + "simonLeary42", + [ + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLeHpW10CCamJtXNXJui49WM07wRnQbQTbQ2MSvF4j8vBpBuAbjiEp14qERLDs3FoWdpbiUwL9mZq6PmUSxaTnk=", + ], + ], ]; } diff --git a/test/unit/UnitySSOTest.php b/test/unit/UnitySSOTest.php index 12469626..bb4ae3aa 100644 --- a/test/unit/UnitySSOTest.php +++ b/test/unit/UnitySSOTest.php @@ -65,10 +65,9 @@ public function testFallbackAttribute() $this->assertEquals("foobar@baz", $sso["mail"]); } - public static function validEppnProvider() { - return [ - ["foo@bar.edu", "foo_bar_edu", "bar_edu"], - ]; + public static function validEppnProvider() + { + return [["foo@bar.edu", "foo_bar_edu", "bar_edu"]]; } #[DataProvider("validEppnProvider")] @@ -103,11 +102,12 @@ public function testEppnToOrg(string $eppn, string $_, string $expectedOrg) } } - public static function invalidEppnProvider() { + public static function invalidEppnProvider() + { return [ ["foo"], // missing @ ["foo@bar@baz"], // too many @ - [""] + [""], ]; } diff --git a/test/unit/UtilsTest.php b/test/unit/UtilsTest.php index 6f16a651..f024e0ef 100644 --- a/test/unit/UtilsTest.php +++ b/test/unit/UtilsTest.php @@ -11,9 +11,9 @@ public function testArrayGetReturnsValueWhenKeyExists() $array = [ "a" => [ "b" => [ - "c" => 123 - ] - ] + "c" => 123, + ], + ], ]; $result = \arrayGet($array, "a", "b", "c"); $this->assertSame(123, $result); @@ -23,8 +23,8 @@ public function testArrayGetReturnsArrayWhenTraversingPartially() { $array = [ "foo" => [ - "bar" => "baz" - ] + "bar" => "baz", + ], ]; $result = \arrayGet($array, "foo"); $this->assertSame(["bar" => "baz"], $result); @@ -120,10 +120,14 @@ public static function SSHKeyProvider() "gG7Fy2a+VWwcB6w0nzyxbqg16AP+luuqHxfVsvP6Uyde4C7LPeB3r3GhAfuU", "Nxnpz/bXGxbJu3+aCnbtaZMzGJ6UFBeJp8MtlmVajDnjx3oEuOGGmobTlaop", "HYVsQ3ySfQ==", - ]) + ]), ]; - $validKeysArgs = array_map(function($x){return [true, $x];}, $validKeys); - $invalidKeysArgs = array_map(function($x){return [false, $x];}, $HTTP_HEADER_TEST_INPUTS); + $validKeysArgs = array_map(function ($x) { + return [true, $x]; + }, $validKeys); + $invalidKeysArgs = array_map(function ($x) { + return [false, $x]; + }, $HTTP_HEADER_TEST_INPUTS); return $validKeysArgs + $invalidKeysArgs; } diff --git a/tools/docker-dev/docker-compose.yml b/tools/docker-dev/docker-compose.yml index 7d0d426e..3a712dc6 100644 --- a/tools/docker-dev/docker-compose.yml +++ b/tools/docker-dev/docker-compose.yml @@ -9,7 +9,11 @@ services: - "8010:80" - "389:389" healthcheck: - test: ["CMD-SHELL", "if [ -f /tmp/up ]; then (nc -z localhost 389 && touch /tmp/up); else true; fi"] + test: + [ + "CMD-SHELL", + "if [ -f /tmp/up ]; then (nc -z localhost 389 && touch /tmp/up); else true; fi", + ] interval: 1s sql: hostname: sql @@ -18,7 +22,11 @@ services: - "8020:80" - "3306:3306" healthcheck: - test: ["CMD-SHELL", "if [ -f /tmp/up ]; then (nc -z localhost 3306 && touch /tmp/up); else true; fi"] + test: + [ + "CMD-SHELL", + "if [ -f /tmp/up ]; then (nc -z localhost 3306 && touch /tmp/up); else true; fi", + ] interval: 1s smtp: hostname: smtp @@ -31,7 +39,11 @@ services: ports: - "6379:6379" healthcheck: - test: ["CMD-SHELL", "if [ -f /tmp/up ]; then (nc -z localhost 6379 && touch /tmp/up); else true; fi"] + test: + [ + "CMD-SHELL", + "if [ -f /tmp/up ]; then (nc -z localhost 6379 && touch /tmp/up); else true; fi", + ] interval: 1s web: hostname: web diff --git a/tools/docker-dev/identity/phpldapadmin-config.php b/tools/docker-dev/identity/phpldapadmin-config.php index 3bd9ef93..b4253ae0 100644 --- a/tools/docker-dev/identity/phpldapadmin-config.php +++ b/tools/docker-dev/identity/phpldapadmin-config.php @@ -1,15 +1,18 @@ custom->appearance['timezone'] = 'America/New_York'; -$config->custom->appearance['hide_template_warning'] = true; -$config->custom->appearance['friendly_attrs'] = array(); +$config->custom->appearance["timezone"] = "America/New_York"; +$config->custom->appearance["hide_template_warning"] = true; +$config->custom->appearance["friendly_attrs"] = []; $servers = new Datastore(); -$servers->newServer('ldap_pla'); -$servers->setValue('server', 'name', 'unity-web-portal-dev'); -$servers->setValue('server', 'host', 'identity'); -$servers->setValue('login', 'auth_type', 'config'); -$servers->setValue('login', 'bind_id', 'cn=admin,dc=unityhpc,dc=test'); -$servers->setValue('login', 'bind_pass', 'password'); -$servers->setValue('server', 'base', array('dc=unityhpc,dc=test')); -$servers->setValue('auto_number', 'min', array('uidNumber' => 30000,'gidNumber' => 30000)); +$servers->newServer("ldap_pla"); +$servers->setValue("server", "name", "unity-web-portal-dev"); +$servers->setValue("server", "host", "identity"); +$servers->setValue("login", "auth_type", "config"); +$servers->setValue("login", "bind_id", "cn=admin,dc=unityhpc,dc=test"); +$servers->setValue("login", "bind_pass", "password"); +$servers->setValue("server", "base", ["dc=unityhpc,dc=test"]); +$servers->setValue("auto_number", "min", [ + "uidNumber" => 30000, + "gidNumber" => 30000, +]); diff --git a/tools/docker-dev/sql/phpmyadmin-config.php b/tools/docker-dev/sql/phpmyadmin-config.php index 24716bf1..7814b5f8 100644 --- a/tools/docker-dev/sql/phpmyadmin-config.php +++ b/tools/docker-dev/sql/phpmyadmin-config.php @@ -1,15 +1,15 @@ * { - display: block; +form > * { + display: block; } -form>*:not(:first-child) { - margin-top: 5px; +form > *:not(:first-child) { + margin-top: 5px; } input:focus, textarea:focus, select:focus { - outline: var(--accent) solid 2px; + outline: var(--accent) solid 2px; } button:focus, -input[type=submit]:focus { - outline: none; +input[type="submit"]:focus { + outline: none; } -input[type=text], -input[type=password], -input[type=date] { - margin: 10px 0 10px 0; - border: 1px solid var(--light_borders); - padding: 5px; - width: calc(100% - 12px); - /* Factors in extra border and padding */ - max-width: 300px; - border-radius: 5px; +input[type="text"], +input[type="password"], +input[type="date"] { + margin: 10px 0 10px 0; + border: 1px solid var(--light_borders); + padding: 5px; + width: calc(100% - 12px); + /* Factors in extra border and padding */ + max-width: 300px; + border-radius: 5px; } -input[type=submit], +input[type="submit"], button { - background: var(--accent); - color: var(--accent_foreground); - cursor: pointer; - border: 0; - padding: 8px 12px 8px 12px; - border-radius: 5px; - transition: background 0.1s; - display: inline-block; + background: var(--accent); + color: var(--accent_foreground); + cursor: pointer; + border: 0; + padding: 8px 12px 8px 12px; + border-radius: 5px; + transition: background 0.1s; + display: inline-block; } -input[type=submit]:hover, +input[type="submit"]:hover, button:hover { - background: var(--accent_2); + background: var(--accent_2); } -input[type=submit]:disabled, +input[type="submit"]:disabled, button:disabled { - background: var(--accent_disabled); - cursor: default; + background: var(--accent_disabled); + cursor: default; } -input[type=checkbox], -input[type=radio] { - display: inline-block; +input[type="checkbox"], +input[type="radio"] { + display: inline-block; } label { - display: inline; - user-select: none; - font-size: 10pt; - color: var(--color-background-text-inactive); - overflow: hidden; + display: inline; + user-select: none; + font-size: 10pt; + color: var(--color-background-text-inactive); + overflow: hidden; } -input[type=radio] { - margin-bottom: 0; +input[type="radio"] { + margin-bottom: 0; } select { - border: 1px solid var(--light_borders); - background: white; - padding: 5px 10px 5px 20px; - max-width: 300px; - border-radius: 5px; + border: 1px solid var(--light_borders); + background: white; + padding: 5px 10px 5px 20px; + max-width: 300px; + border-radius: 5px; } textarea { - border: 1px solid var(--light_borders); - width: calc(100% - 12px); - min-height: 150px; - display: block; - margin: 10px 0 10px 0; - resize: vertical; - font-family: Arial, Helvetica, sans-serif; - font-size: 10pt; - border-radius: 5px; - padding: 5px; + border: 1px solid var(--light_borders); + width: calc(100% - 12px); + min-height: 150px; + display: block; + margin: 10px 0 10px 0; + resize: vertical; + font-family: Arial, Helvetica, sans-serif; + font-size: 10pt; + border-radius: 5px; + padding: 5px; } div.inline * { - display: inline; - margin-right: 10px; + display: inline; + margin-right: 10px; } /* @@ -224,122 +224,122 @@ div.inline * { /* WRAPPERS */ main { - flex: 1 0 auto; - padding: 15px 20px 20px 20px; - max-width: 1000px; - line-height: 24px; + flex: 1 0 auto; + padding: 15px 20px 20px 20px; + max-width: 1000px; + line-height: 24px; } /* FOOTER */ footer { - background: var(--light_footer_background); - flex-shrink: 0; - text-align: center; - font-size: 8pt; - color: var(--light_footer_foreground); - padding-bottom: 10px; + background: var(--light_footer_background); + flex-shrink: 0; + text-align: center; + font-size: 8pt; + color: var(--light_footer_foreground); + padding-bottom: 10px; } -footer>* { - display: table; - margin: 10px auto 0 auto; +footer > * { + display: table; + margin: 10px auto 0 auto; } footer #footerLogos img { - padding: 0 10px 0 10px; - max-width: 120px; - max-height: 90px; - margin-bottom: 10px; - vertical-align: middle; + padding: 0 10px 0 10px; + max-width: 120px; + max-height: 90px; + margin-bottom: 10px; + vertical-align: middle; } @media only screen and (min-width: 501px) { - footer #footerLogos>* { - display: inline-block; - } + footer #footerLogos > * { + display: inline-block; + } } @media only screen and (max-width: 500px) { - footer #footerLogos>* { - display: block; - } + footer #footerLogos > * { + display: block; + } } /* notices */ div.notice { - background: var(--light_panel_background); - border-radius: 10px; - margin: 20px 0 20px 0; - padding-bottom: 5px; + background: var(--light_panel_background); + border-radius: 10px; + margin: 20px 0 20px 0; + padding-bottom: 5px; } -div.notice>span.noticeTitle { - display: block; - background: var(--accent); - color: var(--accent_foreground); - border-radius: 8px 8px 0 0; - padding: 10px 10px 0 10px; - font-size: 14pt; - font-weight: bold; +div.notice > span.noticeTitle { + display: block; + background: var(--accent); + color: var(--accent_foreground); + border-radius: 8px 8px 0 0; + padding: 10px 10px 0 10px; + font-size: 14pt; + font-weight: bold; } -div.notice>span.noticeDate { - display: block; - background: var(--accent); - color: var(--accent_foreground); - padding: 0 10px 10px 10px; +div.notice > span.noticeDate { + display: block; + background: var(--accent); + color: var(--accent_foreground); + padding: 0 10px 10px 10px; } -div.notice>p { - margin: 10px 15px 10px 15px; - padding: 0; +div.notice > p { + margin: 10px 15px 10px 15px; + padding: 0; } -div.notice>div.noticeText { - padding: 0 10px 0 10px; +div.notice > div.noticeText { + padding: 0 10px 0 10px; } -div.notice>button { - margin: 0 0 5px 10px; +div.notice > button { + margin: 0 0 5px 10px; } #viewAsBar { - background: orange; - padding: 10px; - border-radius: 10px; - margin-bottom: 20px; + background: orange; + padding: 10px; + border-radius: 10px; + margin-bottom: 20px; } -#viewAsBar>* { - display: inline-block; +#viewAsBar > * { + display: inline-block; } -#viewAsBar input[type=submit] { - margin: 0 0 0 20px; +#viewAsBar input[type="submit"] { + margin: 0 0 0 20px; } /* Search Box CSS */ div.searchWrapper { - background: var(--light_background); - position: absolute; - top: 50px; - z-index: 200; - overflow: hidden; - font-size: 11pt; - user-select: none; - width: 100%; - margin-left: -2px; - border: 1px solid var(--light_borders); - border-radius: 5px; -} - -div.searchWrapper>* { - display: block; - cursor: pointer; - padding: 4px 10px 4px 10px; -} - -div.searchWrapper>*:hover { - background: var(--light_panel_background); + background: var(--light_background); + position: absolute; + top: 50px; + z-index: 200; + overflow: hidden; + font-size: 11pt; + user-select: none; + width: 100%; + margin-left: -2px; + border: 1px solid var(--light_borders); + border-radius: 5px; +} + +div.searchWrapper > * { + display: block; + cursor: pointer; + padding: 4px 10px 4px 10px; +} + +div.searchWrapper > *:hover { + background: var(--light_panel_background); } diff --git a/webroot/css/modal.css b/webroot/css/modal.css index 7252e62b..af0aa313 100644 --- a/webroot/css/modal.css +++ b/webroot/css/modal.css @@ -1,67 +1,66 @@ div.modalWrapper { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: rgba(0, 0, 0, 0.6); - z-index: 100; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.6); + z-index: 100; } div.modalContent { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: var(--light_background); - padding: 15px; - width: calc(100% - 30px); - box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.4); - border-radius: 5px; - max-width: 600px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--light_background); + padding: 15px; + width: calc(100% - 30px); + box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.4); + border-radius: 5px; + max-width: 600px; } div.modalTitleWrapper { - overflow: hidden; - display: inline-block; + overflow: hidden; + display: inline-block; } span.modalTitle { - font-size: 13pt; + font-size: 13pt; } div.modalMessages { - color: var(--color-text-failure); - font-size: 11pt; + color: var(--color-text-failure); + font-size: 11pt; } -div.modalMessages>* { - margin-top: 7px; - display: block; +div.modalMessages > * { + margin-top: 7px; + display: block; } -div.modalBody>* { - margin: 0; +div.modalBody > * { + margin: 0; } .btnClose { - width: 30px; - height: 30px; - padding: 0; - text-align: center; - font-size: 20pt; + width: 30px; + height: 30px; + padding: 0; + text-align: center; + font-size: 20pt; } - .btnClose::before { - content: "\00d7"; + content: "\00d7"; } .buttonList { - margin-top: 10px; + margin-top: 10px; } -.buttonList>* { - display: inline-block; - margin-right: 10px; +.buttonList > * { + display: inline-block; + margin-right: 10px; } diff --git a/webroot/css/navbar.css b/webroot/css/navbar.css index 5f3e919a..42b73456 100644 --- a/webroot/css/navbar.css +++ b/webroot/css/navbar.css @@ -1,120 +1,120 @@ /* Navbar */ hr.navHR { - margin-left: 10px; - margin-right: 10px; + margin-left: 10px; + margin-right: 10px; } nav.mainNav { - background: var(--accent); - position: fixed; - left: 0; - top: 45px; - font-size: 11pt; - overflow: hidden; - white-space: nowrap; - z-index: 100; + background: var(--accent); + position: fixed; + left: 0; + top: 45px; + font-size: 11pt; + overflow: hidden; + white-space: nowrap; + z-index: 100; } nav.mainNav a { - color: var(--accent_foreground); - background: var(--accent_1); - border-radius: 10px; - display: block; - margin: 10px 8px 10px 8px; - padding: 8px; - text-decoration: none; - cursor: pointer; - transition: background 0.1s; + color: var(--accent_foreground); + background: var(--accent_1); + border-radius: 10px; + display: block; + margin: 10px 8px 10px 8px; + padding: 8px; + text-decoration: none; + cursor: pointer; + transition: background 0.1s; } nav.mainNav a:hover { - text-decoration: none; - background: var(--accent_2); + text-decoration: none; + background: var(--accent_2); } nav.mainNav a.active { - background: var(--accent_2); + background: var(--accent_2); } header { - background: var(--accent); - height: 50px; - width: 250px; - position: fixed; - top: 0; - left: 0; + background: var(--accent); + height: 50px; + width: 250px; + position: fixed; + top: 0; + left: 0; } -header>#imgLogo { - height: 65%; - margin-left: 10px; - margin-top: 8px; +header > #imgLogo { + height: 65%; + margin-left: 10px; + margin-top: 8px; } -header>a.unity-state { - background: var(--accent_foreground); - color: var(--accent); - display: block; - position: absolute; - top: 8px; - left: 140px; - padding: 1px 5px 1px 5px; - font-weight: bold; - font-size: 10pt; - border-radius: 5px; +header > a.unity-state { + background: var(--accent_foreground); + color: var(--accent); + display: block; + position: absolute; + top: 8px; + left: 140px; + padding: 1px 5px 1px 5px; + font-weight: bold; + font-size: 10pt; + border-radius: 5px; } -header>a.unity-state:hover { - text-decoration: none; +header > a.unity-state:hover { + text-decoration: none; } -header>button.hamburger { - background: var(--accent); - padding: 0; - right: 23px; - height: 60%; +header > button.hamburger { + background: var(--accent); + padding: 0; + right: 23px; + height: 60%; } -header>button.hamburger>img { - position: relative; - height: 100%; +header > button.hamburger > img { + position: relative; + height: 100%; } /* MOBILE VIEW */ @media only screen and (max-width: 1000px) { - nav.mainNav { - right: 0; - } - - header { - width: 100%; - } - - main { - /* Header element height + 2*element padding */ - margin-top: 45px; - } + nav.mainNav { + right: 0; + } + + header { + width: 100%; + } + + main { + /* Header element height + 2*element padding */ + margin-top: 45px; + } } /* DESKTOP VIEW */ @media only screen and (min-width: 1001px) { - nav.mainNav { - bottom: 0; - width: 250px; - } - - header>button.hamburger { - display: none; - } - - main { - margin-left: 250px; - } - - footer { - margin-left: 250px; - } + nav.mainNav { + bottom: 0; + width: 250px; + } + + header > button.hamburger { + display: none; + } + + main { + margin-left: 250px; + } + + footer { + margin-left: 250px; + } } diff --git a/webroot/css/tables.css b/webroot/css/tables.css index 132c1f5e..d867ae48 100644 --- a/webroot/css/tables.css +++ b/webroot/css/tables.css @@ -1,142 +1,142 @@ table { - border-collapse: collapse; - margin-bottom: 10px; - table-layout: fixed; - word-wrap: break-word; - max-width: 100%; + border-collapse: collapse; + margin-bottom: 10px; + table-layout: fixed; + word-wrap: break-word; + max-width: 100%; } table.longTable { - width: 100%; + width: 100%; } table.longTable tr:not(:last-child) { - border-bottom: 1px solid var(--light_borders); + border-bottom: 1px solid var(--light_borders); } table tr, table td { - padding: 0 20px 0 4px; - overflow: hidden; - white-space: nowrap; + padding: 0 20px 0 4px; + overflow: hidden; + white-space: nowrap; } table button, -table input[type=submit] { - padding: 5px 10px 5px 10px; +table input[type="submit"] { + padding: 5px 10px 5px 10px; } table form { - display: inline-block; + display: inline-block; } table form:not(:first-child) { - margin-left: 10px; + margin-left: 10px; } -table form>* { - margin: 0; +table form > * { + margin: 0; } tr.expanded { - border: 0; + border: 0; } -tr.expanded>td { - background: var(--light_panel_background); +tr.expanded > td { + background: var(--light_panel_background); } tr.expandable { - cursor: pointer; + cursor: pointer; } tr.expandable:hover { - background: var(--light_footer_background); + background: var(--light_footer_background); } -tr.expandable:hover>td:first-child { - border-top-left-radius: 10px; - border-bottom-left-radius: 10px; +tr.expandable:hover > td:first-child { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; } -tr.expandable:hover>td:last-child { - border-top-right-radius: 10px; - border-bottom-right-radius: 10px; +tr.expandable:hover > td:last-child { + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; } -tr.expandable.first>td:first-child { - border-bottom-left-radius: 0; +tr.expandable.first > td:first-child { + border-bottom-left-radius: 0; } -tr.expandable.first>td:last-child { - border-bottom-right-radius: 0; +tr.expandable.first > td:last-child { + border-bottom-right-radius: 0; } tr.expanded.first { - border-bottom: 2px solid var(--light_borders); + border-bottom: 2px solid var(--light_borders); } -tr.expanded.first>td:first-child { - border-top-left-radius: 10px; +tr.expanded.first > td:first-child { + border-top-left-radius: 10px; } -tr.expanded.first>td:last-child { - border-top-right-radius: 10px; +tr.expanded.first > td:last-child { + border-top-right-radius: 10px; } -tr.expanded.last>td:first-child { - border-bottom-left-radius: 10px; +tr.expanded.last > td:first-child { + border-bottom-left-radius: 10px; } -tr.expanded.last>td:last-child { - border-bottom-right-radius: 10px; +tr.expanded.last > td:last-child { + border-bottom-right-radius: 10px; } tr.expanded:not(.expandable) { - padding-top: 0; - padding-bottom: 0; + padding-top: 0; + padding-bottom: 0; } -tr.expanded:not(.expandable)>td:first-child { - padding-left: 30px; +tr.expanded:not(.expandable) > td:first-child { + padding-left: 30px; } button.btnExpanded { - transform: rotate(90deg); + transform: rotate(90deg); } /* Buttons */ button.btnExpand { - margin: 0 10px 0 0; - padding: 0; - background: transparent; - color: var(--accent); + margin: 0 10px 0 0; + padding: 0; + background: transparent; + color: var(--accent); } button.btnExpand:hover { - background: transparent; + background: transparent; } button.btnExpanded { - transform: rotate(90deg); + transform: rotate(90deg); } -td:last-child>button { - float: right; +td:last-child > button { + float: right; } tr.key { - background: var(--light_panel_background); - text-align: center; - font-weight: bold; + background: var(--light_panel_background); + text-align: center; + font-weight: bold; } -tr.key>td:first-child { - border-top-left-radius: 10px; - border-bottom-left-radius: 10px; +tr.key > td:first-child { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; } -tr.key>td:last-child { - border-top-right-radius: 10px; - border-bottom-right-radius: 10px; +tr.key > td:last-child { + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; } diff --git a/webroot/js/filter.js b/webroot/js/filter.js index af8d85a7..7e2bfa14 100644 --- a/webroot/js/filter.js +++ b/webroot/js/filter.js @@ -1,90 +1,102 @@ function getQueryVariable(variable) { - var query = window.location.search.substring(1); - var vars = query.split("&"); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split("="); + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split("="); - if (pair[0] == variable) { - return pair[1]; - } + if (pair[0] == variable) { + return pair[1]; } - return false; + } + return false; } function updateQueryStringParameter(uri, key, value) { - let currentURL = new URL(window.location.href); - let params = currentURL.searchParams; - if (params.has(key)) { - params.delete(key); - } - params.append(key, value); - window.history.pushState("object or string", "Title", currentURL.href); + let currentURL = new URL(window.location.href); + let params = currentURL.searchParams; + if (params.has(key)) { + params.delete(key); + } + params.append(key, value); + window.history.pushState("object or string", "Title", currentURL.href); } function updateFilterInput() { - const commonFilterInputBox = document.querySelector(".filterSearch"); - commonFilterInputBox.style.display = "none"; - commonFilterInputBox.style.visibility = "hidden"; - commonFilterInputBox.value = ""; - - var filter = getQueryVariable("filter"); - if (filter) { - commonFilterInputBox.style.display = "inline-block"; - commonFilterInputBox.style.visibility = "visible"; + const commonFilterInputBox = document.querySelector(".filterSearch"); + commonFilterInputBox.style.display = "none"; + commonFilterInputBox.style.visibility = "hidden"; + commonFilterInputBox.value = ""; - if (filter == "uid") { - commonFilterInputBox.placeholder = "Filter by " + filter.toUpperCase() + '...'; - } else { - commonFilterInputBox.placeholder = "Filter by " + filter.charAt(0).toUpperCase() + filter.slice(1) + '...'; - } + var filter = getQueryVariable("filter"); + if (filter) { + commonFilterInputBox.style.display = "inline-block"; + commonFilterInputBox.style.visibility = "visible"; - if (getQueryVariable("value") != false) { - commonFilterInputBox.value = getQueryVariable("value"); - filterRows(); - } + if (filter == "uid") { + commonFilterInputBox.placeholder = + "Filter by " + filter.toUpperCase() + "..."; + } else { + commonFilterInputBox.placeholder = + "Filter by " + filter.charAt(0).toUpperCase() + filter.slice(1) + "..."; + } - commonFilterInputBox.addEventListener("keyup", function(e) { - updateQueryStringParameter(window.location.href, "value", e.target.value); - filterRows(); - }); + if (getQueryVariable("value") != false) { + commonFilterInputBox.value = getQueryVariable("value"); + filterRows(); } + + commonFilterInputBox.addEventListener("keyup", function (e) { + updateQueryStringParameter(window.location.href, "value", e.target.value); + filterRows(); + }); + } } updateFilterInput(); var filters = document.querySelectorAll("span.filter"); -filters.forEach(function(filter) { - filter.addEventListener("click", function(e) { - e.preventDefault(); - e.stopPropagation(); - if (e.target.parentElement.id != getQueryVariable("filter")) { - updateQueryStringParameter(window.location.href, "filter", e.target.parentElement.id); - updateQueryStringParameter(window.location.href, "value", ""); - filterRows(); - } else { - updateQueryStringParameter(window.location.href, "filter", ""); - updateQueryStringParameter(window.location.href, "value", ""); - filterRows(); - } - updateFilterInput(); - }); +filters.forEach(function (filter) { + filter.addEventListener("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + if (e.target.parentElement.id != getQueryVariable("filter")) { + updateQueryStringParameter( + window.location.href, + "filter", + e.target.parentElement.id, + ); + updateQueryStringParameter(window.location.href, "value", ""); + filterRows(); + } else { + updateQueryStringParameter(window.location.href, "filter", ""); + updateQueryStringParameter(window.location.href, "value", ""); + filterRows(); + } + updateFilterInput(); + }); }); function filterRows() { - var filter = getQueryVariable("filter"); - var filterValue = getQueryVariable("value"); + var filter = getQueryVariable("filter"); + var filterValue = getQueryVariable("value"); - if (filter) { - var table = document.querySelector("table.filterable"); - var rows = Array.from(table.querySelectorAll("tr:nth-child(n+2)")); - var column = table.querySelector("tr.key").querySelector("td#" + filter).cellIndex; - rows.forEach(function(row) { - if (row.cells[column].textContent.trim().toLowerCase().indexOf(filterValue.toLowerCase()) == -1) { - row.style.display = "none"; - } else { - row.style.display = ""; - } - } - ); - } + if (filter) { + var table = document.querySelector("table.filterable"); + var rows = Array.from(table.querySelectorAll("tr:nth-child(n+2)")); + var column = table + .querySelector("tr.key") + .querySelector("td#" + filter).cellIndex; + rows.forEach(function (row) { + if ( + row.cells[column].textContent + .trim() + .toLowerCase() + .indexOf(filterValue.toLowerCase()) == -1 + ) { + row.style.display = "none"; + } else { + row.style.display = ""; + } + }); + } } diff --git a/webroot/js/global.js b/webroot/js/global.js index cfaade95..933bd194 100644 --- a/webroot/js/global.js +++ b/webroot/js/global.js @@ -3,9 +3,9 @@ $(window).resize(setNav); function setNav() { if ($("button.hamburger").is(":visible")) { - $("nav.mainNav").hide(); // Mobile View + $("nav.mainNav").hide(); // Mobile View } else { - $("nav.mainNav").show(); // Desktop View + $("nav.mainNav").show(); // Desktop View } } @@ -19,36 +19,39 @@ $("button.hamburger").on("click", function () { }); $(window).click(function (e) { - if (!$(e.target).parent().hasClass("hamburger") && $("button.hamburger").is(":visible")) { + if ( + !$(e.target).parent().hasClass("hamburger") && + $("button.hamburger").is(":visible") + ) { $("nav.mainNav").fadeOut(100); } }); // Functions to set nav links as active. Sub links can activate parents by naming files with same prefix, for example: documentation.php and documentation_view.php activate the same link var url = location.pathname; -if (url.lastIndexOf('.') >= 0) { - url = url.substring(0, url.lastIndexOf('.')); +if (url.lastIndexOf(".") >= 0) { + url = url.substring(0, url.lastIndexOf(".")); } -if (url.lastIndexOf('/') >= 0) { - url = url.substring(url.lastIndexOf('/') + 1); +if (url.lastIndexOf("/") >= 0) { + url = url.substring(url.lastIndexOf("/") + 1); } -$('nav.mainNav a').each(function () { +$("nav.mainNav a").each(function () { var href = $(this).attr("href"); - if (href.lastIndexOf('.') >= 0) { - href = href.substring(0, href.lastIndexOf('.')); + if (href.lastIndexOf(".") >= 0) { + href = href.substring(0, href.lastIndexOf(".")); } - if (href.lastIndexOf('/') >= 0) { - href = href.substring(href.lastIndexOf('/') + 1); + if (href.lastIndexOf("/") >= 0) { + href = href.substring(href.lastIndexOf("/") + 1); } if (url.indexOf(href) == 0) { $(this).addClass("active"); } -}) +}); /** * btnDropdown Click Events @@ -64,9 +67,12 @@ $(window).click(function (e) { }); function downloadFile(text, filename) { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); - element.setAttribute('download', filename); + var element = document.createElement("a"); + element.setAttribute( + "href", + "data:text/plain;charset=utf-8," + encodeURIComponent(text), + ); + element.setAttribute("download", filename); element.style.display = "none"; $("body").append(element); diff --git a/webroot/js/modal.js b/webroot/js/modal.js index 8cd681ff..d7c28b3d 100644 --- a/webroot/js/modal.js +++ b/webroot/js/modal.js @@ -1,17 +1,18 @@ function openModal(title, link, message = "") { - $("span.modalTitle").html(title); - $("div.modalMessages").html(message); - $.ajax({ - url: link, success: function (result) { - $("div.modalBody").html(result); - } - }); + $("span.modalTitle").html(title); + $("div.modalMessages").html(message); + $.ajax({ + url: link, + success: function (result) { + $("div.modalBody").html(result); + }, + }); - $("div.modalWrapper").fadeIn(100); // With Animation - //$("div.modalWrapper").show(); + $("div.modalWrapper").fadeIn(100); // With Animation + //$("div.modalWrapper").show(); } $("button.btnClose").click(function () { - //$("div.modalWrapper").fadeOut(50); // With Animation - $("div.modalWrapper").hide(); + //$("div.modalWrapper").fadeOut(50); // With Animation + $("div.modalWrapper").hide(); }); diff --git a/webroot/js/sort.js b/webroot/js/sort.js index 3ab04f28..5f8b7e1e 100644 --- a/webroot/js/sort.js +++ b/webroot/js/sort.js @@ -1,73 +1,89 @@ var table = document.querySelector("table.sortable"); -table.querySelectorAll("td").forEach(function(td) { - td.addEventListener("click", function(e) { - if (td.parentElement.classList.contains("key") && td.innerHTML != "Actions") { - if (e.target.classList.contains("filter")) { - updateQueryStringParameter(window.location.href, "filter", e.target.parentElement.id); - updateFilterInput(); - } else { - var column = td.cellIndex; - var rows = Array.from(table.querySelectorAll("tr:nth-child(n+2)")); - var order = td.classList.toggle("asc") ? 1 : -1; - rows.sort(function(a, b) { - return order * (a.cells[column].textContent.trim().localeCompare(b.cells[column].textContent.trim(), undefined, { - numeric: true - })); - }); - rows.forEach(function(row) { - table.appendChild(row); - }); - var keys = document.querySelectorAll("tr.key"); - keys.forEach(function(key) { - key.querySelectorAll("td").forEach(function(td) { - td.innerHTML = td.innerHTML.replace(/ ▲| ▼/, ""); - }); - }); - var orderSymbol = order == 1 ? "▲" : "▼"; - td.innerHTML = td.innerHTML + " " + orderSymbol; - updateQueryStringParameter(window.location.href, "sort", td.id); - updateQueryStringParameter(window.location.href, "order", order == 1 ? "asc" : "desc"); - } - } - }); +table.querySelectorAll("td").forEach(function (td) { + td.addEventListener("click", function (e) { + if ( + td.parentElement.classList.contains("key") && + td.innerHTML != "Actions" + ) { + if (e.target.classList.contains("filter")) { + updateQueryStringParameter( + window.location.href, + "filter", + e.target.parentElement.id, + ); + updateFilterInput(); + } else { + var column = td.cellIndex; + var rows = Array.from(table.querySelectorAll("tr:nth-child(n+2)")); + var order = td.classList.toggle("asc") ? 1 : -1; + rows.sort(function (a, b) { + return ( + order * + a.cells[column].textContent + .trim() + .localeCompare(b.cells[column].textContent.trim(), undefined, { + numeric: true, + }) + ); + }); + rows.forEach(function (row) { + table.appendChild(row); + }); + var keys = document.querySelectorAll("tr.key"); + keys.forEach(function (key) { + key.querySelectorAll("td").forEach(function (td) { + td.innerHTML = td.innerHTML.replace(/ ▲| ▼/, ""); + }); + }); + var orderSymbol = order == 1 ? "▲" : "▼"; + td.innerHTML = td.innerHTML + " " + orderSymbol; + updateQueryStringParameter(window.location.href, "sort", td.id); + updateQueryStringParameter( + window.location.href, + "order", + order == 1 ? "asc" : "desc", + ); + } + } + }); }); function getQueryVariable(variable) { - var query = window.location.search.substring(1); - var vars = query.split("&"); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split("="); + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split("="); - if (pair[0] == variable) { - return pair[1]; - } + if (pair[0] == variable) { + return pair[1]; } - return false; + } + return false; } function updateQueryStringParameter(uri, key, value) { - let currentURL = new URL(window.location.href); - let params = currentURL.searchParams; - if (params.has(key)) { - params.delete(key); - } - params.append(key, value); - window.history.pushState("object or string", "Title", currentURL.href); + let currentURL = new URL(window.location.href); + let params = currentURL.searchParams; + if (params.has(key)) { + params.delete(key); + } + params.append(key, value); + window.history.pushState("object or string", "Title", currentURL.href); } -window.onload = function() { - var sort = getQueryVariable("sort"); - var order = getQueryVariable("order"); - if (sort) { - var sortElement = document.getElementById(sort); - if (sortElement) { - if (order == "asc") { - sortElement.click(); - } else if (order == "desc") { - sortElement.click(); - sortElement.click(); - } - } +window.onload = function () { + var sort = getQueryVariable("sort"); + var order = getQueryVariable("order"); + if (sort) { + var sortElement = document.getElementById(sort); + if (sortElement) { + if (order == "asc") { + sortElement.click(); + } else if (order == "desc") { + sortElement.click(); + sortElement.click(); + } } - filterRows(); -} + } + filterRows(); +}; diff --git a/webroot/js/tables.js b/webroot/js/tables.js index a749b43e..aea1739e 100644 --- a/webroot/js/tables.js +++ b/webroot/js/tables.js @@ -1,54 +1,53 @@ +$("tr.expandable").on("click", function (e) { + var target = $(e.target); + if (target.is("button") || target.is("a") || target.is("input")) { + return; + } + + var button = $(this).find("button.btnExpand"); + button.trigger("click"); +}); -$("tr.expandable").on('click', function(e) { - var target = $(e.target); - if (target.is("button") || target.is("a") || target.is("input")) { - return; - } +$("button.btnExpand").click(function () { + var pi_wrapper = $(this).parent(); // parent column (td) + var piRow = pi_wrapper.parent(); // parent row (tr) + var piTree = piRow.parent(); // parent table (table) + var gid = pi_wrapper.next().html(); - var button = $(this).find("button.btnExpand"); - button.trigger("click"); -}); + if ($(this).hasClass("btnExpanded")) { + // already expanded + var currentNode = piRow.nextAll().first(); -$("button.btnExpand").click(function() { - var pi_wrapper = $(this).parent(); // parent column (td) - var piRow = pi_wrapper.parent(); // parent row (tr) - var piTree = piRow.parent(); // parent table (table) - var gid = pi_wrapper.next().html(); - - if ($(this).hasClass("btnExpanded")) { - // already expanded - var currentNode = piRow.nextAll().first(); - - while (!currentNode.hasClass("expandable") && currentNode.length != 0) { - var nextNode = currentNode.nextAll().first(); - currentNode.remove(); - currentNode = nextNode; - } - - $(this).removeClass("btnExpanded"); - piRow.removeClass("expanded"); - piRow.removeClass("first"); - } else { - $("button.btnExpanded").trigger("click"); - // not expanded - $.ajax({ - url: ajax_url + gid, - success: function(result) { - console.log(result); - piRow.after(result); - } - }); - - $(this).addClass("btnExpanded"); - piRow.addClass("expanded"); - piRow.addClass("first"); + while (!currentNode.hasClass("expandable") && currentNode.length != 0) { + var nextNode = currentNode.nextAll().first(); + currentNode.remove(); + currentNode = nextNode; } + + $(this).removeClass("btnExpanded"); + piRow.removeClass("expanded"); + piRow.removeClass("first"); + } else { + $("button.btnExpanded").trigger("click"); + // not expanded + $.ajax({ + url: ajax_url + gid, + success: function (result) { + console.log(result); + piRow.after(result); + }, + }); + + $(this).addClass("btnExpanded"); + piRow.addClass("expanded"); + piRow.addClass("first"); + } }); -$('#tableSearch').keyup(function() { - var value = $(this).val().toLowerCase(); +$("#tableSearch").keyup(function () { + var value = $(this).val().toLowerCase(); - $("table.searchable tr:not(:first-child)").filter(function() { - $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1) - }); + $("table.searchable tr:not(:first-child)").filter(function () { + $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1); + }); }); diff --git a/workers/clear-audit-log.php b/workers/clear-audit-log.php index 6a28dcd4..63cfee69 100755 --- a/workers/clear-audit-log.php +++ b/workers/clear-audit-log.php @@ -1,6 +1,5 @@ #!/usr/bin/env php getConn() + ->prepare("DELETE FROM audit_log WHERE timestamp < :daysAgo") + ->execute(["daysAgo" => $daysAgo]); -$SQL->getConn()->prepare( - "DELETE FROM audit_log WHERE timestamp < :daysAgo" -)->execute(['daysAgo' => $daysAgo]); diff --git a/workers/group_user_request_owner_reminder.php b/workers/group_user_request_owner_reminder.php index 4f463262..9464712c 100755 --- a/workers/group_user_request_owner_reminder.php +++ b/workers/group_user_request_owner_reminder.php @@ -1,6 +1,5 @@ #!/usr/bin/env php 1 && $daysDifference % 7 == 0) { $new_user = $request[0]; // send email to PI - $MAILER->sendMail( - $pi_user->getMail(), - "group_user_request_owner", - array( - "group" => $pi_group->gid, - "user" => $new_user->uid, - "name" => $new_user->getFullName(), - "email" => $new_user->getMail(), - "org" => $new_user->getOrg() - ) - ); + $MAILER->sendMail($pi_user->getMail(), "group_user_request_owner", [ + "group" => $pi_group->gid, + "user" => $new_user->uid, + "name" => $new_user->getFullName(), + "email" => $new_user->getMail(), + "org" => $new_user->getOrg(), + ]); } } } + diff --git a/workers/remove-users-from-group.php b/workers/remove-users-from-group.php index b293b841..01fe8c99 100755 --- a/workers/remove-users-from-group.php +++ b/workers/remove-users-from-group.php @@ -1,6 +1,5 @@ #!/usr/bin/env php exists()) { _die("No such group '$gid'\n"); } -$handle = fopen($filename, "r") or _die("Can't open '$filename'\n"); +($handle = fopen($filename, "r")) or _die("Can't open '$filename'\n"); while (($line = fgets($handle)) !== false) { $uid = trim($line); $user = new UnityUser($uid, $LDAP, $SQL, $MAILER, $REDIS, $WEBHOOK); if (!$group->memberExists($user)) { - print("Skipping '$uid' who doesn't appear to be in '$gid'\n"); + print "Skipping '$uid' who doesn't appear to be in '$gid'\n"; continue; } $group->removeUser($user); } fclose($handle); + diff --git a/workers/update-ldap-cache.php b/workers/update-ldap-cache.php index 96bd02f0..0afc0153 100755 --- a/workers/update-ldap-cache.php +++ b/workers/update-ldap-cache.php @@ -1,6 +1,5 @@ #!/usr/bin/env php flushAll(); } -if ((!is_null($REDIS->getCache("initialized", "")) and (!array_key_exists("u", $options)))) { +if ( + !is_null($REDIS->getCache("initialized", "")) and + !array_key_exists("u", $options) +) { echo "cache is already initialized, nothing doing."; echo " use -f argument to flush cache, or -u argument to update without flush.\n"; } else { @@ -37,7 +39,11 @@ // search entire tree, some users created for admin purposes might not be in the normal OU echo "waiting for LDAP search (users)...\n"; - $users = $LDAP->search("objectClass=posixAccount", CONFIG["ldap"]["basedn"], []); + $users = $LDAP->search( + "objectClass=posixAccount", + CONFIG["ldap"]["basedn"], + [], + ); echo "response received.\n"; $user_CNs = $LDAP->getUserGroup()->getAttribute("memberuid"); sort($user_CNs); @@ -47,35 +53,57 @@ if (!in_array($uid, $user_CNs)) { continue; } - $REDIS->setCache($uid, "firstname", $user->getAttribute("givenname")[0]); + $REDIS->setCache( + $uid, + "firstname", + $user->getAttribute("givenname")[0], + ); $REDIS->setCache($uid, "lastname", $user->getAttribute("sn")[0]); $REDIS->setCache($uid, "org", $user->getAttribute("o")[0]); $REDIS->setCache($uid, "mail", $user->getAttribute("mail")[0]); $REDIS->setCache($uid, "sshkeys", $user->getAttribute("sshpublickey")); - $REDIS->setCache($uid, "loginshell", $user->getAttribute("loginshell")[0]); - $REDIS->setCache($uid, "homedir", $user->getAttribute("homedirectory")[0]); + $REDIS->setCache( + $uid, + "loginshell", + $user->getAttribute("loginshell")[0], + ); + $REDIS->setCache( + $uid, + "homedir", + $user->getAttribute("homedirectory")[0], + ); } - $org_group_ou = new LDAPEntry($LDAP->getConn(), CONFIG["ldap"]["orggroup_ou"]); + $org_group_ou = new LDAPEntry( + $LDAP->getConn(), + CONFIG["ldap"]["orggroup_ou"], + ); echo "waiting for LDAP search (org groups)...\n"; $org_groups = $org_group_ou->getChildrenArray(["cn", "memberuid"], true); echo "response received.\n"; // phpcs:disable - $org_group_CNs = array_map(function($x){return $x["cn"][0];}, $org_groups); + $org_group_CNs = array_map(function ($x) { + return $x["cn"][0]; + }, $org_groups); // phpcs:enable sort($org_group_CNs); $REDIS->setCache("sorted_orgs", "", $org_group_CNs); foreach ($org_groups as $org_group) { $gid = $org_group["cn"][0]; - $REDIS->setCache($gid, "members", ($org_group["memberuid"] ?? [])); + $REDIS->setCache($gid, "members", $org_group["memberuid"] ?? []); } - $pi_group_ou = new LDAPEntry($LDAP->getConn(), CONFIG["ldap"]["pigroup_ou"]); + $pi_group_ou = new LDAPEntry( + $LDAP->getConn(), + CONFIG["ldap"]["pigroup_ou"], + ); echo "waiting for LDAP search (pi groups)...\n"; $pi_groups = $pi_group_ou->getChildrenArray(["cn", "memberuid"], true); echo "response received.\n"; // phpcs:disable - $pi_group_CNs = array_map(function($x){return $x["cn"][0];}, $pi_groups); + $pi_group_CNs = array_map(function ($x) { + return $x["cn"][0]; + }, $pi_groups); // phpcs:enable sort($pi_group_CNs); // FIXME should be sorted_pi_groups @@ -87,7 +115,7 @@ } foreach ($pi_groups as $pi_group) { $gid = $pi_group["cn"][0]; - $members = ($pi_group["memberuid"] ?? []); + $members = $pi_group["memberuid"] ?? []; foreach ($members as $uid) { if (in_array($uid, $user_CNs)) { array_push($user_pi_group_member_of[$uid], $gid); @@ -95,7 +123,7 @@ echo "warning: group '$gid' has member '$uid' who is not in the users group!\n"; } } - $REDIS->setCache($gid, "members", ($pi_group["memberuid"] ?? [])); + $REDIS->setCache($gid, "members", $pi_group["memberuid"] ?? []); } foreach ($user_pi_group_member_of as $uid => $pi_groups) { // FIXME should be pi_groups @@ -105,3 +133,4 @@ $REDIS->setCache("initialized", "", true); echo "done!\n"; } + From 0bef3de9f90f9dc52d0839a26e97286890441443 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:30:06 -0500 Subject: [PATCH 04/13] fix php version --- CONTRIBUTING.md | 1 + composer.json | 5 +++++ composer.lock | 33 ++++++++++++++++++++------------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb6f717e..4d04e91c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,7 @@ This repository will automatically check PRs for linting compliance. ### Setting up your Environment 1. Clone this repo (including submodules): `git clone --recurse-submodules` +1. Install php v8.3 1. install [composer](https://getcomposer.org/) 1. install PHP dependencies: `composer update` 1. If you're on Windows, use [WSL](https://learn.microsoft.com/en-us/windows/wsl/) diff --git a/composer.json b/composer.json index e0e04619..a3edd9a0 100644 --- a/composer.json +++ b/composer.json @@ -7,5 +7,10 @@ "require-dev": { "phpunit/phpunit": ">=12", "robiningelbrecht/phpunit-coverage-tools": "<2" + }, + "config": { + "platform": { + "php": "8.3.6" + } } } diff --git a/composer.lock b/composer.lock index bd70b728..5d3ad790 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3cc718e763cf89866d307423ef19105d", + "content-hash": "ca6d4e77c5ab7ba1fbe152a409d86cf9", "packages": [ { "name": "paragonie/constant_time_encoding", @@ -2103,16 +2103,16 @@ }, { "name": "symfony/console", - "version": "v7.3.5", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7" + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7", - "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7", + "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", "shasum": "" }, "require": { @@ -2177,7 +2177,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.5" + "source": "https://github.com/symfony/console/tree/v7.3.6" }, "funding": [ { @@ -2197,7 +2197,7 @@ "type": "tidelift" } ], - "time": "2025-10-14T15:46:26+00:00" + "time": "2025-11-04T01:21:42+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2603,16 +2603,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -2666,7 +2666,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -2677,12 +2677,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", @@ -2832,5 +2836,8 @@ "prefer-lowest": false, "platform": {}, "platform-dev": {}, + "platform-overrides": { + "php": "8.3.6" + }, "plugin-api-version": "2.6.0" } From 284055aeb16bbf3b9455b88c7e1e0700769faf68 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:40:56 -0500 Subject: [PATCH 05/13] specify php version --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61ff708f..de9876a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,8 @@ repos: - id: prettier (php) name: prettier (php) entry: npx - args: [prettier, --plugin=@prettier/plugin-php, --write] + args: + [prettier, --plugin=@prettier/plugin-php, --php-version=8.3, --write] language: system files: \.php$ # prettier doesn't do well with the html files with Date: Fri, 7 Nov 2025 12:41:16 -0500 Subject: [PATCH 06/13] fix formatting for php8.3 --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 688f42bc..bdadb22b 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -67,7 +67,7 @@ public static function errorLog( // newlines are bad for error log, but getTrace() is too verbose $output["trace"] = explode( "\n", - new \Exception()->getTraceAsString(), + (new \Exception())->getTraceAsString(), ); } error_log("$title: " . \jsonEncode($output)); From e4970d8f3227b99c05f62558b17911676733ac2b Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:42:21 -0500 Subject: [PATCH 07/13] gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d08024d2..c776c0fc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ deployment/**/* .phpunit.result.cache +package.json +package-lock.json node_modules From 31b89bd14074f2ecd0f643db0cca41c01fa7fd30 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:48:55 -0500 Subject: [PATCH 08/13] bring back psr12 for impure PHP files --- .phpcs-ruleset.xml | 11 ++++++ .pre-commit-config.yaml | 36 +++++++++++++++++- CONTRIBUTING.md | 3 +- composer.json | 3 +- composer.lock | 81 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 .phpcs-ruleset.xml diff --git a/.phpcs-ruleset.xml b/.phpcs-ruleset.xml new file mode 100644 index 00000000..13faff04 --- /dev/null +++ b/.phpcs-ruleset.xml @@ -0,0 +1,11 @@ + + + PSR12, with max line length = 100 characters + + + + + + + + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de9876a8..77c7bb88 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,6 +24,24 @@ repos: composer\.lock| )$ + - repo: local + hooks: + - id: phpcbf + name: PHP Code Beautifier and Fixer + entry: ./vendor/bin/phpcbf + args: [--standard=./.phpcs-ruleset.xml, --colors] + language: system + files: \.php$ + # exclude pure PHP files + exclude: | + (?x)^( + node_modules/.*| + resources/lib/phpopenldaper/.*| + vendor/.*| + resources/lib/.*| + workers/.*| + )$ + - repo: local hooks: - id: prettier (php) @@ -33,7 +51,7 @@ repos: [prettier, --plugin=@prettier/plugin-php, --php-version=8.3, --write] language: system files: \.php$ - # prettier doesn't do well with the html files with =12", - "robiningelbrecht/phpunit-coverage-tools": "<2" + "robiningelbrecht/phpunit-coverage-tools": "<2", + "squizlabs/php_codesniffer": "*" }, "config": { "platform": { diff --git a/composer.lock b/composer.lock index 5d3ad790..74733f60 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ca6d4e77c5ab7ba1fbe152a409d86cf9", + "content-hash": "fa7299619e6198f9ef10dca9d729d04e", "packages": [ { "name": "paragonie/constant_time_encoding", @@ -2049,6 +2049,85 @@ ], "time": "2025-02-07T05:00:38+00:00" }, + { + "name": "squizlabs/php_codesniffer", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "06113cfdaf117fc2165f9cd040bd0f17fcd5242d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/06113cfdaf117fc2165f9cd040bd0f17fcd5242d", + "reference": "06113cfdaf117fc2165f9cd040bd0f17fcd5242d", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=7.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-09-15T11:28:58+00:00" + }, { "name": "staabm/side-effects-detector", "version": "1.0.5", From 65c441e860f3f56c4f2886e77f6cee27124f8b91 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:52:22 -0500 Subject: [PATCH 09/13] check end of file but ignore workers --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77c7bb88..1d4a5933 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,9 +10,9 @@ repos: rev: v5.0.0 hooks: - id: trailing-whitespace - # prettier should take care of this, but it has this one problem with shebang files - # https://github.com/prettier/plugin-php/issues/2430 - # - id: end-of-file-fixer + - id: end-of-file-fixer + # https://github.com/prettier/plugin-php/issues/2430 + exclude: workers/.* - repo: https://github.com/rbubley/mirrors-prettier rev: v3.4.2 From 6c503a3cd3fc2385dc209ee8ee73d48b69a63203 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 12:54:10 -0500 Subject: [PATCH 10/13] dont check test --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1d4a5933..88cdcbcc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,11 +35,12 @@ repos: # exclude pure PHP files exclude: | (?x)^( + test/.*| + workers/.*| node_modules/.*| resources/lib/phpopenldaper/.*| vendor/.*| resources/lib/.*| - workers/.*| )$ - repo: local From 00b1c63a4f170dad132f118e876f72b0b4a5514f Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 13:07:56 -0500 Subject: [PATCH 11/13] fix CI --- .github/workflows/phpunit.yml | 2 +- .github/workflows/pre-commit.yml | 4 +++- .pre-commit-config.yaml | 11 ++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 79c16afa..2ccdda08 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -18,7 +18,7 @@ jobs: php-version: "8.3" tools: composer - name: install composer dependencies - run: composer update + run: composer install --dev - name: Run docker compose uses: hoverkraft-tech/compose-action@v2.0.1 with: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 3a35b365..1618f33c 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,5 +17,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: "8.3" - tools: composer, phpcs, phpcbf + tools: composer + - name: install composer dependencies + run: composer install --dev - uses: pre-commit/action@v3.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 88cdcbcc..bc28907b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,7 +49,16 @@ repos: name: prettier (php) entry: npx args: - [prettier, --plugin=@prettier/plugin-php, --php-version=8.3, --write] + [ + -p, + prettier, + -p, + "@prettier/plugin-php", + prettier, + --plugin=@prettier/plugin-php, + --php-version=8.3, + --write, + ] language: system files: \.php$ # exclude impure PHP files From 9fdcf91da3399280bf709c171b18ed6caaecc48f Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 13:13:26 -0500 Subject: [PATCH 12/13] save node tools in package.json, package.lock --- .github/workflows/phpunit.yml | 12 +++---- .github/workflows/pre-commit.yml | 7 ++-- .gitignore | 2 -- CONTRIBUTING.md | 3 +- package-lock.json | 57 ++++++++++++++++++++++++++++++++ package.json | 6 ++++ 6 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 2ccdda08..92fa84bb 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -12,16 +12,12 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: setup PHP - uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@v2 with: php-version: "8.3" tools: composer - - name: install composer dependencies - run: composer install --dev - - name: Run docker compose - uses: hoverkraft-tech/compose-action@v2.0.1 + - run: composer install --dev + - uses: hoverkraft-tech/compose-action@v2.0.1 with: compose-file: "./tools/docker-dev/docker-compose.yml" - - name: Execute tests in the running services - run: docker compose -f ./tools/docker-dev/docker-compose.yml exec -w '/var/www/unity-web-portal' web bash -c 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover="$(mktemp --suffix=.xml)" -d --min-coverage=./coverage.php' + - run: docker compose -f ./tools/docker-dev/docker-compose.yml exec -w '/var/www/unity-web-portal' web bash -c 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover="$(mktemp --suffix=.xml)" -d --min-coverage=./coverage.php' diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 1618f33c..aa806672 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,11 +13,10 @@ jobs: with: submodules: true - uses: actions/setup-python@v3 - - name: setup PHP - uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@v2 with: php-version: "8.3" tools: composer - - name: install composer dependencies - run: composer install --dev + - run: composer install --dev + - uses: actions/setup-node@v6.0.0 - uses: pre-commit/action@v3.0.1 diff --git a/.gitignore b/.gitignore index c776c0fc..d08024d2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,4 @@ deployment/**/* .phpunit.result.cache -package.json -package-lock.json node_modules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6c055e9..d4c3e197 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,8 @@ This repository will automatically check PRs for linting compliance. 1. Install [pre-commit](https://pre-commit.com/) 1. setup pre-commit hooks: `pre-commit install` 1. Install modern implementation of grep (not macOS builtin grep): `brew install grep` -1. Install `prettier`, `prettier-plugin-php`: `npm install --dev prettier '@prettier/plugin-php'` +1. Install `npm` +1. Install nodeJS tools: `npm install` ### Environment Usage diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..5e051dcb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,57 @@ +{ + "name": "unity-web-portal", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@prettier/plugin-php": "^0.24.0", + "prettier": "^3.6.2" + } + }, + "node_modules/@prettier/plugin-php": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@prettier/plugin-php/-/plugin-php-0.24.0.tgz", + "integrity": "sha512-x9l65fCE/pgoET6RQowgdgG8Xmzs44z6j6Hhg3coINCyCw9JBGJ5ZzMR2XHAM2jmAdbJAIgqB6cUn4/3W3XLTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "linguist-languages": "^8.0.0", + "php-parser": "^3.2.5" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/linguist-languages": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-8.2.0.tgz", + "integrity": "sha512-KCUUH9x97QWYU0SXOCGxUrZR6cSfuQrMhABB7L/0I8N0LXOeaKe7+RZs7FAwvWCV2qKfZ4Wv1luLq4OfMezSJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/php-parser": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.2.5.tgz", + "integrity": "sha512-M1ZYlALFFnESbSdmRtTQrBFUHSriHgPhgqtTF/LCbZM4h7swR5PHtUceB2Kzby5CfqcsYwBn7OXTJ0+8Sajwkw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..84c53231 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "@prettier/plugin-php": "^0.24.0", + "prettier": "^3.6.2" + } +} From dbebc8a85b61d82ea46057da5753595fa7d21ba7 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Fri, 7 Nov 2025 13:15:46 -0500 Subject: [PATCH 13/13] npm install --- .github/workflows/pre-commit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index aa806672..fca4d814 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -19,4 +19,5 @@ jobs: tools: composer - run: composer install --dev - uses: actions/setup-node@v6.0.0 + - run: npm install --dev - uses: pre-commit/action@v3.0.1