From 9ae69c47fda4243055fe2e57802e6a17e87b3a63 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Fri, 24 May 2024 17:14:56 +0800 Subject: [PATCH 01/46] fix exception thrown when a user's password is about to expire This error was shown in the browser: ``` SimpleSAML\Error\Error: UNHANDLEDEXCEPTION Backtrace: 1 www/_include.php:20 (SimpleSAML_exception_handler) 0 [builtin] (N/A) Caused by: TypeError: str_replace(): Argument #2 ($replace) must be of type array|string, float given Backtrace: 7 lib/SimpleSAML/Locale/Translate.php:308 (str_replace) 6 lib/SimpleSAML/Locale/Translate.php:308 (SimpleSAML\Locale\Translate::t) 5 lib/SimpleSAML/XHTML/Template.php:914 (SimpleSAML\XHTML\Template::t) 4 modules/material/themes/material/expirychecker/about2expire.php:34 (require) 3 lib/SimpleSAML/XHTML/Template.php:560 (SimpleSAML\XHTML\Template::show) 2 modules/expirychecker/www/about2expire.php:63 (require) 1 lib/SimpleSAML/Module.php:266 (SimpleSAML\Module::process) 0 www/module.php:10 (N/A) ``` --- modules/expirychecker/lib/Auth/Process/ExpiryDate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/expirychecker/lib/Auth/Process/ExpiryDate.php b/modules/expirychecker/lib/Auth/Process/ExpiryDate.php index 0275dffb..da8edfc4 100644 --- a/modules/expirychecker/lib/Auth/Process/ExpiryDate.php +++ b/modules/expirychecker/lib/Auth/Process/ExpiryDate.php @@ -118,7 +118,7 @@ protected function getAttribute($attributeName, $state) * expire. * @return int The number of days remaining */ - protected function getDaysLeftBeforeExpiry($expiryTimestamp) + protected function getDaysLeftBeforeExpiry($expiryTimestamp): int { $now = time(); $end = $expiryTimestamp; @@ -389,7 +389,7 @@ protected function redirectToWarningPage(&$state, $accountName, $expiryTimestamp ])); $daysLeft = $this->getDaysLeftBeforeExpiry($expiryTimestamp); - $state['daysLeft'] = $daysLeft; + $state['daysLeft'] = (string)$daysLeft; if (isset($state['isPassive']) && $state['isPassive'] === true) { /* We have a passive request. Skip the warning. */ From e3730cc755effbfaa3ffafec6a621a91d7a50fc8 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Fri, 24 May 2024 17:16:19 +0800 Subject: [PATCH 02/46] fix exception thrown when a user is low on printable codes This error was shown in the browser: ``` SimpleSAML\Error\Error: UNHANDLEDEXCEPTION Backtrace: 1 www/_include.php:20 (SimpleSAML_exception_handler) 0 [builtin] (N/A) Caused by: TypeError: str_replace(): Argument #2 ($replace) must be of type array|string, int given Backtrace: 7 lib/SimpleSAML/Locale/Translate.php:308 (str_replace) 6 lib/SimpleSAML/Locale/Translate.php:308 (SimpleSAML\Locale\Translate::t) 5 lib/SimpleSAML/XHTML/Template.php:914 (SimpleSAML\XHTML\Template::t) 4 modules/material/themes/material/mfa/low-on-backup-codes.php:32 (require) 3 lib/SimpleSAML/XHTML/Template.php:560 (SimpleSAML\XHTML\Template::show) 2 modules/mfa/www/low-on-backup-codes.php:35 (require) 1 lib/SimpleSAML/Module.php:266 (SimpleSAML\Module::process) 0 www/module.php:10 (N/A) ``` --- modules/mfa/lib/Auth/Process/Mfa.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index ecf95aa8..44b475eb 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -761,7 +761,7 @@ protected static function redirectToLowOnBackupCodesNag( $numBackupCodesRemaining ) { $state['employeeId'] = $employeeId; - $state['numBackupCodesRemaining'] = $numBackupCodesRemaining; + $state['numBackupCodesRemaining'] = (string)$numBackupCodesRemaining; $stateId = State::saveState($state, self::STAGE_SENT_TO_LOW_ON_BACKUP_CODES_NAG); $url = Module::getModuleURL('mfa/low-on-backup-codes.php'); From ac6b1c2d27dc4e3ea5297d5ef65499ffb1783330 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Mon, 27 May 2024 18:30:05 +0800 Subject: [PATCH 03/46] remove the cast to int --- modules/material/themes/material/mfa/low-on-backup-codes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/material/themes/material/mfa/low-on-backup-codes.php b/modules/material/themes/material/mfa/low-on-backup-codes.php index 152207dd..b8d75d41 100644 --- a/modules/material/themes/material/mfa/low-on-backup-codes.php +++ b/modules/material/themes/material/mfa/low-on-backup-codes.php @@ -29,7 +29,7 @@

- t('{material:mfa:running_out_info}', ['{numBackupCodesRemaining}' => (int)$this->data['numBackupCodesRemaining']]) ?> + t('{material:mfa:running_out_info}', ['{numBackupCodesRemaining}' => $this->data['numBackupCodesRemaining']]) ?>

From e019a44e3ea4025d47e9504145024bd3885e8fef Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Mon, 27 May 2024 20:57:32 +0800 Subject: [PATCH 04/46] use material module for all tests --- actions-services.yml | 4 +- development/idp-local/config/authsources.php | 3 ++ development/idp3-local/config/config.php | 2 +- development/sp-local/config/config.php | 2 +- development/sp2-local/config/config.php | 2 +- development/sp3-local/config/config.php | 2 +- docker-compose.yml | 4 +- features/Sp1Idp1Sp2Idp2Sp3.feature | 4 +- features/bootstrap/ExpiryContext.php | 2 +- features/bootstrap/MfaContext.php | 28 +++++----- features/bootstrap/ProfileReviewContext.php | 54 +++++++++++++++++-- features/bootstrap/SilDiscoContext.php | 4 +- features/profilereview.feature | 30 ++++++++--- .../material/mfa/low-on-backup-codes.php | 2 +- modules/mfa/lib/Auth/Process/Mfa.php | 2 +- 15 files changed, 107 insertions(+), 38 deletions(-) diff --git a/actions-services.yml b/actions-services.yml index abcd117f..07835e8d 100644 --- a/actions-services.yml +++ b/actions-services.yml @@ -117,7 +117,7 @@ services: PROFILE_URL_FOR_TESTS: "http://pwmanager.local/module.php/core/authenticate.php?as=ssp-hub" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "default" + THEME_USE: "material:material" MYSQL_HOST: "db" MYSQL_DATABASE: "silauth" MYSQL_USER: "silauth" @@ -261,7 +261,7 @@ services: SHOW_SAML_ERRORS: "true" SAML20_IDP_ENABLE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" - THEME_USE: default + THEME_USE: "material:material" # the broker and brokerDb containers are used by the silauth module broker: diff --git a/development/idp-local/config/authsources.php b/development/idp-local/config/authsources.php index b03e4fcb..2c72033e 100644 --- a/development/idp-local/config/authsources.php +++ b/development/idp-local/config/authsources.php @@ -72,6 +72,9 @@ 'mail' => ['missing_exp@example.com'], 'employeeNumber' => ['44444'], 'cn' => ['MISSING_EXP'], + 'mfa' => [ + 'prompt' => 'no', + ], ], // expirychecker test user whose password expiry is invalid diff --git a/development/idp3-local/config/config.php b/development/idp3-local/config/config.php index 656009fa..28a93ce9 100644 --- a/development/idp3-local/config/config.php +++ b/development/idp3-local/config/config.php @@ -27,7 +27,7 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'default'); +$THEME_USE = Env::get('THEME_USE', 'material:material'); $MEMCACHE_STORE_EXPIRES = (int)(Env::get('MEMCACHE_STORE_EXPIRES', (36 * 60 * 60))); // 36 hours. $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); diff --git a/development/sp-local/config/config.php b/development/sp-local/config/config.php index 4bd8af87..742d2a61 100644 --- a/development/sp-local/config/config.php +++ b/development/sp-local/config/config.php @@ -37,7 +37,7 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'default'); +$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); diff --git a/development/sp2-local/config/config.php b/development/sp2-local/config/config.php index 3c42682a..26239025 100644 --- a/development/sp2-local/config/config.php +++ b/development/sp2-local/config/config.php @@ -37,7 +37,7 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'default'); +$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); diff --git a/development/sp3-local/config/config.php b/development/sp3-local/config/config.php index 1686e5a2..74e472af 100644 --- a/development/sp3-local/config/config.php +++ b/development/sp3-local/config/config.php @@ -37,7 +37,7 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'default'); +$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); diff --git a/docker-compose.yml b/docker-compose.yml index 267dbfa8..2b3a78a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -184,7 +184,7 @@ services: PROFILE_URL_FOR_TESTS: "http://pwmanager.local/module.php/core/authenticate.php?as=ssp-hub" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "default" + THEME_USE: "material:material" SESSION_STORE_TYPE: "sql" MYSQL_HOST: "db" MYSQL_DATABASE: "silauth" @@ -372,7 +372,7 @@ services: SHOW_SAML_ERRORS: "true" SAML20_IDP_ENABLE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" - THEME_USE: material:material + THEME_USE: "material:material" # the broker and brokerDb containers are used by the silauth module broker: diff --git a/features/Sp1Idp1Sp2Idp2Sp3.feature b/features/Sp1Idp1Sp2Idp2Sp3.feature index be765055..6498fa23 100644 --- a/features/Sp1Idp1Sp2Idp2Sp3.feature +++ b/features/Sp1Idp1Sp2Idp2Sp3.feature @@ -25,9 +25,9 @@ Feature: Ensure I can login to Sp1 through Idp1, must login to Sp2 through Idp2 Scenario: Logout of IDP1 Given I have authenticated with IDP1 for SP1 When I log out of IDP1 - Then I should see "You have been logged out." + Then I should see "You have now been logged out." Scenario: Logout of IDP2 Given I have authenticated with IDP2 for SP2 When I log out of IDP2 - Then I should see "You have been logged out." + Then I should see "You have now been logged out." diff --git a/features/bootstrap/ExpiryContext.php b/features/bootstrap/ExpiryContext.php index 749c134b..358e1767 100644 --- a/features/bootstrap/ExpiryContext.php +++ b/features/bootstrap/ExpiryContext.php @@ -152,7 +152,7 @@ public function iProvideCredentialsThatHaveNoPasswordExpirationDate() public function iShouldSeeAnErrorMessage() { $page = $this->session->getPage(); - Assert::assertContains('Unhandled exception', $page->getHtml()); + Assert::assertContains('An error occurred', $page->getHtml()); } /** diff --git a/features/bootstrap/MfaContext.php b/features/bootstrap/MfaContext.php index 16dbdaa0..d23d1592 100644 --- a/features/bootstrap/MfaContext.php +++ b/features/bootstrap/MfaContext.php @@ -150,7 +150,7 @@ public function iShouldSeeAPromptForABackupCode() { $page = $this->session->getPage(); $pageHtml = $page->getHtml(); - Assert::assertContains('

Printable Backup Code

', $pageHtml); + Assert::assertContains('Printable code', $pageHtml); Assert::assertContains('Enter code', $pageHtml); } @@ -171,7 +171,7 @@ public function iShouldSeeAPromptForATotpCode() { $page = $this->session->getPage(); $pageHtml = $page->getHtml(); - Assert::assertContains('

Smartphone App

', $pageHtml); + Assert::assertContains('Smartphone app', $pageHtml); Assert::assertContains('Enter 6-digit code', $pageHtml); } @@ -191,7 +191,7 @@ public function iProvideCredentialsThatNeedMfaAndHaveUfAvailable() public function iShouldSeeAPromptForAWebAuthn() { $page = $this->session->getPage(); - Assert::assertContains('

USB Security Key

', $page->getHtml()); + Assert::assertContains('Security key', $page->getHtml()); } protected function submitMfaValue($mfaValue) @@ -207,8 +207,10 @@ protected function submitMfaValue($mfaValue) */ public function iSubmitACorrectBackupCode() { - if (! $this->pageContainsElementWithText('h2', 'Printable Backup Code')) { - $this->clickLink('backupcode'); + if (! $this->pageContainsElementWithText('h1', 'Printable code')) { + // find image of the backup code option presented in other_mfas.php + $printableCodeOption = $this->session->getPage()->find('css', 'img[src=mfa-backupcode\002Esvg]'); + $printableCodeOption->click(); } $this->submitMfaValue(FakeIdBrokerClient::CORRECT_VALUE); } @@ -344,7 +346,7 @@ public function iShouldSeeAMessageThatIAmRunningLowOnBackupCodes() { $page = $this->session->getPage(); Assert::assertContains( - 'You are almost out of Printable Backup Codes', + 'Almost out of printable codes', $page->getHtml() ); } @@ -375,7 +377,7 @@ public function iShouldSeeAMessageThatIHaveUsedUpMyBackupCodes() { $page = $this->session->getPage(); Assert::assertContains( - 'You just used your last Printable Backup Code', + 'Last printable code used', $page->getHtml() ); } @@ -405,7 +407,7 @@ public function iShouldBeToldIOnlyHaveBackupCodesLeft($numRemaining) { $page = $this->session->getPage(); Assert::assertContains( - 'You only have ' . $numRemaining . ' remaining', + 'You only have ' . $numRemaining . ' more left', $page->getHtml() ); } @@ -417,7 +419,7 @@ public function iShouldBeGivenMoreBackupCodes() { $page = $this->session->getPage(); Assert::assertContains( - 'Here are your new Printable Backup Codes', + 'New printable codes', $page->getContent() ); } @@ -610,7 +612,7 @@ public function theUserHasAManagerEmail() public function iShouldSeeALinkToSendACodeToTheUsersManager() { $page = $this->session->getPage(); - Assert::assertContains('Can\'t use any of your 2-Step Verification options', $page->getContent()); + Assert::assertContains('I need help', $page->getContent()); } /** @@ -637,7 +639,9 @@ public function iShouldNotSeeALinkToSendACodeToTheUsersManager() */ public function iClickTheRequestAssistanceLink() { - $this->clickLink('Click here'); + // find image of the recovery contact option presented in prompt_for_mfa_manager.php + $printableCodeOption = $this->session->getPage()->find('css', 'img[src=mfa-manager\002Esvg]'); + $printableCodeOption->click(); } /** @@ -655,7 +659,7 @@ public function iShouldSeeAPromptForAManagerRescueCode() { $page = $this->session->getPage(); $pageHtml = $page->getHtml(); - Assert::assertContains('

Manager Rescue Code

', $pageHtml); + Assert::assertContains('Recovery contact help', $pageHtml); Assert::assertContains('Enter code', $pageHtml); } diff --git a/features/bootstrap/ProfileReviewContext.php b/features/bootstrap/ProfileReviewContext.php index 9c88f526..917be783 100644 --- a/features/bootstrap/ProfileReviewContext.php +++ b/features/bootstrap/ProfileReviewContext.php @@ -77,13 +77,19 @@ public function iProvideCredentialsThatAreDueForAReminder($category, $nagType) case 'method_add': $this->password = 'g'; break; - - case 'profile_review': - $this->password = 'h'; - break; } } + /** + * @Given I provide credentials that are due for a profile review + */ + public function iProvideCredentialsThatAreDueForAProfileReview() + { + // See `development/idp-local/config/authsources.php` for options. + $this->username = 'profile_review'; + $this->password = 'h'; + } + protected function pageContainsElementWithText($cssSelector, $text) { @@ -122,6 +128,14 @@ public function iClickTheUpdateProfileButton() $this->submitFormByClickingButtonNamed('update'); } + /** + * @When I click the :text link + */ + public function iClickTheLink($text) + { + $this->clickLink($text); + } + /** * @Then I should end up at the update profile URL */ @@ -137,6 +151,29 @@ public function iShouldEndUpAtTheUpdateProfileUrl() ); } + /** + * @Then I should end up at the update profile URL on a new tab + */ + public function iShouldEndUpAtTheUpdateProfileUrlOnANewTab() + { + $profileUrl = Env::get('PROFILE_URL_FOR_TESTS'); + Assert::assertNotEmpty($profileUrl, 'No PROFILE_URL_FOR_TESTS provided'); + + $windowNames = $this->session->getWindowNames(); + Assert::assertGreaterThanOrEqual(2, sizeof($windowNames), + 'Expected to see at least 2 windows opened'); + + foreach ($windowNames as $windowName) { + $this->session->switchToWindow($windowName); + $currentUrl = $this->session->getCurrentUrl(); + if ($currentUrl == $profileUrl) { + return; + } + } + + Assert::fail('Did NOT end up at the update profile URL'); + } + /** * @Then I should see the message: :message */ @@ -155,6 +192,15 @@ public function thereShouldBeAWayToGoUpdateMyProfileNow() $this->assertFormContains('name="update"', $page); } + /** + * @Then there should be a way to go review my profile now + */ + public function thereShouldBeAWayToGoReviewMyProfileNow() + { + $page = $this->session->getPage(); + Assert::assertContains('Some of these need updating', $page->getHtml()); + } + /** * @Given I provide credentials for a user that has used the manager mfa option */ diff --git a/features/bootstrap/SilDiscoContext.php b/features/bootstrap/SilDiscoContext.php index 24781840..b277f256 100644 --- a/features/bootstrap/SilDiscoContext.php +++ b/features/bootstrap/SilDiscoContext.php @@ -86,7 +86,7 @@ public function iLogOutOfIdp1() $this->iGoToTheSpLoginPage('SP3'); $this->iClickOnTheTile('IDP 1'); $this->clickLink('Logout'); - $this->assertPageContainsText('You have been logged out.'); + $this->assertPageContainsText('You have now been logged out.'); } /** @@ -96,7 +96,7 @@ public function iLogOutOfIdp2() { $this->iGoToTheSpLoginPage('SP2'); $this->clickLink('Logout'); - $this->assertPageContainsText('You have been logged out.'); + $this->assertPageContainsText('You have now been logged out.'); } /** diff --git a/features/profilereview.feature b/features/profilereview.feature index 9c3988d1..be4f84e3 100644 --- a/features/profilereview.feature +++ b/features/profilereview.feature @@ -16,10 +16,16 @@ Feature: Prompt to review profile information And there should be a way to continue to my intended destination Examples: - | category | nag type | message | - | mfa | add | "2-Step Verification" | - | method | add | "alternate email addresses" | - | profile | review | "Please take a moment to review" | + | category | nag type | message | + | mfa | add | "2-Step Verification" | + | method | add | "alternate email address" | + + Scenario: Present profile review as required by the user profile + Given I provide credentials that are due for a profile review + When I log in + Then I should see the message: "Are these still correct?" + And there should be a way to go review my profile now + And there should be a way to continue to my intended destination Scenario Outline: Obeying a reminder Given I provide credentials that are due for a reminder @@ -31,7 +37,12 @@ Feature: Prompt to review profile information | category | nag type | | mfa | add | | method | add | - | profile | review | + + Scenario: Obeying a profile review reminder + Given I provide credentials that are due for a profile review + And I have logged in + When I click the "Some of these need updating" link + Then I should end up at the update profile URL on a new tab Scenario Outline: Ignoring a reminder Given I provide credentials that are due for a reminder @@ -43,10 +54,15 @@ Feature: Prompt to review profile information | category | nag type | | mfa | add | | method | add | - | profile | review | + + Scenario: Ignoring a profile review reminder + Given I provide credentials that are due for a profile review + And I have logged in + When I click the remind-me-later button + Then I should end up at my intended destination Scenario: Ensuring that manager mfa data is not displayed to the user Given I provide credentials for a user that has used the manager mfa option And I have logged in - Then I should see the message: "Please take a moment to review" + Then I should see the message: "Are these still correct?" And I should not see any manager mfa information diff --git a/modules/material/themes/material/mfa/low-on-backup-codes.php b/modules/material/themes/material/mfa/low-on-backup-codes.php index 152207dd..b8d75d41 100644 --- a/modules/material/themes/material/mfa/low-on-backup-codes.php +++ b/modules/material/themes/material/mfa/low-on-backup-codes.php @@ -29,7 +29,7 @@

- t('{material:mfa:running_out_info}', ['{numBackupCodesRemaining}' => (int)$this->data['numBackupCodesRemaining']]) ?> + t('{material:mfa:running_out_info}', ['{numBackupCodesRemaining}' => $this->data['numBackupCodesRemaining']]) ?>

diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index ecf95aa8..44b475eb 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -761,7 +761,7 @@ protected static function redirectToLowOnBackupCodesNag( $numBackupCodesRemaining ) { $state['employeeId'] = $employeeId; - $state['numBackupCodesRemaining'] = $numBackupCodesRemaining; + $state['numBackupCodesRemaining'] = (string)$numBackupCodesRemaining; $stateId = State::saveState($state, self::STAGE_SENT_TO_LOW_ON_BACKUP_CODES_NAG); $url = Module::getModuleURL('mfa/low-on-backup-codes.php'); From 58518071e42e6267eb4c99312d14aee35d874b4e Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Mon, 27 May 2024 22:05:32 +0800 Subject: [PATCH 05/46] ensure daysLeft is int --- modules/material/themes/material/expirychecker/about2expire.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/material/themes/material/expirychecker/about2expire.php b/modules/material/themes/material/expirychecker/about2expire.php index 59e8f0fe..a8f1cccc 100644 --- a/modules/material/themes/material/expirychecker/about2expire.php +++ b/modules/material/themes/material/expirychecker/about2expire.php @@ -27,7 +27,7 @@

data['daysLeft'] ?? 0; + $daysLeft = $this->data['daysLeft'] ?? '0'; $expiringMessage = $daysLeft < 2 ? $this->t('{material:about2expire:expiring_in_a_day}') : $this->t('{material:about2expire:expiring_soon}', From 80551cae88d65ddd277808c2d02ceec32ad21f35 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 28 May 2024 09:31:43 +0800 Subject: [PATCH 06/46] remove THEME_USE variable --- README.md | 2 +- actions-services.yml | 4 ---- development/idp-local/config/config.php | 3 +-- development/idp2-local/config/config.php | 3 +-- development/idp3-local/config/config.php | 3 +-- development/sp-local/config/config.php | 3 +-- development/sp2-local/config/config.php | 3 +-- development/sp3-local/config/config.php | 3 +-- docker-compose.yml | 4 ---- dockerbuild/config/config.php | 3 +-- local.env.dist | 1 - 11 files changed, 8 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 922fd98d..5e554c5f 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ Update `/simplesamlphp/config/config.php`: 'theme.use' => 'material:material' ``` -This project provides a convenience by loading this config with whatever is in the environment variable `THEME_USE`._ +This project sets this as the default value in the provided config file. ##### Google reCAPTCHA diff --git a/actions-services.yml b/actions-services.yml index 07835e8d..a8afc811 100644 --- a/actions-services.yml +++ b/actions-services.yml @@ -70,7 +70,6 @@ services: SECURE_COOKIE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" THEME_COLOR_SCHEME: "orange-light_blue" HUB_MODE: "true" @@ -117,7 +116,6 @@ services: PROFILE_URL_FOR_TESTS: "http://pwmanager.local/module.php/core/authenticate.php?as=ssp-hub" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" MYSQL_HOST: "db" MYSQL_DATABASE: "silauth" MYSQL_USER: "silauth" @@ -148,7 +146,6 @@ services: IDP_NAME: "IDP 2" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" ssp-idp3.local: build: . @@ -261,7 +258,6 @@ services: SHOW_SAML_ERRORS: "true" SAML20_IDP_ENABLE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" - THEME_USE: "material:material" # the broker and brokerDb containers are used by the silauth module broker: diff --git a/development/idp-local/config/config.php b/development/idp-local/config/config.php index db650c58..cb912a0d 100644 --- a/development/idp-local/config/config.php +++ b/development/idp-local/config/config.php @@ -51,7 +51,6 @@ $ENABLE_DEBUG = Env::get('ENABLE_DEBUG', false); $LOGGING_LEVEL = Env::get('LOGGING_LEVEL', 'NOTICE'); $LOGGING_HANDLER = Env::get('LOGGING_HANDLER', 'stderr'); -$THEME_USE = Env::get('THEME_USE', 'material:material'); // Options: https://github.com/silinternational/simplesamlphp-module-material/blob/develop/README.md#branding $THEME_COLOR_SCHEME = Env::get('THEME_COLOR_SCHEME', null); @@ -964,7 +963,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* * Set this option to the text you would like to appear at the header of each page. Set to false if you don't want diff --git a/development/idp2-local/config/config.php b/development/idp2-local/config/config.php index db650c58..cb912a0d 100644 --- a/development/idp2-local/config/config.php +++ b/development/idp2-local/config/config.php @@ -51,7 +51,6 @@ $ENABLE_DEBUG = Env::get('ENABLE_DEBUG', false); $LOGGING_LEVEL = Env::get('LOGGING_LEVEL', 'NOTICE'); $LOGGING_HANDLER = Env::get('LOGGING_HANDLER', 'stderr'); -$THEME_USE = Env::get('THEME_USE', 'material:material'); // Options: https://github.com/silinternational/simplesamlphp-module-material/blob/develop/README.md#branding $THEME_COLOR_SCHEME = Env::get('THEME_COLOR_SCHEME', null); @@ -964,7 +963,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* * Set this option to the text you would like to appear at the header of each page. Set to false if you don't want diff --git a/development/idp3-local/config/config.php b/development/idp3-local/config/config.php index 28a93ce9..6673268b 100644 --- a/development/idp3-local/config/config.php +++ b/development/idp3-local/config/config.php @@ -27,7 +27,6 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'material:material'); $MEMCACHE_STORE_EXPIRES = (int)(Env::get('MEMCACHE_STORE_EXPIRES', (36 * 60 * 60))); // 36 hours. $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); @@ -445,7 +444,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* diff --git a/development/sp-local/config/config.php b/development/sp-local/config/config.php index 742d2a61..2608cec5 100644 --- a/development/sp-local/config/config.php +++ b/development/sp-local/config/config.php @@ -37,7 +37,6 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); @@ -486,7 +485,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* diff --git a/development/sp2-local/config/config.php b/development/sp2-local/config/config.php index 26239025..f1b6c903 100644 --- a/development/sp2-local/config/config.php +++ b/development/sp2-local/config/config.php @@ -37,7 +37,6 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); @@ -486,7 +485,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* diff --git a/development/sp3-local/config/config.php b/development/sp3-local/config/config.php index 74e472af..df03c377 100644 --- a/development/sp3-local/config/config.php +++ b/development/sp3-local/config/config.php @@ -37,7 +37,6 @@ $SESSION_COOKIE_LIFETIME = (int)(Env::get('SESSION_COOKIE_LIFETIME', 0)); $SESSION_REMEMBERME_LIFETIME = (int)(Env::get('SESSION_REMEMBERME_LIFETIME', (14 * 86400))); // 14 days $SECURE_COOKIE = Env::get('SECURE_COOKIE', true); -$THEME_USE = Env::get('THEME_USE', 'material:material'); $SAML20_IDP_ENABLE = Env::get('SAML20_IDP_ENABLE', true); $GOOGLE_ENABLE = Env::get('GOOGLE_ENABLE', false); @@ -486,7 +485,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* diff --git a/docker-compose.yml b/docker-compose.yml index 2b3a78a6..5526b028 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -127,7 +127,6 @@ services: SECURE_COOKIE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" THEME_COLOR_SCHEME: "orange-light_blue" HUB_MODE: "true" @@ -184,7 +183,6 @@ services: PROFILE_URL_FOR_TESTS: "http://pwmanager.local/module.php/core/authenticate.php?as=ssp-hub" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" SESSION_STORE_TYPE: "sql" MYSQL_HOST: "db" MYSQL_DATABASE: "silauth" @@ -225,7 +223,6 @@ services: IDP_NAME: "IDP 2" SECURE_COOKIE: "false" SHOW_SAML_ERRORS: "true" - THEME_USE: "material:material" ssp-idp3.local: build: . @@ -372,7 +369,6 @@ services: SHOW_SAML_ERRORS: "true" SAML20_IDP_ENABLE: "false" ADMIN_PROTECT_INDEX_PAGE: "false" - THEME_USE: "material:material" # the broker and brokerDb containers are used by the silauth module broker: diff --git a/dockerbuild/config/config.php b/dockerbuild/config/config.php index 7f0280c6..6d3d347e 100644 --- a/dockerbuild/config/config.php +++ b/dockerbuild/config/config.php @@ -51,7 +51,6 @@ $ENABLE_DEBUG = Env::get('ENABLE_DEBUG', false); $LOGGING_LEVEL = Env::get('LOGGING_LEVEL', 'NOTICE'); $LOGGING_HANDLER = Env::get('LOGGING_HANDLER', 'stderr'); -$THEME_USE = Env::get('THEME_USE', 'material:material'); // Options: https://github.com/silinternational/simplesamlphp-module-material/blob/develop/README.md#branding $THEME_COLOR_SCHEME = Env::get('THEME_COLOR_SCHEME', null); @@ -963,7 +962,7 @@ /* * Which theme directory should be used? */ - 'theme.use' => $THEME_USE, + 'theme.use' => 'material:material', /* * Set this option to the text you would like to appear at the header of each page. Set to false if you don't want diff --git a/local.env.dist b/local.env.dist index 9f8fd47b..f037ef32 100644 --- a/local.env.dist +++ b/local.env.dist @@ -56,7 +56,6 @@ HELP_CENTER_URL= SAML20_IDP_ENABLE= SECURE_COOKIE= SHOW_SAML_ERRORS= -THEME_USE= TIMEZONE= XDEBUG_REMOTE_HOST= From b9a3c4c5e33e1ac755339d89ada9d4fe403febcd Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 28 May 2024 09:41:45 +0800 Subject: [PATCH 07/46] remove templates not used by material theme --- .../expirychecker/templates/about2expire.php | 48 ----- modules/expirychecker/templates/expired.php | 38 ---- .../material/mfa/prompt-for-mfa-u2f.php | 175 ------------------ modules/mfa/templates/low-on-backup-codes.php | 21 --- modules/mfa/templates/must-set-up-mfa.php | 16 -- modules/mfa/templates/new-backup-codes.php | 40 ---- modules/mfa/templates/out-of-backup-codes.php | 37 ---- .../templates/prompt-for-mfa-backupcode.php | 57 ------ .../mfa/templates/prompt-for-mfa-manager.php | 49 ----- modules/mfa/templates/prompt-for-mfa-totp.php | 53 ------ .../mfa/templates/prompt-for-mfa-webauthn.php | 79 -------- modules/mfa/templates/send-manager-mfa.php | 20 -- .../templates/nag-for-method.php | 21 --- .../profilereview/templates/nag-for-mfa.php | 28 --- modules/profilereview/templates/review.php | 62 ------- 15 files changed, 744 deletions(-) delete mode 100644 modules/expirychecker/templates/about2expire.php delete mode 100644 modules/expirychecker/templates/expired.php delete mode 100644 modules/material/themes/material/mfa/prompt-for-mfa-u2f.php delete mode 100644 modules/mfa/templates/low-on-backup-codes.php delete mode 100644 modules/mfa/templates/must-set-up-mfa.php delete mode 100644 modules/mfa/templates/new-backup-codes.php delete mode 100644 modules/mfa/templates/out-of-backup-codes.php delete mode 100644 modules/mfa/templates/prompt-for-mfa-backupcode.php delete mode 100644 modules/mfa/templates/prompt-for-mfa-manager.php delete mode 100644 modules/mfa/templates/prompt-for-mfa-totp.php delete mode 100644 modules/mfa/templates/prompt-for-mfa-webauthn.php delete mode 100644 modules/mfa/templates/send-manager-mfa.php delete mode 100644 modules/profilereview/templates/nag-for-method.php delete mode 100644 modules/profilereview/templates/nag-for-mfa.php delete mode 100644 modules/profilereview/templates/review.php diff --git a/modules/expirychecker/templates/about2expire.php b/modules/expirychecker/templates/about2expire.php deleted file mode 100644 index 468faeaf..00000000 --- a/modules/expirychecker/templates/about2expire.php +++ /dev/null @@ -1,48 +0,0 @@ -data['header'] = sprintf( - 'Your password will expire in %s %s', - $this->data['daysLeft'], - $this->data['dayOrDays'] -); -$this->data['autofocus'] = 'yesbutton'; - -$this->includeAtTemplateBase('includes/header.php'); - -$dateString = msgfmt_format_message( - $this->getLanguage(), - '{0,date,long}', - [$this->data['expiresAtTimestamp']] -); - -?> -

- The password for your data['accountName']); ?> - account will expire on . -

-

- Would you like to update your password now? -

- -
- - data['formData'] as $name => $value): ?> - - - - - - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/expirychecker/templates/expired.php b/modules/expirychecker/templates/expired.php deleted file mode 100644 index 8b46d306..00000000 --- a/modules/expirychecker/templates/expired.php +++ /dev/null @@ -1,38 +0,0 @@ -data['header'] = 'Your password has expired'; - -$this->includeAtTemplateBase('includes/header.php'); - -$dateString = msgfmt_format_message( - $this->getLanguage(), - '{0,date,long}', - [$this->data['expiresAtTimestamp']] -); - -?> -

- The password for your data['accountName']); ?> - account expired on . -

-

- You will need to update your password before you can continue to where you - were going. -

-

-

- - data['formData'] as $name => $value): ?> - - - - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/material/themes/material/mfa/prompt-for-mfa-u2f.php b/modules/material/themes/material/mfa/prompt-for-mfa-u2f.php deleted file mode 100644 index bc867991..00000000 --- a/modules/material/themes/material/mfa/prompt-for-mfa-u2f.php +++ /dev/null @@ -1,175 +0,0 @@ - - - - <?= $this->t('{material:mfa:title}') ?> - - - - - - - - -data['supportsU2f']; ?> - - -
-
-
- - t('{material:mfa:header}') ?> - -
-
- -
-
-
-
- <?= $this->t('{material:mfa:u2f_icon}') ?> -
- -
-

- t('{material:mfa:u2f_header}') ?> -

-
- - -
-

- t('{material:mfa:u2f_instructions}') ?> -

-
- -
-

- t('{material:mfa:u2f_unsupported}') ?> -

-
- - - data['errorMessage']; - if (! empty($message)) { - ?> - - -
-

- error - - - - -

-
- -
- - - - -
- - -
- -
- -
-
-
-
- - diff --git a/modules/mfa/templates/low-on-backup-codes.php b/modules/mfa/templates/low-on-backup-codes.php deleted file mode 100644 index bf1f7a37..00000000 --- a/modules/mfa/templates/low-on-backup-codes.php +++ /dev/null @@ -1,21 +0,0 @@ -data['header'] = 'Almost out of Printable Backup Codes'; -$this->includeAtTemplateBase('includes/header.php'); - -$numBackupCodesRemaining = $this->data['numBackupCodesRemaining']; -?> -

- You are almost out of Printable Backup Codes. - You only have remaining. -

-
- - - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/must-set-up-mfa.php b/modules/mfa/templates/must-set-up-mfa.php deleted file mode 100644 index dbb5c4bd..00000000 --- a/modules/mfa/templates/must-set-up-mfa.php +++ /dev/null @@ -1,16 +0,0 @@ -data['header'] = 'Set up 2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); - -?> -

- Your account requires additional security. - You must set up 2-step verification at this time. -

-
- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/new-backup-codes.php b/modules/mfa/templates/new-backup-codes.php deleted file mode 100644 index 5624a681..00000000 --- a/modules/mfa/templates/new-backup-codes.php +++ /dev/null @@ -1,40 +0,0 @@ -data['header'] = 'New Printable Backup Codes'; -$this->includeAtTemplateBase('includes/header.php'); - -$newBackupCodes = $this->data['newBackupCodes']; -$mfaSetupUrl = $this->data['mfaSetupUrl']; -?> - - -

- Something went wrong while we were trying to get more Printable Backup Codes for you. -

-

- We are sorry for the inconvenience. After you finish logging in, please - check your 2-Step Verification methods here:
- -

- -

- Here are your new Printable Backup Codes. Remember to keep them - secret (like a password) and store them somewhere safe. -

-

-

- Once you have stored them somewhere safe, you are welcome to click the - button below to continue to where you were going. -

- - -
- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/out-of-backup-codes.php b/modules/mfa/templates/out-of-backup-codes.php deleted file mode 100644 index 27005578..00000000 --- a/modules/mfa/templates/out-of-backup-codes.php +++ /dev/null @@ -1,37 +0,0 @@ -data['header'] = 'Last Printable Backup Code'; -$this->includeAtTemplateBase('includes/header.php'); - -$hasOtherMfaOptions = $this->data['hasOtherMfaOptions']; -?> -

- You just used your last Printable Backup Code. -

- - -

- We recommend you get more now so that you will have some next time we ask - you for one. Otherwise, you will need to use a different option (such as a - Security Key or Smartphone App) the next time we ask you for 2-Step Verification. -

- -

- Since you do not have any other 2-Step Verification options set up yet, - you need to get more Printable Backup Codes now so that you will have some - next time we ask you for one. -

- - -
- - - - - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/prompt-for-mfa-backupcode.php b/modules/mfa/templates/prompt-for-mfa-backupcode.php deleted file mode 100644 index 3fb79ef5..00000000 --- a/modules/mfa/templates/prompt-for-mfa-backupcode.php +++ /dev/null @@ -1,57 +0,0 @@ -data['header'] = '2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); - -if (! empty($this->data['errorMessage'])) { - ?> -

- Oops!
- data['errorMessage']); ?> -

- -
-

Printable Backup Code

-

- Each code can only be used once, so the code you enter this time will be - used up and will not be available again. -

-

- Enter code: -
- - -
- -

- data['mfaOptions']) > 1): ?> -

- Don't have your printable backup codes handy? You may also use: -

-
    - data['mfaOptions'] as $mfaOpt) { - if ($mfaOpt['type'] != 'backupcode') { - ?> -
  • - -
- - data['managerEmail'])): ?> -

- Can't use any of your 2-Step Verification options? - - Click here for assistance. -

- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/prompt-for-mfa-manager.php b/modules/mfa/templates/prompt-for-mfa-manager.php deleted file mode 100644 index 643f9631..00000000 --- a/modules/mfa/templates/prompt-for-mfa-manager.php +++ /dev/null @@ -1,49 +0,0 @@ -data['header'] = '2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); - -if (! empty($this->data['errorMessage'])) { - ?> -

- Oops!
- data['errorMessage']); ?> -

- -
-

Manager Rescue Code

-

- When you receive your code from your manager, enter it here. -

-

- Enter code: -
- - -
- -

- data['mfaOptions']) > 1): ?> -

- You may also use: -

-
    - data['mfaOptions'] as $mfaOpt) { - if ($mfaOpt['type'] != 'manager') { - ?> -
  • - -
- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/prompt-for-mfa-totp.php b/modules/mfa/templates/prompt-for-mfa-totp.php deleted file mode 100644 index 3603c8e9..00000000 --- a/modules/mfa/templates/prompt-for-mfa-totp.php +++ /dev/null @@ -1,53 +0,0 @@ -data['header'] = '2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); - -if (! empty($this->data['errorMessage'])) { - ?> -

- Oops!
- data['errorMessage']); ?> -

- -
-

Smartphone App

-

- Enter 6-digit code: -
- - -
- -

- data['mfaOptions']) > 1): ?> -

- Don't have your smartphone app handy? You may also use: -

-
    - data['mfaOptions'] as $mfaOpt) { - if ($mfaOpt['type'] != 'totp') { - ?> -
  • - -
- - data['managerEmail'])): ?> -

- Can't use any of your 2-Step Verification options? - - Click here for assistance. -

- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/prompt-for-mfa-webauthn.php b/modules/mfa/templates/prompt-for-mfa-webauthn.php deleted file mode 100644 index 12478937..00000000 --- a/modules/mfa/templates/prompt-for-mfa-webauthn.php +++ /dev/null @@ -1,79 +0,0 @@ -data['header'] = '2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); -?> -data['supportsWebAuthn']): ?> - - - - -
-

USB Security Key

- data['supportsWebAuthn']): ?> -

Please insert your security key and press its button.

-

- - -
- - - -

- -

- USB Security Keys are not supported in your current browser. - Please consider a more secure browser like - Google Chrome. -

- - - data['mfaOptions']) > 1): ?> -

- Don't have your security key handy? You may also use: -

-
    - data['mfaOptions'] as $mfaOpt) { - if ($mfaOpt['type'] != 'webauthn') { - ?> -
  • - -
- - data['managerEmail'])): ?> -

- Can't use any of your 2-Step Verification options? - - Click here for assistance. -

- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/mfa/templates/send-manager-mfa.php b/modules/mfa/templates/send-manager-mfa.php deleted file mode 100644 index 4a274ac6..00000000 --- a/modules/mfa/templates/send-manager-mfa.php +++ /dev/null @@ -1,20 +0,0 @@ -data['header'] = 'Send manager backup code'; -$this->includeAtTemplateBase('includes/header.php'); - -?> -

- You can send a backup code to your manager to serve as an - additional 2-Step Verification option. - The email address on file (masked for privacy) is data['managerEmail'] ?> -

-
- - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/profilereview/templates/nag-for-method.php b/modules/profilereview/templates/nag-for-method.php deleted file mode 100644 index bd1c66ef..00000000 --- a/modules/profilereview/templates/nag-for-method.php +++ /dev/null @@ -1,21 +0,0 @@ -data['header'] = 'Set up Recovery Methods'; -$this->includeAtTemplateBase('includes/header.php'); -?> -

- Did you know you can provide alternate email addresses for password recovery? -

-

- We highly encourage you to do this to ensure continuous access and improved security. -

-
- - - -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/profilereview/templates/nag-for-mfa.php b/modules/profilereview/templates/nag-for-mfa.php deleted file mode 100644 index 1e31541e..00000000 --- a/modules/profilereview/templates/nag-for-mfa.php +++ /dev/null @@ -1,28 +0,0 @@ -data['header'] = 'Set up 2-Step Verification'; -$this->includeAtTemplateBase('includes/header.php'); - -$mfaLearnMoreUrl = $this->data['mfaLearnMoreUrl']; -?> -

- Did you know you could greatly increase the security of your account by enabling 2-Step Verification? -

-

- We highly encourage you to do this for your own safety. -

-
- - - - - -

Learn more

- -
-includeAtTemplateBase('includes/footer.php'); diff --git a/modules/profilereview/templates/review.php b/modules/profilereview/templates/review.php deleted file mode 100644 index 365d908d..00000000 --- a/modules/profilereview/templates/review.php +++ /dev/null @@ -1,62 +0,0 @@ -data['header'] = 'Review 2-Step Verification and Password Recovery'; -$this->includeAtTemplateBase('includes/header.php'); - -$profileUrl = $this->data['profileUrl']; - -?> -

- Please take a moment to review your 2-Step Verification options and - Password Recovery Methods. -

-

- We highly encourage you to do this for your own safety. -

-

2-Step Verification

- - - - - - - - data['mfaOptions'] as $option): ?> - - - - - - - -
LabelTypeCreatedLast Used
-

Password Recovery Methods

- - - - - - - data['methodOptions'] as $option): ?> - - - - - - -
EmailVerifiedCreated
-
- - - - - -

Go to Profile

- -
-includeAtTemplateBase('includes/footer.php'); From 76f4b749f3393bd040d3858cbc8a79881d1ba582 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 28 May 2024 09:46:08 +0800 Subject: [PATCH 08/46] copy material theme templates to other modules --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index a68fb93e..d55ec50f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,11 @@ ENV SSP_PATH /data/vendor/simplesamlphp/simplesamlphp # Copy modules into simplesamlphp COPY modules/ $SSP_PATH/modules +# Copy material theme templates to other modules, just in case the "default" theme is selected +COPY modules/material/themes/material/expirychecker/* $SSP_PATH/modules/expirychecker/templates/ +COPY modules/material/themes/material/mfa/* $SSP_PATH/modules/mfa/templates/ +COPY modules/material/themes/material/profilereview/* $SSP_PATH/modules/profilereview/templates/ + # Copy in SSP override files RUN mv $SSP_PATH/www/index.php $SSP_PATH/www/ssp-index.php COPY dockerbuild/ssp-overrides/index.php $SSP_PATH/www/index.php From 2e239d32e4a730225789032f62d5b0e283450f38 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 28 May 2024 13:12:19 +0800 Subject: [PATCH 09/46] generate browser.js path in the controller, not the template --- Makefile | 4 ++-- .../material/mfa/prompt-for-mfa-webauthn.php | 6 +----- .../material/www/simplewebauthn/LICENSE.md | 21 ------------------- .../material/www/simplewebauthn/browser.js | 2 -- modules/mfa/www/prompt-for-mfa.php | 2 ++ package-lock.json | 18 ++++++++++++++++ 6 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 modules/material/www/simplewebauthn/LICENSE.md delete mode 100644 modules/material/www/simplewebauthn/browser.js create mode 100644 package-lock.json diff --git a/Makefile b/Makefile index 6fd4442f..92e85ba8 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ test-integration: docker-compose run --rm test ./run-integration-tests.sh copyJsLib: - cp ./node_modules/@simplewebauthn/browser/dist/bundle/index.umd.min.js ./modules/material/www/simplewebauthn/browser.js - cp ./node_modules/@simplewebauthn/browser/LICENSE.md ./www/simplewebauthn/LICENSE.md + cp ./node_modules/@simplewebauthn/browser/dist/bundle/index.umd.min.js ./modules/mfa/www/simplewebauthn/browser.js + cp ./node_modules/@simplewebauthn/browser/LICENSE.md ./modules/mfa/www/simplewebauthn/LICENSE.md deps: docker-compose run --rm node npm install --ignore-scripts diff --git a/modules/material/themes/material/mfa/prompt-for-mfa-webauthn.php b/modules/material/themes/material/mfa/prompt-for-mfa-webauthn.php index 7f8ccbe2..02fa6ff4 100644 --- a/modules/material/themes/material/mfa/prompt-for-mfa-webauthn.php +++ b/modules/material/themes/material/mfa/prompt-for-mfa-webauthn.php @@ -5,11 +5,7 @@ - - + data['recaptcha.siteKey'] ?? null); + $siteKey = htmlentities($this->data['recaptcha.siteKey']); if (! empty($siteKey)) { ?> diff --git a/modules/silauth/www/loginuserpass.php b/modules/silauth/www/loginuserpass.php index 34ec0388..dcddce3a 100644 --- a/modules/silauth/www/loginuserpass.php +++ b/modules/silauth/www/loginuserpass.php @@ -98,6 +98,8 @@ $request = new Request(); if (Authenticator::isCaptchaRequired($username, $request->getUntrustedIpAddresses())) { $t->data['recaptcha.siteKey'] = $recaptchaSiteKey; +} else { + $t->data['recaptcha.siteKey'] = null; } if (isset($state['SPMetadata'])) { From de7c619aad0a96144a322e18ef6975d09cd7f5dc Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 28 May 2024 20:30:20 +0800 Subject: [PATCH 14/46] add announcement.php to actions-services.yml --- actions-services.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions-services.yml b/actions-services.yml index a8afc811..0d053e30 100644 --- a/actions-services.yml +++ b/actions-services.yml @@ -53,6 +53,7 @@ services: # Utilize custom configs - ./development/hub/config/authsources.php:/data/vendor/simplesamlphp/simplesamlphp/config/authsources.php + - ./development/announcement.php:/data/vendor/simplesamlphp/simplesamlphp/announcement/announcement.php # Utilize custom metadata - ./development/hub/metadata/idp-remote.php:/data/vendor/simplesamlphp/simplesamlphp/metadata/idp-remote.php @@ -84,6 +85,7 @@ services: # Utilize custom configs - ./development/idp-local/config/authsources.php:/data/vendor/simplesamlphp/simplesamlphp/config/authsources.php - ./development/idp-local/config/config.php:/data/vendor/simplesamlphp/simplesamlphp/config/config.php + - ./development/announcement.php:/data/vendor/simplesamlphp/simplesamlphp/announcement/announcement.php # Utilize custom metadata - ./development/idp-local/metadata/saml20-idp-hosted.php:/data/vendor/simplesamlphp/simplesamlphp/metadata/saml20-idp-hosted.php From be25acad767401aff3dd53dc886bd515ad022cf3 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 29 May 2024 18:49:55 +0800 Subject: [PATCH 15/46] update match strings in mfa tests --- features/bootstrap/MfaContext.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/bootstrap/MfaContext.php b/features/bootstrap/MfaContext.php index d23d1592..988f1fb6 100644 --- a/features/bootstrap/MfaContext.php +++ b/features/bootstrap/MfaContext.php @@ -171,7 +171,7 @@ public function iShouldSeeAPromptForATotpCode() { $page = $this->session->getPage(); $pageHtml = $page->getHtml(); - Assert::assertContains('Smartphone app', $pageHtml); + Assert::assertContains('Authenticator app', $pageHtml); Assert::assertContains('Enter 6-digit code', $pageHtml); } @@ -659,7 +659,7 @@ public function iShouldSeeAPromptForAManagerRescueCode() { $page = $this->session->getPage(); $pageHtml = $page->getHtml(); - Assert::assertContains('Recovery contact help', $pageHtml); + Assert::assertContains('Ask Your Recovery Contact for Help', $pageHtml); Assert::assertContains('Enter code', $pageHtml); } From 2e490851604b409ae0f8d5e30bfd64cfb7bcbaad Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Thu, 30 May 2024 08:28:28 +0800 Subject: [PATCH 16/46] insert blank line after opening php tag [skip ci] --- modules/material/themes/material/common-announcement.php | 1 + modules/material/themes/material/mfa/other_mfas.php | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/material/themes/material/common-announcement.php b/modules/material/themes/material/common-announcement.php index 0486f597..0fe9dcef 100644 --- a/modules/material/themes/material/common-announcement.php +++ b/modules/material/themes/material/common-announcement.php @@ -1,4 +1,5 @@ data['announcement'])) { ?>
diff --git a/modules/material/themes/material/mfa/other_mfas.php b/modules/material/themes/material/mfa/other_mfas.php index 8359a126..7d466a59 100644 --- a/modules/material/themes/material/mfa/other_mfas.php +++ b/modules/material/themes/material/mfa/other_mfas.php @@ -1,4 +1,5 @@ data['otherOptions']; if (count($otherOptions) > 0) { ?> From 24fd66b7785cb7f13ddd65fbde8c12a57eebb13f Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Thu, 30 May 2024 10:54:14 +0800 Subject: [PATCH 17/46] clarify comment about the purpose of authsources.php [skip ci] --- features/bootstrap/ProfileReviewContext.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/bootstrap/ProfileReviewContext.php b/features/bootstrap/ProfileReviewContext.php index 917be783..5312b09e 100644 --- a/features/bootstrap/ProfileReviewContext.php +++ b/features/bootstrap/ProfileReviewContext.php @@ -57,7 +57,7 @@ protected function submitFormByClickingButtonNamed($buttonName) */ public function iProvideCredentialsThatDoNotNeedReview() { - // See `development/idp-local/config/authsources.php` for options. + // Credentials defined in `development/idp-local/config/authsources.php` $this->username = 'no_review'; $this->password = 'e'; } @@ -67,7 +67,7 @@ public function iProvideCredentialsThatDoNotNeedReview() */ public function iProvideCredentialsThatAreDueForAReminder($category, $nagType) { - // See `development/idp-local/config/authsources.php` for options. + // Credentials defined in `development/idp-local/config/authsources.php` $this->username = $category . '_' . $nagType; switch ($this->username) { case 'mfa_add': @@ -85,7 +85,7 @@ public function iProvideCredentialsThatAreDueForAReminder($category, $nagType) */ public function iProvideCredentialsThatAreDueForAProfileReview() { - // See `development/idp-local/config/authsources.php` for options. + // Credentials defined in `development/idp-local/config/authsources.php` $this->username = 'profile_review'; $this->password = 'h'; } From f5b8856a28718c1302d6155b1ded76a653f64888 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:41:50 +0400 Subject: [PATCH 18/46] remove xdebug and add instructions to README --- README.md | 20 ++++++++++++++++++++ development/{hub => }/run-debug.sh | 0 docker-compose.yml | 4 ---- 3 files changed, 20 insertions(+), 4 deletions(-) rename development/{hub => }/run-debug.sh (100%) diff --git a/README.md b/README.md index 7519080e..00cac4a4 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,26 @@ will overwrite variables set in the execution environment. 4. `make` or `docker-compose up -d` within the project root. 5. Visit http://ssp-hub.local to see SimpleSAMLphp +### Configure a container for debugging with Xdebug + +1. Add a volume map for run-debug.sh on the container you wish to debug. + +```yml + - ./development/run-debug.sh:/data/run-debug.sh +``` + +2. Add or change the `command` for the container. + +```yml + command: /data/run-debug.sh +``` + +3. Restart the container. + +```shell +docker composer up -d ssp-hub.local +``` + ### Setup PhpStorm for remote debugging with Docker 1. Make sure you're running PhpStorm 2016.3 or later diff --git a/development/hub/run-debug.sh b/development/run-debug.sh similarity index 100% rename from development/hub/run-debug.sh rename to development/run-debug.sh diff --git a/docker-compose.yml b/docker-compose.yml index be691f7a..0e1c324c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -104,9 +104,6 @@ services: - ./development/hub/metadata/saml20-sp-hosted.php:/data/vendor/simplesamlphp/simplesamlphp/metadata/saml20-sp-hosted.php - ./development/hub/metadata/sp-remote.php:/data/vendor/simplesamlphp/simplesamlphp/metadata/sp-remote.php - # Configure the debugger - - ./development/hub/run-debug.sh:/data/run-debug.sh - # Enable checking our test metadata - ./dockerbuild/run-metadata-tests.sh:/data/run-metadata-tests.sh @@ -117,7 +114,6 @@ services: - ./modules/silauth:/data/vendor/simplesamlphp/simplesamlphp/modules/silauth - ./modules/sildisco:/data/vendor/simplesamlphp/simplesamlphp/modules/sildisco - ./modules/material:/data/vendor/simplesamlphp/simplesamlphp/modules/material - command: /data/run-debug.sh ports: - "80:80" environment: From fa4cc40c0c039583d6d72b023ec2df6bfec3af87 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:50:01 +0400 Subject: [PATCH 19/46] use a new Docker arg to control composer install command flags --- Dockerfile | 3 ++- docker-compose.yml | 5 ++++- dockerbuild/run-integration-tests.sh | 1 - dockerbuild/run-metadata-tests.sh | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index d55ec50f..ec614245 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,10 +34,11 @@ RUN curl https://raw.githubusercontent.com/silinternational/s3-expand/1.5/s3-exp WORKDIR /data # Install/cleanup composer dependencies +ARG COMPOSER_FLAGS="--prefer-dist --no-interaction --no-dev --optimize-autoloader --no-scripts --no-progress" COPY composer.json /data/ COPY composer.lock /data/ RUN composer self-update --no-interaction -RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --prefer-dist --no-interaction --no-dev --optimize-autoloader --no-scripts --no-progress +RUN COMPOSER_ALLOW_SUPERUSER=1 composer install $COMPOSER_FLAGS ENV SSP_PATH /data/vendor/simplesamlphp/simplesamlphp diff --git a/docker-compose.yml b/docker-compose.yml index 0e1c324c..945bc54c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,10 @@ services: PMA_PASSWORD: silauth test: - build: . + build: + context: . + args: + COMPOSER_FLAGS: "--no-interaction --no-progress" depends_on: - ssp-hub.local - ssp-idp1.local diff --git a/dockerbuild/run-integration-tests.sh b/dockerbuild/run-integration-tests.sh index b3d92545..61c751f7 100755 --- a/dockerbuild/run-integration-tests.sh +++ b/dockerbuild/run-integration-tests.sh @@ -7,7 +7,6 @@ set -x set -e cd /data -export COMPOSER_ALLOW_SUPERUSER=1; composer install whenavail "ssp-hub.local" 80 15 echo Hub ready whenavail "ssp-idp1.local" 80 5 echo IDP 1 ready diff --git a/dockerbuild/run-metadata-tests.sh b/dockerbuild/run-metadata-tests.sh index f052741c..d971eda7 100755 --- a/dockerbuild/run-metadata-tests.sh +++ b/dockerbuild/run-metadata-tests.sh @@ -7,6 +7,5 @@ set -x set -e cd /data -export COMPOSER_ALLOW_SUPERUSER=1; composer install ./vendor/bin/phpunit -v tests/MetadataTest.php From 608cce649dab7b77e437bba479b9c5b44f7d9c0b Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:01:09 +0400 Subject: [PATCH 20/46] change container name to match docker-compose.yml --- .github/workflows/test-and-publish.yml | 2 +- actions-services.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-and-publish.yml b/.github/workflows/test-and-publish.yml index 05245c7b..74986898 100644 --- a/.github/workflows/test-and-publish.yml +++ b/.github/workflows/test-and-publish.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: test - run: docker-compose -f actions-services.yml run --rm app ./run-tests.sh + run: docker-compose -f actions-services.yml run --rm test ./run-tests.sh - name: check hub metadata for tests run: docker-compose -f actions-services.yml run --rm ssp-hub.local ./run-metadata-tests.sh - name: check idp metadata for tests diff --git a/actions-services.yml b/actions-services.yml index 0d053e30..d2417b0b 100644 --- a/actions-services.yml +++ b/actions-services.yml @@ -10,7 +10,7 @@ services: MYSQL_USER: silauth MYSQL_PASSWORD: silauth - app: + test: build: . depends_on: - ssp-hub.local From 2dbed2681227cec3df8922b7eeddccad1f09439a Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:01:51 +0400 Subject: [PATCH 21/46] change the composer flags in actions-services.yml --- actions-services.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/actions-services.yml b/actions-services.yml index d2417b0b..5e47a067 100644 --- a/actions-services.yml +++ b/actions-services.yml @@ -11,7 +11,10 @@ services: MYSQL_PASSWORD: silauth test: - build: . + build: + context: . + args: + COMPOSER_FLAGS: "--no-interaction --no-progress" depends_on: - ssp-hub.local - ssp-idp1.local From b44307e22e3904141fde06ef31cd1ddb93e898cd Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:02:08 +0400 Subject: [PATCH 22/46] still need composer install for metadata tests --- dockerbuild/run-metadata-tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dockerbuild/run-metadata-tests.sh b/dockerbuild/run-metadata-tests.sh index d971eda7..f052741c 100755 --- a/dockerbuild/run-metadata-tests.sh +++ b/dockerbuild/run-metadata-tests.sh @@ -7,5 +7,6 @@ set -x set -e cd /data +export COMPOSER_ALLOW_SUPERUSER=1; composer install ./vendor/bin/phpunit -v tests/MetadataTest.php From 9d6bd38d15b51cbfb0787836bd4bb2c80346c802 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:19:15 +0400 Subject: [PATCH 23/46] share the use of static functions in Mfa authproc --- modules/mfa/lib/Auth/Process/Mfa.php | 8 +-- .../lib/Auth/Process/ProfileReview.php | 66 ++----------------- 2 files changed, 9 insertions(+), 65 deletions(-) diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index 44b475eb..ce6f38ac 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -319,7 +319,7 @@ public static function getTemplateFor($mfaType) * @param array $state * @return string */ - protected static function getRelayStateUrl($state) + public static function getRelayStateUrl(array $state): string { if (array_key_exists('saml:RelayState', $state)) { $samlRelayState = $state['saml:RelayState']; @@ -406,12 +406,12 @@ protected function initComposerAutoloader() } } - protected static function isHeadedToMfaSetupUrl($state, $mfaSetupUrl) + public static function isHeadedToUrl(array $state, string $url): bool { if (array_key_exists('saml:RelayState', $state)) { $currentDestination = self::getRelayStateUrl($state); if (! empty($currentDestination)) { - return (strpos($currentDestination, $mfaSetupUrl) === 0); + return (strpos($currentDestination, $url) === 0); } } return false; @@ -578,7 +578,7 @@ public function process(&$state) // Get the necessary info from the state data. $employeeId = $this->getAttribute($this->employeeIdAttr, $state); $mfa = $this->getAttributeAllValues('mfa', $state); - $isHeadedToMfaSetupUrl = self::isHeadedToMfaSetupUrl( + $isHeadedToMfaSetupUrl = self::isHeadedToUrl( $state, $this->mfaSetupUrl ); diff --git a/modules/profilereview/lib/Auth/Process/ProfileReview.php b/modules/profilereview/lib/Auth/Process/ProfileReview.php index 8b8584a9..87a62cd3 100644 --- a/modules/profilereview/lib/Auth/Process/ProfileReview.php +++ b/modules/profilereview/lib/Auth/Process/ProfileReview.php @@ -7,6 +7,7 @@ use SimpleSAML\Auth\ProcessingFilter; use SimpleSAML\Auth\State; use SimpleSAML\Module; +use SimpleSAML\Module\mfa\Auth\Process\Mfa; use SimpleSAML\Module\profilereview\LoggerFactory; use SimpleSAML\Session; use SimpleSAML\Utils\HTTP; @@ -69,8 +70,8 @@ protected function loadValuesFromConfig($config, $attributes) { foreach ($attributes as $attribute) { $this->$attribute = $config[$attribute] ?? null; - - self::validateConfigValue( + + Mfa::validateConfigValue( $attribute, $this->$attribute, $this->logger @@ -78,28 +79,6 @@ protected function loadValuesFromConfig($config, $attributes) } } - /** - * Validate the given config value - * - * @param string $attribute The name of the attribute. - * @param mixed $value The value to check. - * @param LoggerInterface $logger The logger. - * @throws \Exception - */ - public static function validateConfigValue($attribute, $value, $logger) - { - if (empty($value) || !is_string($value)) { - $exception = new \Exception(sprintf( - 'The value we have for %s (%s) is empty or is not a string', - $attribute, - var_export($value, true) - ), 1507146042); - - $logger->critical($exception->getMessage()); - throw $exception; - } - } - /** * Get the specified attribute from the given state data. * @@ -141,30 +120,6 @@ protected function getAttributeAllValues($attributeName, $state) return is_null($attributeData) ? null : (array)$attributeData; } - /** - * Return the saml:RelayState if it begins with "http" or "https". Otherwise - * return an empty string. - * - * @param array $state - * @returns string - * @return mixed|string - */ - protected static function getRelayStateUrl($state) - { - if (array_key_exists('saml:RelayState', $state)) { - $samlRelayState = $state['saml:RelayState']; - - if (strpos($samlRelayState, "http://") === 0) { - return $samlRelayState; - } - - if (strpos($samlRelayState, "https://") === 0) { - return $samlRelayState; - } - } - return ''; - } - protected function initComposerAutoloader() { $path = __DIR__ . '/../../../vendor/autoload.php'; @@ -172,17 +127,6 @@ protected function initComposerAutoloader() require_once $path; } } - - protected static function isHeadedToProfileUrl($state, $ProfileUrl) - { - if (array_key_exists('saml:RelayState', $state)) { - $currentDestination = self::getRelayStateUrl($state); - if (! empty($currentDestination)) { - return (strpos($currentDestination, $ProfileUrl) === 0); - } - } - return false; - } /** * Redirect the user to set up profile. @@ -193,7 +137,7 @@ public static function redirectToProfile(&$state) { $profileUrl = $state['ProfileUrl']; // Tell the profile-setup URL where the user is ultimately trying to go (if known). - $currentDestination = self::getRelayStateUrl($state); + $currentDestination = Mfa::getRelayStateUrl($state); if (! empty($currentDestination)) { $profileUrl = HTTP::addURLParameters( $profileUrl, @@ -223,7 +167,7 @@ public function process(&$state) { // Get the necessary info from the state data. $employeeId = $this->getAttribute($this->employeeIdAttr, $state); - $isHeadedToProfileUrl = self::isHeadedToProfileUrl($state, $this->profileUrl); + $isHeadedToProfileUrl = Mfa::isHeadedToUrl($state, $this->profileUrl); $mfa = $this->getAttributeAllValues('mfa', $state); $method = $this->getAttributeAllValues('method', $state); From 0a1cdfdb7214f1ce9b8271ac951eab481ca8cc5c Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:13:55 +0400 Subject: [PATCH 24/46] add typehints --- features/fakes/FakeIdBrokerClient.php | 2 +- .../lib/Auth/Process/ExpiryDate.php | 58 ++++----- modules/expirychecker/lib/Utilities.php | 19 +-- modules/expirychecker/lib/Validator.php | 4 +- modules/mfa/lib/Assert.php | 6 +- modules/mfa/lib/Auth/Process/Mfa.php | 113 +++++++++--------- modules/mfa/lib/LoggerFactory.php | 4 +- modules/mfa/lib/LoginBrowser.php | 4 +- modules/profilereview/lib/Assert.php | 6 +- .../lib/Auth/Process/ProfileReview.php | 16 ++- modules/profilereview/lib/LoggerFactory.php | 4 +- modules/silauth/lib/Auth/Source/SilAuth.php | 18 +-- .../lib/Auth/Source/auth/AuthError.php | 12 +- .../lib/Auth/Source/auth/Authenticator.php | 67 +++++------ .../silauth/lib/Auth/Source/auth/IdBroker.php | 12 +- .../lib/Auth/Source/captcha/Captcha.php | 6 +- .../lib/Auth/Source/config/ConfigManager.php | 19 +-- .../lib/Auth/Source/csrf/CsrfProtector.php | 18 +-- .../silauth/lib/Auth/Source/http/Request.php | 26 ++-- .../M161213135750CreateInitialTables.php | 4 +- .../M161213150831SwitchToUtcForDateTimes.php | 4 +- .../M170214141109CreateFailedLoginsTable.php | 4 +- .../M170214145629RemoveOldTables.php | 4 +- .../M170215141724SplitFailedLoginsTable.php | 4 +- .../Source/models/FailedLoginIpAddress.php | 27 +++-- .../models/FailedLoginIpAddressBase.php | 6 +- .../Source/models/FailedLoginUsername.php | 25 ++-- .../Source/models/FailedLoginUsernameBase.php | 6 +- modules/silauth/lib/Auth/Source/saml/User.php | 9 +- .../silauth/lib/Auth/Source/system/System.php | 14 +-- .../Source/tests/fakes/FakeFailedIdBroker.php | 4 +- .../tests/fakes/FakeInvalidIdBroker.php | 4 +- .../tests/fakes/FakeSuccessfulIdBroker.php | 4 +- .../tests/unit/captcha/DummyFailedCaptcha.php | 2 +- .../unit/captcha/DummySuccessfulCaptcha.php | 2 +- .../Source/tests/unit/http/DummyRequest.php | 4 +- .../unit/models/FailedLoginIpAddressTest.php | 2 +- .../unit/models/FailedLoginUsernameTest.php | 2 +- modules/silauth/lib/Auth/Source/text/Text.php | 4 +- .../silauth/lib/Auth/Source/time/UtcTime.php | 14 +-- .../silauth/lib/Auth/Source/time/WaitTime.php | 12 +- .../Auth/Source/traits/LoggerAwareTrait.php | 6 +- .../lib/Auth/Process/AddIdp2NameId.php | 17 +-- modules/sildisco/lib/Auth/Process/LogUser.php | 29 +++-- .../sildisco/lib/Auth/Process/TagGroup.php | 3 +- modules/sildisco/lib/IdPDisco.php | 28 ++--- 46 files changed, 335 insertions(+), 323 deletions(-) diff --git a/features/fakes/FakeIdBrokerClient.php b/features/fakes/FakeIdBrokerClient.php index 31899b3a..78654f93 100644 --- a/features/fakes/FakeIdBrokerClient.php +++ b/features/fakes/FakeIdBrokerClient.php @@ -12,7 +12,7 @@ class FakeIdBrokerClient const CORRECT_VALUE = '111111'; const INCORRECT_VALUE = '999999'; - const RATE_LIMITED_MFA_ID = '987'; + const RATE_LIMITED_MFA_ID = 987; /** * Constructor. diff --git a/modules/expirychecker/lib/Auth/Process/ExpiryDate.php b/modules/expirychecker/lib/Auth/Process/ExpiryDate.php index da8edfc4..30322af4 100644 --- a/modules/expirychecker/lib/Auth/Process/ExpiryDate.php +++ b/modules/expirychecker/lib/Auth/Process/ExpiryDate.php @@ -25,16 +25,15 @@ class ExpiryDate extends ProcessingFilter const HAS_SEEN_SPLASH_PAGE = 'has_seen_splash_page'; const SESSION_TYPE = 'expirychecker'; - private $warnDaysBefore = 14; - private $originalUrlParam = 'originalurl'; - private $passwordChangeUrl = null; - private $accountNameAttr = null; - private $employeeIdAttr = 'employeeNumber'; - private $expiryDateAttr = null; - private $dateFormat = 'Y-m-d'; + private int $warnDaysBefore = 14; + private string $originalUrlParam = 'originalurl'; + private string|null $passwordChangeUrl = null; + private string|null $accountNameAttr = null; + private string $employeeIdAttr = 'employeeNumber'; + private string|null $expiryDateAttr = null; + private string $dateFormat = 'Y-m-d'; - /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; /** * Initialize this filter. @@ -42,7 +41,7 @@ class ExpiryDate extends ProcessingFilter * @param array $config Configuration information about this filter. * @param mixed $reserved For future use. */ - public function __construct($config, $reserved) + public function __construct(array $config, mixed $reserved) { parent::__construct($config, $reserved); @@ -77,7 +76,7 @@ public function __construct($config, $reserved) ]); } - protected function loadValuesFromConfig($config, $attributeRules) + protected function loadValuesFromConfig(array $config, array $attributeRules): void { foreach ($attributeRules as $attribute => $rules) { if (array_key_exists($attribute, $config)) { @@ -99,7 +98,7 @@ protected function loadValuesFromConfig($config, $attributeRules) * @param array $state The state data. * @return mixed The attribute value, or null if not found. */ - protected function getAttribute($attributeName, $state) + protected function getAttribute(string $attributeName, array $state): mixed { $attributeData = $state['Attributes'][$attributeName] ?? null; @@ -118,7 +117,7 @@ protected function getAttribute($attributeName, $state) * expire. * @return int The number of days remaining */ - protected function getDaysLeftBeforeExpiry($expiryTimestamp): int + protected function getDaysLeftBeforeExpiry(int $expiryTimestamp): int { $now = time(); $end = $expiryTimestamp; @@ -135,7 +134,7 @@ protected function getDaysLeftBeforeExpiry($expiryTimestamp): int * @return int The expiration timestamp. * @throws \Exception */ - protected function getExpiryTimestamp($expiryDateAttr, $state) + protected function getExpiryTimestamp(string $expiryDateAttr, array $state): int { $expiryDateString = $this->getAttribute($expiryDateAttr, $state); @@ -153,7 +152,7 @@ protected function getExpiryTimestamp($expiryDateAttr, $state) return $expiryTimestamp; } - public static function hasSeenSplashPageRecently() + public static function hasSeenSplashPageRecently(): bool { $session = Session::getSessionFromRequest(); return (bool)$session->getData( @@ -162,7 +161,7 @@ public static function hasSeenSplashPageRecently() ); } - public static function skipSplashPagesFor($seconds) + public static function skipSplashPagesFor(int $seconds): void { $session = Session::getSessionFromRequest(); $session->setData( @@ -174,7 +173,7 @@ public static function skipSplashPagesFor($seconds) $session->save(); } - protected function initLogger($config) + protected function initLogger(array $config): void { $loggerClass = $config['loggerClass'] ?? Psr3SamlLogger::class; $this->logger = new $loggerClass(); @@ -193,7 +192,7 @@ protected function initLogger($config) * @param int $timestamp The timestamp to check. * @return bool */ - public function isDateInPast(int $timestamp) + public function isDateInPast(int $timestamp): bool { return ($timestamp < time()); } @@ -205,7 +204,7 @@ public function isDateInPast(int $timestamp) * will expire. * @return bool */ - public function isExpired(int $expiryTimestamp) + public function isExpired(int $expiryTimestamp): bool { return $this->isDateInPast($expiryTimestamp); } @@ -219,7 +218,7 @@ public function isExpired(int $expiryTimestamp) * warn the user. * @return boolean */ - public function isTimeToWarn($expiryTimestamp, $warnDaysBefore) + public function isTimeToWarn(int $expiryTimestamp, int $warnDaysBefore): bool { $daysLeft = $this->getDaysLeftBeforeExpiry($expiryTimestamp); return ($daysLeft <= $warnDaysBefore); @@ -235,12 +234,13 @@ public function isTimeToWarn($expiryTimestamp, $warnDaysBefore) * @param int $expiryTimestamp The timestamp when the password will expire. */ public function redirect2PasswordChange( - &$state, - $accountName, - $passwordChangeUrl, - $change_pwd_session, - $expiryTimestamp - ) { + array &$state, + string $accountName, + string $passwordChangeUrl, + string $change_pwd_session, + int $expiryTimestamp + ): void + { $sessionType = 'expirychecker'; /* Save state and redirect. */ $state['expiresAtTimestamp'] = $expiryTimestamp; @@ -302,7 +302,7 @@ public function redirect2PasswordChange( * * @param array &$state The current state. */ - public function process(&$state) + public function process(&$state): void { $employeeId = $this->getAttribute($this->employeeIdAttr, $state); @@ -351,7 +351,7 @@ public function process(&$state) * @param string $accountName The name of the user account. * @param int $expiryTimestamp When the password expired. */ - public function redirectToExpiredPage(&$state, $accountName, $expiryTimestamp) + public function redirectToExpiredPage(array &$state, string $accountName, int $expiryTimestamp): void { assert('is_array($state)'); @@ -379,7 +379,7 @@ public function redirectToExpiredPage(&$state, $accountName, $expiryTimestamp) * @param string $accountName The name of the user account. * @param int $expiryTimestamp When the password will expire. */ - protected function redirectToWarningPage(&$state, $accountName, $expiryTimestamp) + protected function redirectToWarningPage(array &$state, string $accountName, int $expiryTimestamp): void { assert('is_array($state)'); diff --git a/modules/expirychecker/lib/Utilities.php b/modules/expirychecker/lib/Utilities.php index 0e2b80b6..72b8a95b 100644 --- a/modules/expirychecker/lib/Utilities.php +++ b/modules/expirychecker/lib/Utilities.php @@ -10,8 +10,8 @@ class Utilities { * * Returns a string with the domain portion of the url (e.g. www.insitehome.org) */ - public static function getUrlDomain($in_url, $start_marker='//', - $end_marker='/') { + public static function getUrlDomain(string $in_url, string $start_marker='//', string $end_marker='/'): string + { $sm_len = strlen($start_marker); $em_len = strlen($end_marker); @@ -29,9 +29,10 @@ public static function getUrlDomain($in_url, $start_marker='//', * * Returns 1 if the domains of the two urls are the same and 0 otherwise. */ - public static function haveSameDomain($url1, $start_marker1, - $end_marker1, $url2, $start_marker2='//', - $end_marker2='/') { + public static function haveSameDomain(string $url1, string $start_marker1, + string $end_marker1, string $url2, string $start_marker2='//', + string $end_marker2='/'): int + { $domain1 = self::getUrlDomain($url1, $start_marker1, $end_marker1); $domain2 = self::getUrlDomain($url2, $start_marker2, $end_marker2); @@ -51,8 +52,9 @@ public static function haveSameDomain($url1, $start_marker1, * for apex to use. If the domains of the change password url and the * original url are different, it appends the StateId to the output. */ - public static function convertOriginalUrl($passwordChangeUrl, - $originalUrlParam, $originalUrl, $stateId ) { + public static function convertOriginalUrl(string $passwordChangeUrl, + string $originalUrlParam, string $originalUrl, string $stateId ): array|string + { $sameDomain = self::haveSameDomain($passwordChangeUrl, '//', '/', $originalUrl, '//', '/'); $original = $originalUrlParam . ":" . urlencode($originalUrl); @@ -80,7 +82,8 @@ public static function convertOriginalUrl($passwordChangeUrl, * @param string $relayState * @return string **/ - public static function getUrlFromRelayState($relayState) { + public static function getUrlFromRelayState(string $relayState): string + { if (strpos($relayState, "http") === 0) { return $relayState; } diff --git a/modules/expirychecker/lib/Validator.php b/modules/expirychecker/lib/Validator.php index ba0020ac..f17be246 100644 --- a/modules/expirychecker/lib/Validator.php +++ b/modules/expirychecker/lib/Validator.php @@ -20,7 +20,7 @@ class Validator * @param LoggerInterface $logger The logger. * @throws Exception */ - public static function validate($value, $rules, $logger, $attribute) + public static function validate(mixed $value, array $rules, LoggerInterface $logger, string $attribute): void { foreach ($rules as $rule) { if ( ! self::isValid($value, $rule, $logger)) { @@ -47,7 +47,7 @@ public static function validate($value, $rules, $logger, $attribute) * @return bool * @throws InvalidArgumentException */ - protected static function isValid($value, $rule, $logger) + protected static function isValid(mixed $value, string $rule, LoggerInterface $logger): bool { switch ($rule) { case self::INT: diff --git a/modules/mfa/lib/Assert.php b/modules/mfa/lib/Assert.php index 7d0ef245..82285d1c 100644 --- a/modules/mfa/lib/Assert.php +++ b/modules/mfa/lib/Assert.php @@ -15,7 +15,7 @@ class Assert * @param string $className The name of the class in question. * @throws InvalidArgumentException */ - public static function classExists(string $className) + public static function classExists(string $className): void { if (! class_exists($className)) { throw new InvalidArgumentException(sprintf( @@ -32,7 +32,7 @@ public static function classExists(string $className) * @param mixed $value The value in question. * @return string */ - protected static function describe($value) + protected static function describe(mixed $value): string { return is_object($value) ? get_class($value) : var_export($value, true); } @@ -44,7 +44,7 @@ protected static function describe($value) * @param string $className The name/classpath of the class in question. * @throws InvalidArgumentException */ - public static function isInstanceOf($object, string $className) + public static function isInstanceOf(mixed $object, string $className): void { if (! ($object instanceof $className)) { throw new InvalidArgumentException(sprintf( diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index ce6f38ac..0f1d681a 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -3,10 +3,13 @@ namespace SimpleSAML\Module\mfa\Auth\Process; use Psr\Log\LoggerInterface; +use Sil\Idp\IdBroker\Client\BaseClient; use Sil\PhpEnv\Env; use Sil\Idp\IdBroker\Client\ServiceException; use Sil\Idp\IdBroker\Client\IdBrokerClient; +use Sil\PhpEnv\EnvVarNotFoundException; use Sil\Psr3Adapters\Psr3SamlLogger; +use Sil\SspBase\Features\fakes\FakeIdBrokerClient; use SimpleSAML\Module\mfa\LoggerFactory; use SimpleSAML\Module\mfa\LoginBrowser; use SimpleSAML\Auth\ProcessingChain; @@ -30,21 +33,19 @@ class Mfa extends ProcessingFilter const STAGE_SENT_TO_NEW_BACKUP_CODES_PAGE = 'mfa:sent_to_new_backup_codes_page'; const STAGE_SENT_TO_OUT_OF_BACKUP_CODES_MESSAGE = 'mfa:sent_to_out_of_backup_codes_message'; - private $employeeIdAttr = null; - private $idpDomainName = null; - private $mfaSetupUrl = null; + private string|null $employeeIdAttr = null; + private string|null $idpDomainName = null; + private string|null $mfaSetupUrl = null; - private $idBrokerAccessToken = null; - private $idBrokerAssertValidIp; - private $idBrokerBaseUri = null; - private $idBrokerClientClass = null; - private $idBrokerTrustedIpRanges = []; + private string|null $idBrokerAccessToken = null; + private bool $idBrokerAssertValidIp; + private string|null $idBrokerBaseUri = null; + private string|null $idBrokerClientClass = null; + private array $idBrokerTrustedIpRanges = []; - /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; - /** @var string */ - protected $loggerClass; + protected string $loggerClass; /** * Initialize this filter. @@ -53,7 +54,7 @@ class Mfa extends ProcessingFilter * @param mixed $reserved For future use. * @throws \Exception */ - public function __construct($config, $reserved) + public function __construct(array $config, mixed $reserved) { parent::__construct($config, $reserved); $this->initComposerAutoloader(); @@ -78,7 +79,7 @@ public function __construct($config, $reserved) $this->idBrokerClientClass = $config['idBrokerClientClass'] ?? IdBrokerClient::class; } - protected function loadValuesFromConfig($config, $attributes) + protected function loadValuesFromConfig(array $config, array $attributes): void { foreach ($attributes as $attribute) { $this->$attribute = $config[$attribute] ?? null; @@ -99,7 +100,7 @@ protected function loadValuesFromConfig($config, $attributes) * @param LoggerInterface $logger The logger. * @throws \Exception */ - public static function validateConfigValue($attribute, $value, $logger) + public static function validateConfigValue(string $attribute, mixed $value, LoggerInterface $logger): void { if (empty($value) || !is_string($value)) { $exception = new \Exception(sprintf( @@ -124,7 +125,7 @@ public static function validateConfigValue($attribute, $value, $logger) * @param array $state The state data. * @return mixed The attribute value, or null if not found. */ - protected function getAttribute($attributeName, $state) + protected function getAttribute(string $attributeName, array $state): mixed { $attributeData = $state['Attributes'][$attributeName] ?? null; @@ -147,7 +148,7 @@ protected function getAttribute($attributeName, $state) * @return array|null The attribute's value(s), or null if the attribute was * not found. */ - protected function getAttributeAllValues($attributeName, $state) + protected function getAttributeAllValues(string $attributeName, array $state): ?array { $attributeData = $state['Attributes'][$attributeName] ?? null; @@ -160,7 +161,7 @@ protected function getAttributeAllValues($attributeName, $state) * @param array $idBrokerConfig * @return IdBrokerClient */ - protected static function getIdBrokerClient($idBrokerConfig) + protected static function getIdBrokerClient(array $idBrokerConfig): IdBrokerClient|FakeIdBrokerClient { $clientClass = $idBrokerConfig['clientClass']; $baseUri = $idBrokerConfig['baseUri']; @@ -186,7 +187,7 @@ protected static function getIdBrokerClient($idBrokerConfig) * @throws \InvalidArgumentException * @throws \Exception */ - public static function getMfaOptionById($mfaOptions, $mfaId) + public static function getMfaOptionById(array $mfaOptions, int $mfaId): array { if (empty($mfaId)) { throw new \Exception('No MFA ID was provided.'); @@ -213,7 +214,7 @@ public static function getMfaOptionById($mfaOptions, $mfaId) * @throws \InvalidArgumentException * @throws \Exception */ - public static function getMfaOptionToUse($mfaOptions, $userAgent) + public static function getMfaOptionToUse(array $mfaOptions, string $userAgent): array { if (empty($mfaOptions)) { throw new \Exception('No MFA options were provided.'); @@ -253,7 +254,8 @@ public static function getMfaOptionToUse($mfaOptions, $userAgent) * @param array[] $mfaOptions The available MFA options. * @return ?array The MFA option to use. */ - private static function getMostRecentUsedMfaOption($mfaOptions) { + private static function getMostRecentUsedMfaOption(array $mfaOptions): ?array + { $recentMfa = null; $recentDate = '1991-01-01T00:00:00Z'; @@ -270,10 +272,9 @@ private static function getMostRecentUsedMfaOption($mfaOptions) { * Get the number of backup codes that the user had left PRIOR to this login. * * @param array $mfaOptions The list of MFA options. - * @return int The number of backup codes that the user HAD (prior to this - * login). + * @return int The number of backup codes that the user HAD (prior to this login). */ - public static function getNumBackupCodesUserHad(array $mfaOptions) + public static function getNumBackupCodesUserHad(array $mfaOptions): int { $numBackupCodes = 0; foreach ($mfaOptions as $mfaOption) { @@ -293,7 +294,7 @@ public static function getNumBackupCodesUserHad(array $mfaOptions) * @return string * @throws \InvalidArgumentException */ - public static function getTemplateFor($mfaType) + public static function getTemplateFor(string $mfaType): string { $mfaOptionTemplates = [ 'backupcode' => 'mfa:prompt-for-mfa-backupcode.php', @@ -344,7 +345,7 @@ public static function getRelayStateUrl(array $state): string * @param array $state The state data. * @param LoggerInterface $logger A PSR-3 compatible logger. */ - public static function giveUserNewBackupCodes(array &$state, $logger) + public static function giveUserNewBackupCodes(array &$state, LoggerInterface $logger): void { try { $idBrokerClient = self::getIdBrokerClient($state['idBrokerConfig']); @@ -375,7 +376,7 @@ public static function giveUserNewBackupCodes(array &$state, $logger) HTTP::redirectTrustedURL($url, ['StateId' => $stateId]); } - protected static function hasMfaOptions($mfa) + protected static function hasMfaOptions(array $mfa): bool { return (count($mfa['options']) > 0); } @@ -387,7 +388,7 @@ protected static function hasMfaOptions($mfa) * @param array $state * @return bool */ - public static function hasMfaOptionsOtherThan($excludedMfaType, $state) + public static function hasMfaOptionsOtherThan(string $excludedMfaType, array $state): bool { $mfaOptions = $state['mfaOptions'] ?? []; foreach ($mfaOptions as $mfaOption) { @@ -398,7 +399,7 @@ public static function hasMfaOptionsOtherThan($excludedMfaType, $state) return false; } - protected function initComposerAutoloader() + protected function initComposerAutoloader(): void { $path = __DIR__ . '/../../../vendor/autoload.php'; if (file_exists($path)) { @@ -430,20 +431,20 @@ public static function isHeadedToUrl(array $state, string $url): bool * @param LoggerInterface $logger A PSR-3 compatible logger. * @param string $mfaType The type of the MFA ('webauthn', 'totp', 'backupcode'). * @param string $rpOrigin The Relying Party Origin (for WebAuthn) - * @return void|string If validation fails, an error message to show to the + * @return string If validation fails, an error message to show to the * end user will be returned. * @throws \Sil\PhpEnv\EnvVarNotFoundException */ public static function validateMfaSubmission( - $mfaId, - $employeeId, - $mfaSubmission, - $state, - $rememberMe, + int $mfaId, + string $employeeId, + string $mfaSubmission, + array $state, + bool $rememberMe, LoggerInterface $logger, string $mfaType, string $rpOrigin - ) { + ): string { if (empty($mfaId)) { return 'No MFA ID was provided.'; } elseif (empty($employeeId)) { @@ -543,7 +544,7 @@ public static function validateMfaSubmission( * * @param array $state */ - public static function redirectToMfaSetup(&$state) + public static function redirectToMfaSetup(array &$state): void { $mfaSetupUrl = $state['mfaSetupUrl']; @@ -573,7 +574,7 @@ public static function redirectToMfaSetup(&$state) * * @param array &$state The current state. */ - public function process(&$state) + public function process(&$state): void { // Get the necessary info from the state data. $employeeId = $this->getAttribute($this->employeeIdAttr, $state); @@ -621,7 +622,7 @@ public function process(&$state) * @param string $employeeId The Employee ID of the user account. * @param string $mfaSetupUrl URL to MFA setup process */ - protected function redirectToMfaNeededMessage(&$state, $employeeId, $mfaSetupUrl) + protected function redirectToMfaNeededMessage(array &$state, string $employeeId, string $mfaSetupUrl): void { assert('is_array($state)'); @@ -648,7 +649,7 @@ protected function redirectToMfaNeededMessage(&$state, $employeeId, $mfaSetupUrl * @param array $mfaOptions Array of MFA options * @throws \Exception */ - protected function redirectToMfaPrompt(&$state, $employeeId, $mfaOptions) + protected function redirectToMfaPrompt(array &$state, string $employeeId, array $mfaOptions): void { assert('is_array($state)'); @@ -696,16 +697,16 @@ protected function redirectToMfaPrompt(&$state, $employeeId, $mfaOptions) * Validate that remember me cookie values are legit and valid * @param string $cookieHash * @param string $expireDate - * @param $mfaOptions - * @param $state + * @param array $mfaOptions + * @param array $state * @return bool - * @throws \Sil\PhpEnv\EnvVarNotFoundException + * @throws EnvVarNotFoundException */ public static function isRememberMeCookieValid( string $cookieHash, string $expireDate, - $mfaOptions, - $state + array $mfaOptions, + array $state ): bool { $rememberSecret = Env::requireEnv('REMEMBER_ME_SECRET'); if (! empty($cookieHash) && ! empty($expireDate) && is_numeric($expireDate)) { @@ -757,9 +758,9 @@ public static function generateRememberMeCookieString( */ protected static function redirectToLowOnBackupCodesNag( array &$state, - $employeeId, - $numBackupCodesRemaining - ) { + string $employeeId, + int $numBackupCodesRemaining + ): void { $state['employeeId'] = $employeeId; $state['numBackupCodesRemaining'] = (string)$numBackupCodesRemaining; @@ -778,7 +779,7 @@ protected static function redirectToLowOnBackupCodesNag( * @param array $state The state data. * @param string $employeeId The Employee ID of the user account. */ - protected static function redirectToOutOfBackupCodesMessage(array &$state, $employeeId) + protected static function redirectToOutOfBackupCodesMessage(array &$state, string $employeeId): void { $state['employeeId'] = $employeeId; @@ -799,7 +800,7 @@ public static function setRememberMeCookies( string $employeeId, array $mfaOptions, string $rememberDuration = '+30 days' - ) { + ): void { $rememberSecret = Env::requireEnv('REMEMBER_ME_SECRET'); $secureCookie = Env::get('SECURE_COOKIE', true); $expireDate = strtotime($rememberDuration); @@ -809,7 +810,7 @@ public static function setRememberMeCookies( setcookie('c2', $expireDate, $expireDate, '/', null, $secureCookie, true); } - protected static function shouldPromptForMfa($mfa) + protected static function shouldPromptForMfa(array $mfa): bool { return (strtolower($mfa['prompt']) !== 'no'); } @@ -823,7 +824,7 @@ protected static function shouldPromptForMfa($mfa) * @param array $state The state data. * @param LoggerInterface $logger A PSR-3 compatible logger. */ - public static function sendManagerCode(array &$state, $logger) + public static function sendManagerCode(array &$state, LoggerInterface $logger): void { try { $idBrokerClient = self::getIdBrokerClient($state['idBrokerConfig']); @@ -864,7 +865,7 @@ public static function sendManagerCode(array &$state, $logger) * @param array $state * @return string|null */ - public static function getManagerEmail($state) + public static function getManagerEmail(array $state): ?string { $managerEmail = $state['Attributes']['manager_email'] ?? ['']; if (empty($managerEmail[0])) { @@ -880,7 +881,7 @@ public static function getManagerEmail($state) * @return array The manager MFA. * @throws \InvalidArgumentException */ - public static function getManagerMfa($mfaOptions) + public static function getManagerMfa(array $mfaOptions): ?array { foreach ($mfaOptions as $mfaOption) { if ($mfaOption['type'] === 'manager') { @@ -895,7 +896,7 @@ public static function getManagerMfa($mfaOptions) * @param string $email an email address * @return string with most letters changed to asterisks */ - public static function maskEmail($email) + public static function maskEmail(string $email): string { list($part1, $domain) = explode('@', $email); $newEmail = ''; @@ -942,7 +943,7 @@ public static function maskEmail($email) * @param array $state * @param LoggerInterface $logger */ - protected static function updateStateWithNewMfaData(&$state, $logger) + protected static function updateStateWithNewMfaData(array &$state, LoggerInterface $logger): void { $idBrokerClient = self::getIdBrokerClient($state['idBrokerConfig']); diff --git a/modules/mfa/lib/LoggerFactory.php b/modules/mfa/lib/LoggerFactory.php index ffdadd6b..b42c6beb 100644 --- a/modules/mfa/lib/LoggerFactory.php +++ b/modules/mfa/lib/LoggerFactory.php @@ -16,7 +16,7 @@ class LoggerFactory * * @throws InvalidArgumentException */ - public static function get($loggerClass) + public static function get(string $loggerClass): LoggerInterface { Assert::classExists($loggerClass); $logger = new $loggerClass(); @@ -34,7 +34,7 @@ public static function get($loggerClass) * * @throws InvalidArgumentException */ - public static function getAccordingToState($state) + public static function getAccordingToState(array $state): LoggerInterface { return self::get($state['loggerClass'] ?? Psr3SamlLogger::class); } diff --git a/modules/mfa/lib/LoginBrowser.php b/modules/mfa/lib/LoginBrowser.php index 77b1abca..19c57b37 100644 --- a/modules/mfa/lib/LoginBrowser.php +++ b/modules/mfa/lib/LoginBrowser.php @@ -13,13 +13,13 @@ class LoginBrowser * * @return string|null */ - public static function getUserAgent() + public static function getUserAgent(): ?string { return filter_input(INPUT_SERVER, 'HTTP_USER_AGENT') ?: null; } // TODO: Replace this with client-side feature detection. - public static function supportsWebAuthn($userAgent) + public static function supportsWebAuthn(string $userAgent): bool { $browser = new Browser($userAgent); $browserName = $browser->getName(); diff --git a/modules/profilereview/lib/Assert.php b/modules/profilereview/lib/Assert.php index f20dff99..af7bf7dc 100644 --- a/modules/profilereview/lib/Assert.php +++ b/modules/profilereview/lib/Assert.php @@ -13,7 +13,7 @@ class Assert * @param string $className The name of the class in question. * @throws InvalidArgumentException */ - public static function classExists(string $className) + public static function classExists(string $className): void { if (! class_exists($className)) { throw new InvalidArgumentException(sprintf( @@ -30,7 +30,7 @@ public static function classExists(string $className) * @param mixed $value The value in question. * @return string */ - protected static function describe($value) + protected static function describe(mixed $value): string { return is_object($value) ? get_class($value) : var_export($value, true); } @@ -42,7 +42,7 @@ protected static function describe($value) * @param string $className The name/classpath of the class in question. * @throws InvalidArgumentException */ - public static function isInstanceOf($object, string $className) + public static function isInstanceOf(mixed $object, string $className): void { if (! ($object instanceof $className)) { throw new InvalidArgumentException(sprintf( diff --git a/modules/profilereview/lib/Auth/Process/ProfileReview.php b/modules/profilereview/lib/Auth/Process/ProfileReview.php index 87a62cd3..b34e0567 100644 --- a/modules/profilereview/lib/Auth/Process/ProfileReview.php +++ b/modules/profilereview/lib/Auth/Process/ProfileReview.php @@ -26,15 +26,13 @@ class ProfileReview extends ProcessingFilter const MFA_ADD_PAGE = 'nag-for-mfa.php'; const METHOD_ADD_PAGE = 'nag-for-method.php'; - private $employeeIdAttr = null; - private $mfaLearnMoreUrl = null; - private $profileUrl = null; + private string|null $employeeIdAttr = null; + private string|null $mfaLearnMoreUrl = null; + private string|null $profileUrl = null; - /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; - /** @var string */ - protected $loggerClass; + protected string $loggerClass; /** * Initialize this filter. @@ -43,7 +41,7 @@ class ProfileReview extends ProcessingFilter * @param mixed $reserved For future use. * @throws \Exception */ - public function __construct($config, $reserved) + public function __construct(array $config, mixed $reserved) { parent::__construct($config, $reserved); $this->initComposerAutoloader(); @@ -66,7 +64,7 @@ public function __construct($config, $reserved) * @param $attributes * @throws \Exception */ - protected function loadValuesFromConfig($config, $attributes) + protected function loadValuesFromConfig(array $config, array $attributes): void { foreach ($attributes as $attribute) { $this->$attribute = $config[$attribute] ?? null; diff --git a/modules/profilereview/lib/LoggerFactory.php b/modules/profilereview/lib/LoggerFactory.php index 83e335d1..8381476a 100644 --- a/modules/profilereview/lib/LoggerFactory.php +++ b/modules/profilereview/lib/LoggerFactory.php @@ -14,7 +14,7 @@ class LoggerFactory * * @throws InvalidArgumentException */ - public static function get($loggerClass) + public static function get(string $loggerClass): LoggerInterface { Assert::classExists($loggerClass); $logger = new $loggerClass(); @@ -32,7 +32,7 @@ public static function get($loggerClass) * * @throws InvalidArgumentException */ - public static function getAccordingToState($state) + public static function getAccordingToState(array $state): LoggerInterface { return self::get($state['loggerClass'] ?? Psr3SamlLogger::class); } diff --git a/modules/silauth/lib/Auth/Source/SilAuth.php b/modules/silauth/lib/Auth/Source/SilAuth.php index 6560b4d9..f7c0624f 100644 --- a/modules/silauth/lib/Auth/Source/SilAuth.php +++ b/modules/silauth/lib/Auth/Source/SilAuth.php @@ -23,11 +23,11 @@ */ class SilAuth extends UserPassBase { - protected $authConfig; - protected $idBrokerConfig; - protected $mysqlConfig; - protected $recaptchaConfig; - protected $templateData; + protected array $authConfig; + protected array $idBrokerConfig; + protected array $mysqlConfig; + protected array $recaptchaConfig; + protected array $templateData; /** * Constructor for this authentication source. @@ -38,7 +38,7 @@ class SilAuth extends UserPassBase * @param array $info Information about this authentication source. * @param array $config Configuration for this authentication source. */ - public function __construct($info, $config) + public function __construct(array $info, array $config) { parent::__construct($info, $config); @@ -67,7 +67,7 @@ public function __construct($info, $config) * * @param array &$state Information about the current authentication. */ - public function authenticate(&$state) + public function authenticate(&$state): void { assert('is_array($state)'); @@ -95,7 +95,7 @@ public function authenticate(&$state) assert('FALSE'); } - protected function getTrustedIpAddresses() + protected function getTrustedIpAddresses(): array { $trustedIpAddresses = []; $ipAddressesString = $this->authConfig['trustedIpAddresses'] ?? ''; @@ -108,7 +108,7 @@ protected function getTrustedIpAddresses() return $trustedIpAddresses; } - protected function login($username, $password) + protected function login($username, $password): ?array { $logger = new Psr3StdOutLogger(); $captcha = new Captcha($this->recaptchaConfig['secret'] ?? null); diff --git a/modules/silauth/lib/Auth/Source/auth/AuthError.php b/modules/silauth/lib/Auth/Source/auth/AuthError.php index e46a4abb..6eefe024 100644 --- a/modules/silauth/lib/Auth/Source/auth/AuthError.php +++ b/modules/silauth/lib/Auth/Source/auth/AuthError.php @@ -16,8 +16,8 @@ class AuthError const CODE_RATE_LIMIT_1_MINUTE = 'rate_limit_1_minute'; const CODE_RATE_LIMIT_MINUTES = 'rate_limit_minutes'; - private $code = null; - private $messageParams = []; + private string $code; + private array $messageParams = []; /** * Constructor. @@ -25,7 +25,7 @@ class AuthError * @param string $code One of the AuthError::CODE_* constants. * @param array $messageParams The error message parameters. */ - public function __construct($code, $messageParams = []) + public function __construct(string $code, array $messageParams = []) { $this->code = $code; $this->messageParams = $messageParams; @@ -44,7 +44,7 @@ public function __toString() * * @return string */ - public function getCode() + public function getCode(): string { return $this->code; } @@ -56,7 +56,7 @@ public function getCode() * * @return string Example: '{silauth:error:generic_try_later}' */ - public function getFullSspErrorTag() + public function getFullSspErrorTag(): string { return sprintf( '{%s:%s}', @@ -65,7 +65,7 @@ public function getFullSspErrorTag() ); } - public function getMessageParams() + public function getMessageParams(): array { return $this->messageParams; } diff --git a/modules/silauth/lib/Auth/Source/auth/Authenticator.php b/modules/silauth/lib/Auth/Source/auth/Authenticator.php index 82ca08fa..2e91ed7e 100644 --- a/modules/silauth/lib/Auth/Source/auth/Authenticator.php +++ b/modules/silauth/lib/Auth/Source/auth/Authenticator.php @@ -21,13 +21,9 @@ class Authenticator const BLOCK_AFTER_NTH_FAILED_LOGIN = 50; const MAX_SECONDS_TO_BLOCK = 3600; // 3600 seconds = 1 hour - /** @var AuthError|null */ - private $authError = null; - - /** @var LoggerInterface */ - protected $logger; - - private $userAttributes = null; + private ?AuthError $authError = null; + protected LoggerInterface $logger; + private ?array $userAttributes = null; /** * Attempt to authenticate using the given username and password. Check @@ -41,12 +37,12 @@ class Authenticator * @param LoggerInterface $logger A PSR-3 compliant logger. */ public function __construct( - $username, - $password, - Request $request, - Captcha $captcha, - IdBroker $idBroker, - LoggerInterface $logger + string $username, + string $password, + Request $request, + Captcha $captcha, + IdBroker $idBroker, + LoggerInterface $logger ) { $this->logger = $logger; @@ -143,7 +139,7 @@ public function __construct( * @return int The number of seconds to delay before allowing another such * login attempt. */ - public static function calculateSecondsToDelay($numRecentFailures) + public static function calculateSecondsToDelay(int $numRecentFailures): int { if ( ! self::isEnoughFailedLoginsToBlock($numRecentFailures)) { return 0; @@ -164,7 +160,7 @@ public static function calculateSecondsToDelay($numRecentFailures) * * @return AuthError|null */ - public function getAuthError() + public function getAuthError(): ?AuthError { return $this->authError; } @@ -184,8 +180,9 @@ public function getAuthError() */ public static function getSecondsUntilUnblocked( int $numRecentFailures, - $mostRecentFailureAt - ) { + ?string $mostRecentFailureAt + ): int + { if ($mostRecentFailureAt === null) { return 0; } @@ -216,7 +213,7 @@ public static function getSecondsUntilUnblocked( * * @throws \Exception */ - public function getUserAttributes() + public function getUserAttributes(): ?array { if ($this->userAttributes === null) { throw new \Exception( @@ -242,7 +239,7 @@ public function getUserAttributes() * this request). * @return WaitTime */ - protected function getWaitTimeUntilUnblocked($username, array $ipAddresses) + protected function getWaitTimeUntilUnblocked(string $username, array $ipAddresses): WaitTime { $durationsInSeconds = [ FailedLoginUsername::getSecondsUntilUnblocked($username), @@ -255,7 +252,7 @@ protected function getWaitTimeUntilUnblocked($username, array $ipAddresses) return WaitTime::getLongestWaitTime($durationsInSeconds); } - protected function hasError() + protected function hasError(): bool { return ($this->authError !== null); } @@ -266,46 +263,46 @@ protected function hasError() * * @return bool */ - public function isAuthenticated() + public function isAuthenticated(): bool { return ( ! $this->hasError()); } - protected function isBlockedByRateLimit($username, array $ipAddresses) + protected function isBlockedByRateLimit(string $username, array $ipAddresses): bool { return FailedLoginUsername::isRateLimitBlocking($username) || FailedLoginIpAddress::isRateLimitBlockingAnyOfThese($ipAddresses); } - public static function isCaptchaRequired($username, array $ipAddresses) + public static function isCaptchaRequired(string $username, array $ipAddresses): bool { return FailedLoginUsername::isCaptchaRequiredFor($username) || FailedLoginIpAddress::isCaptchaRequiredForAnyOfThese($ipAddresses); } - public static function isEnoughFailedLoginsToBlock($numFailedLogins) + public static function isEnoughFailedLoginsToBlock(int $numFailedLogins): bool { return ($numFailedLogins >= self::BLOCK_AFTER_NTH_FAILED_LOGIN); } - public static function isEnoughFailedLoginsToRequireCaptcha($numFailedLogins) + public static function isEnoughFailedLoginsToRequireCaptcha(int $numFailedLogins): bool { return ($numFailedLogins >= self::REQUIRE_CAPTCHA_AFTER_NTH_FAILED_LOGIN); } - protected function recordFailedLoginBy($username, array $ipAddresses) + protected function recordFailedLoginBy(string $username, array $ipAddresses): void { FailedLoginUsername::recordFailedLoginBy($username, $this->logger); FailedLoginIpAddress::recordFailedLoginBy($ipAddresses, $this->logger); } - protected function resetFailedLoginsBy($username, array $ipAddresses) + protected function resetFailedLoginsBy(string $username, array $ipAddresses): void { FailedLoginUsername::resetFailedLoginsBy($username); FailedLoginIpAddress::resetFailedLoginsBy($ipAddresses); } - protected function setError($code, $messageParams = []) + protected function setError(string $code, array $messageParams = []): void { $this->authError = new AuthError($code, $messageParams); } @@ -313,7 +310,7 @@ protected function setError($code, $messageParams = []) /** * @param WaitTime $waitTime */ - protected function setErrorBlockedByRateLimit($waitTime) + protected function setErrorBlockedByRateLimit(WaitTime $waitTime): void { $unit = $waitTime->getUnit(); $number = $waitTime->getFriendlyNumber(); @@ -331,32 +328,32 @@ protected function setErrorBlockedByRateLimit($waitTime) $this->setError($errorCode, ['{number}' => $number]); } - protected function setErrorGenericTryLater() + protected function setErrorGenericTryLater(): void { $this->setError(AuthError::CODE_GENERIC_TRY_LATER); } - protected function setErrorInvalidLogin() + protected function setErrorInvalidLogin(): void { $this->setError(AuthError::CODE_INVALID_LOGIN); } - protected function setErrorNeedToSetAcctPassword() + protected function setErrorNeedToSetAcctPassword(): void { $this->setError(AuthError::CODE_NEED_TO_SET_ACCT_PASSWORD); } - protected function setErrorPasswordRequired() + protected function setErrorPasswordRequired(): void { $this->setError(AuthError::CODE_PASSWORD_REQUIRED); } - protected function setErrorUsernameRequired() + protected function setErrorUsernameRequired(): void { $this->setError(AuthError::CODE_USERNAME_REQUIRED); } - protected function setUserAttributes($attributes) + protected function setUserAttributes($attributes): void { $this->userAttributes = $attributes; } diff --git a/modules/silauth/lib/Auth/Source/auth/IdBroker.php b/modules/silauth/lib/Auth/Source/auth/IdBroker.php index 78f1cbd4..d542efa5 100644 --- a/modules/silauth/lib/Auth/Source/auth/IdBroker.php +++ b/modules/silauth/lib/Auth/Source/auth/IdBroker.php @@ -3,17 +3,17 @@ use Psr\Log\LoggerInterface; use Sil\Idp\IdBroker\Client\IdBrokerClient; +use Sil\SspBase\Features\fakes\FakeIdBrokerClient; use SimpleSAML\Module\silauth\Auth\Source\saml\User as SamlUser; class IdBroker { - /** @var IdBrokerClient */ - protected $client; + protected IdBrokerClient|FakeIdBrokerClient $client; /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; - protected $idpDomainName; + protected string $idpDomainName; /** * @@ -64,7 +64,7 @@ public function __construct( * @return array|null The user's attributes (if successful), otherwise null. * @throws \Exception */ - public function getAuthenticatedUser(string $username, string $password) + public function getAuthenticatedUser(string $username, string $password): ?array { $rpOrigin = 'https://' . $this->idpDomainName; $userInfo = $this->client->authenticate($username, $password, $rpOrigin); @@ -102,7 +102,7 @@ public function getAuthenticatedUser(string $username, string $password) * @return string "OK" * @throws Exception */ - public function getSiteStatus() + public function getSiteStatus(): string { return $this->client->getSiteStatus(); } diff --git a/modules/silauth/lib/Auth/Source/captcha/Captcha.php b/modules/silauth/lib/Auth/Source/captcha/Captcha.php index bffc6d46..3d001b8d 100644 --- a/modules/silauth/lib/Auth/Source/captcha/Captcha.php +++ b/modules/silauth/lib/Auth/Source/captcha/Captcha.php @@ -5,14 +5,14 @@ class Captcha { - private $secret; + private ?string $secret; - public function __construct($secret = null) + public function __construct(?string $secret = null) { $this->secret = $secret; } - public function isValidIn(Request $request) + public function isValidIn(Request $request): bool { if (empty($this->secret)) { throw new \RuntimeException('No captcha secret available.', 1487342411); diff --git a/modules/silauth/lib/Auth/Source/config/ConfigManager.php b/modules/silauth/lib/Auth/Source/config/ConfigManager.php index 0e95ecb7..553cac78 100644 --- a/modules/silauth/lib/Auth/Source/config/ConfigManager.php +++ b/modules/silauth/lib/Auth/Source/config/ConfigManager.php @@ -2,6 +2,7 @@ namespace SimpleSAML\Module\silauth\Auth\Source\config; use SimpleSAML\Module\silauth\Auth\Source\text\Text; +use yii\console\Application; class ConfigManager { @@ -12,7 +13,7 @@ class ConfigManager * * @return array */ - public static function getSspConfig() + public static function getSspConfig(): array { return require __DIR__ . '/ssp-config.php'; } @@ -26,7 +27,7 @@ public static function getSspConfig() * prefix will have been removed, so 'mysql.database' will be returned * as 'database', etc. */ - public static function getSspConfigFor($category) + public static function getSspConfigFor(string $category): array { return self::getConfigFor($category, self::getSspConfig()); } @@ -41,7 +42,7 @@ public static function getSspConfigFor($category) * prefix will have been removed, so 'mysql.database' will be returned * as 'database', etc. */ - public static function getConfigFor($category, $config) + public static function getConfigFor(string $category, array $config): array { $categoryPrefix = $category . self::SEPARATOR; $categoryConfig = []; @@ -60,7 +61,7 @@ public static function getConfigFor($category, $config) * @param array $customConfig * @return array */ - public static function getMergedYii2Config($customConfig) + public static function getMergedYii2Config(array $customConfig): array { $defaultConfig = require __DIR__ . '/yii2-config.php'; return array_replace_recursive( @@ -69,21 +70,21 @@ public static function getMergedYii2Config($customConfig) ); } - private static function initializeYiiClass() + private static function initializeYiiClass(): void { if ( ! class_exists('Yii')) { require_once __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php'; } } - public static function getYii2ConsoleApp($customConfig) + public static function getYii2ConsoleApp(array $customConfig): Application { self::initializeYiiClass(); $mergedYii2Config = self::getMergedYii2Config($customConfig); - return new \yii\console\Application($mergedYii2Config); + return new Application($mergedYii2Config); } - public static function initializeYii2WebApp($customConfig = []) + public static function initializeYii2WebApp(array $customConfig = []): void { self::initializeYiiClass(); @@ -99,7 +100,7 @@ public static function initializeYii2WebApp($customConfig = []) $app->log->getLogger(); } - public static function removeCategory($key) + public static function removeCategory($key): bool|string|null { if ($key === null) { return null; diff --git a/modules/silauth/lib/Auth/Source/csrf/CsrfProtector.php b/modules/silauth/lib/Auth/Source/csrf/CsrfProtector.php index b913d51f..966b36ed 100644 --- a/modules/silauth/lib/Auth/Source/csrf/CsrfProtector.php +++ b/modules/silauth/lib/Auth/Source/csrf/CsrfProtector.php @@ -9,9 +9,9 @@ */ class CsrfProtector { - protected $csrfSessionKey = 'silauth.csrfToken'; - protected $csrfTokenDataType = 'string'; - private $session; + protected string $csrfSessionKey = 'silauth.csrfToken'; + protected string $csrfTokenDataType = 'string'; + private Session $session; /** * Constructor. @@ -23,13 +23,13 @@ public function __construct(Session $session) $this->session = $session; } - public function changeMasterToken() + public function changeMasterToken(): void { $newMasterToken = $this->generateToken(); $this->setTokenInSession($newMasterToken); } - protected function generateToken() + protected function generateToken(): string { return bin2hex(random_bytes(32)); } @@ -40,7 +40,7 @@ protected function generateToken() * * @return string The master (aka. authoritative) CSRF token. */ - public function getMasterToken() + public function getMasterToken(): string { $masterToken = $this->getTokenFromSession(); if (empty($masterToken)) { @@ -50,7 +50,7 @@ public function getMasterToken() return $masterToken; } - protected function getTokenFromSession() + protected function getTokenFromSession(): mixed { return $this->session->getData( $this->csrfTokenDataType, @@ -65,12 +65,12 @@ protected function getTokenFromSession() * HTTP request. * @return bool */ - public function isTokenCorrect($submittedToken) + public function isTokenCorrect(string $submittedToken): bool { return hash_equals($this->getMasterToken(), $submittedToken); } - protected function setTokenInSession($masterToken) + protected function setTokenInSession(string $masterToken): void { $this->session->setData( $this->csrfTokenDataType, diff --git a/modules/silauth/lib/Auth/Source/http/Request.php b/modules/silauth/lib/Auth/Source/http/Request.php index fe6f242c..fda2d155 100644 --- a/modules/silauth/lib/Auth/Source/http/Request.php +++ b/modules/silauth/lib/Auth/Source/http/Request.php @@ -12,14 +12,14 @@ class Request * * @var IP[] */ - private $trustedIpAddresses = []; + private array $trustedIpAddresses = []; /** * The list of trusted IP address ranges (aka. blocks). * * @var IPBlock[] */ - private $trustedIpAddressRanges = []; + private array $trustedIpAddressRanges = []; /** * Constructor. @@ -39,7 +39,7 @@ public function __construct(array $ipAddressesToTrust = []) } } - public function getCaptchaResponse() + public function getCaptchaResponse(): string { return self::sanitizeInputString(INPUT_POST, 'g-recaptcha-response'); } @@ -53,7 +53,7 @@ public function getCaptchaResponse() * * @return string[] A list of IP addresses. */ - public function getIpAddresses() + public function getIpAddresses(): array { $ipAddresses = []; @@ -84,7 +84,7 @@ public function getIpAddresses() * * @return string|null An IP address, or null if none was available. */ - public function getMostLikelyIpAddress() + public function getMostLikelyIpAddress(): ?string { $untrustedIpAddresses = $this->getUntrustedIpAddresses(); @@ -117,13 +117,13 @@ public function getMostLikelyIpAddress() * @param string $variableName Example: 'username' * @return string */ - public static function getRawInputString(int $inputType, string $variableName) + public static function getRawInputString(int $inputType, string $variableName): string { $input = filter_input($inputType, $variableName); return is_string($input) ? $input : ''; } - public function getUntrustedIpAddresses() + public function getUntrustedIpAddresses(): array { $untrustedIpAddresses = []; foreach ($this->getIpAddresses() as $ipAddress) { @@ -139,7 +139,7 @@ public function getUntrustedIpAddresses() * * @return string The UA string, or an empty string if not found. */ - public static function getUserAgent() + public static function getUserAgent(): string { return self::sanitizeInputString(INPUT_SERVER, 'HTTP_USER_AGENT'); } @@ -151,7 +151,7 @@ public static function getUserAgent() * @param string $ipAddress The IP address in question. * @return bool */ - public function isTrustedIpAddress($ipAddress) + public function isTrustedIpAddress(string $ipAddress): bool { foreach ($this->trustedIpAddresses as $trustedIp) { if ($trustedIp->numeric() === IP::create($ipAddress)->numeric()) { @@ -174,7 +174,7 @@ public function isTrustedIpAddress($ipAddress) * @param string $ipAddress The IP address in question. * @return bool */ - public static function isValidIpAddress($ipAddress) + public static function isValidIpAddress(string $ipAddress): bool { $flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6; return (filter_var($ipAddress, FILTER_VALIDATE_IP, $flags) !== false); @@ -188,12 +188,12 @@ public static function isValidIpAddress($ipAddress) * @param string $variableName Example: 'username' * @return string */ - public static function sanitizeInputString(int $inputType, string $variableName) + public static function sanitizeInputString(int $inputType, string $variableName): string { return Text::sanitizeString(filter_input($inputType, $variableName)); } - public function trustIpAddress($ipAddress) + public function trustIpAddress(string $ipAddress): void { if ( ! self::isValidIpAddress($ipAddress)) { throw new \InvalidArgumentException(sprintf( @@ -204,7 +204,7 @@ public function trustIpAddress($ipAddress) $this->trustedIpAddresses[] = IP::create($ipAddress); } - public function trustIpAddressRange($ipAddressRangeString) + public function trustIpAddressRange(string $ipAddressRangeString): void { $ipBlock = IPBlock::create($ipAddressRangeString); $this->trustedIpAddressRanges[] = $ipBlock; diff --git a/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php b/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php index bc0dab3d..efd4a2b3 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php +++ b/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php @@ -6,7 +6,7 @@ class M161213135750CreateInitialTables extends Migration { - public function safeUp() + public function safeUp(): void { $this->createTable('{{user}}', [ 'id' => 'pk', @@ -45,7 +45,7 @@ public function safeUp() ); } - public function safeDown() + public function safeDown(): void { $this->dropForeignKey( 'fk_prev_pw_user_user_id', diff --git a/modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php b/modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php index c9d38f96..c1075306 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php +++ b/modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php @@ -6,14 +6,14 @@ class M161213150831SwitchToUtcForDateTimes extends Migration { - public function safeUp() + public function safeUp(): void { $this->renameColumn('{{user}}', 'block_until', 'block_until_utc'); $this->renameColumn('{{user}}', 'last_updated', 'last_updated_utc'); $this->renameColumn('{{previous_password}}', 'created', 'created_utc'); } - public function safeDown() + public function safeDown(): void { $this->renameColumn('{{previous_password}}', 'created_utc', 'created'); $this->renameColumn('{{user}}', 'last_updated_utc', 'last_updated'); diff --git a/modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php b/modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php index acba2e8a..169a2117 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php +++ b/modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php @@ -6,7 +6,7 @@ class M170214141109CreateFailedLoginsTable extends Migration { - public function safeUp() + public function safeUp(): void { /* The max length needed to store an IP address is 45 characters. See * http://stackoverflow.com/a/1076755/3813891 for details. */ @@ -30,7 +30,7 @@ public function safeUp() ); } - public function safeDown() + public function safeDown(): void { $this->dropIndex('idx_failed_logins_ip_address', '{{failed_logins}}'); $this->dropIndex('idx_failed_logins_username', '{{failed_logins}}'); diff --git a/modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php b/modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php index 0b407f9d..a2c327c2 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php +++ b/modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php @@ -6,7 +6,7 @@ class M170214145629RemoveOldTables extends Migration { - public function safeUp() + public function safeUp(): void { $this->dropForeignKey( 'fk_prev_pw_user_user_id', @@ -21,7 +21,7 @@ public function safeUp() $this->dropTable('{{user}}'); } - public function safeDown() + public function safeDown(): bool { echo "M170214145629RemoveOldTables cannot be reverted.\n"; diff --git a/modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php b/modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php index 305e21fe..4d894c1d 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php +++ b/modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php @@ -6,7 +6,7 @@ class M170215141724SplitFailedLoginsTable extends Migration { - public function safeUp() + public function safeUp(): void { // Remove old indexes. $this->dropIndex('idx_failed_logins_ip_address', '{{failed_logins}}'); @@ -36,7 +36,7 @@ public function safeUp() ); } - public function safeDown() + public function safeDown(): bool { echo "M170215141724SplitFailedLoginsTable cannot be reverted.\n"; return false; diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php index ce192668..e62a0ef0 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php @@ -18,7 +18,7 @@ class FailedLoginIpAddress extends FailedLoginIpAddressBase implements LoggerAwa /** * @inheritdoc */ - public function attributeLabels() + public function attributeLabels(): array { return ArrayHelper::merge(parent::attributeLabels(), [ 'ip_address' => Yii::t('app', 'IP Address'), @@ -26,7 +26,7 @@ public function attributeLabels() ]); } - public function behaviors() + public function behaviors(): array { return [ [ @@ -38,7 +38,7 @@ public function behaviors() ]; } - public static function countRecentFailedLoginsFor($ipAddress) + public static function countRecentFailedLoginsFor(string $ipAddress): string|int|bool|null { return self::find()->where([ 'ip_address' => strtolower($ipAddress), @@ -47,7 +47,7 @@ public static function countRecentFailedLoginsFor($ipAddress) ])->count(); } - public static function getFailedLoginsFor($ipAddress) + public static function getFailedLoginsFor(string $ipAddress): array { if ( ! Request::isValidIpAddress($ipAddress)) { throw new \InvalidArgumentException(sprintf( @@ -66,7 +66,7 @@ public static function getFailedLoginsFor($ipAddress) * @param string $ipAddress The IP address. * @return FailedLoginIpAddress|null */ - public static function getMostRecentFailedLoginFor($ipAddress) + public static function getMostRecentFailedLoginFor(string $ipAddress): ?FailedLoginIpAddress { return self::find()->where([ 'ip_address' => strtolower($ipAddress), @@ -83,7 +83,7 @@ public static function getMostRecentFailedLoginFor($ipAddress) * @param string $ipAddress The IP address in question * @return int The number of seconds */ - public static function getSecondsUntilUnblocked($ipAddress) + public static function getSecondsUntilUnblocked(string $ipAddress): int { $failedLogin = self::getMostRecentFailedLoginFor($ipAddress); @@ -93,20 +93,20 @@ public static function getSecondsUntilUnblocked($ipAddress) ); } - public function init() + public function init(): void { $this->initializeLogger(); parent::init(); } - public static function isCaptchaRequiredFor($ipAddress) + public static function isCaptchaRequiredFor(string $ipAddress): bool { return Authenticator::isEnoughFailedLoginsToRequireCaptcha( self::countRecentFailedLoginsFor($ipAddress) ); } - public static function isCaptchaRequiredForAnyOfThese(array $ipAddresses) + public static function isCaptchaRequiredForAnyOfThese(array $ipAddresses): bool { foreach ($ipAddresses as $ipAddress) { if (self::isCaptchaRequiredFor($ipAddress)) { @@ -116,13 +116,13 @@ public static function isCaptchaRequiredForAnyOfThese(array $ipAddresses) return false; } - public static function isRateLimitBlocking($ipAddress) + public static function isRateLimitBlocking(string $ipAddress): bool { $secondsUntilUnblocked = self::getSecondsUntilUnblocked($ipAddress); return ($secondsUntilUnblocked > 0); } - public static function isRateLimitBlockingAnyOfThese($ipAddresses) + public static function isRateLimitBlockingAnyOfThese(array $ipAddresses): bool { foreach ($ipAddresses as $ipAddress) { if (self::isRateLimitBlocking($ipAddress)) { @@ -135,7 +135,8 @@ public static function isRateLimitBlockingAnyOfThese($ipAddresses) public static function recordFailedLoginBy( array $ipAddresses, LoggerInterface $logger - ) { + ): void + { foreach ($ipAddresses as $ipAddress) { $newRecord = new FailedLoginIpAddress(['ip_address' => strtolower($ipAddress)]); @@ -150,7 +151,7 @@ public static function recordFailedLoginBy( } } - public static function resetFailedLoginsBy(array $ipAddresses) + public static function resetFailedLoginsBy(array $ipAddresses): void { foreach ($ipAddresses as $ipAddress) { self::deleteAll(['ip_address' => strtolower($ipAddress)]); diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddressBase.php b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddressBase.php index 0675e2e0..25f2b5b1 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddressBase.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddressBase.php @@ -16,7 +16,7 @@ class FailedLoginIpAddressBase extends \yii\db\ActiveRecord /** * @inheritdoc */ - public static function tableName() + public static function tableName(): string { return 'failed_login_ip_address'; } @@ -24,7 +24,7 @@ public static function tableName() /** * @inheritdoc */ - public function rules() + public function rules(): array { return [ [['ip_address', 'occurred_at_utc'], 'required'], @@ -36,7 +36,7 @@ public function rules() /** * @inheritdoc */ - public function attributeLabels() + public function attributeLabels(): array { return [ 'id' => Yii::t('app', 'ID'), diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php index 771132fc..26615487 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php @@ -17,14 +17,14 @@ class FailedLoginUsername extends FailedLoginUsernameBase implements LoggerAware /** * @inheritdoc */ - public function attributeLabels() + public function attributeLabels(): array { return ArrayHelper::merge(parent::attributeLabels(), [ 'occurred_at_utc' => Yii::t('app', 'Occurred At (UTC)'), ]); } - public function behaviors() + public function behaviors(): array { return [ [ @@ -36,7 +36,7 @@ public function behaviors() ]; } - public static function countRecentFailedLoginsFor($username) + public static function countRecentFailedLoginsFor(string $username): bool|int|string|null { return self::find()->where([ 'username' => strtolower($username), @@ -51,7 +51,7 @@ public static function countRecentFailedLoginsFor($username) * @param string $username The username. * @return FailedLoginUsername[] An array of any matching records. */ - public static function getFailedLoginsFor($username) + public static function getFailedLoginsFor(string $username): array { return self::findAll(['username' => strtolower($username)]); } @@ -63,7 +63,7 @@ public static function getFailedLoginsFor($username) * @param string $username The username. * @return FailedLoginUsername|null */ - public static function getMostRecentFailedLoginFor($username) + public static function getMostRecentFailedLoginFor(string $username): ?FailedLoginUsername { return self::find()->where([ 'username' => strtolower($username), @@ -80,7 +80,7 @@ public static function getMostRecentFailedLoginFor($username) * @param string $username The username in question * @return int The number of seconds */ - public static function getSecondsUntilUnblocked($username) + public static function getSecondsUntilUnblocked(string $username): int { $failedLogin = self::getMostRecentFailedLoginFor($username); @@ -90,7 +90,7 @@ public static function getSecondsUntilUnblocked($username) ); } - public function init() + public function init(): void { $this->initializeLogger(); parent::init(); @@ -102,13 +102,13 @@ public function init() * @param string $username The username * @return bool */ - public static function isRateLimitBlocking($username) + public static function isRateLimitBlocking(string $username): bool { $secondsUntilUnblocked = self::getSecondsUntilUnblocked($username); return ($secondsUntilUnblocked > 0); } - public static function isCaptchaRequiredFor($username) + public static function isCaptchaRequiredFor(string $username): bool { if (empty($username)) { return false; @@ -119,9 +119,10 @@ public static function isCaptchaRequiredFor($username) } public static function recordFailedLoginBy( - $username, + string $username, LoggerInterface $logger - ) { + ): void + { $newRecord = new FailedLoginUsername(['username' => strtolower($username)]); if ( ! $newRecord->save()) { $logger->critical(json_encode([ @@ -133,7 +134,7 @@ public static function recordFailedLoginBy( } } - public static function resetFailedLoginsBy($username) + public static function resetFailedLoginsBy(string $username): void { self::deleteAll(['username' => strtolower($username)]); } diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginUsernameBase.php b/modules/silauth/lib/Auth/Source/models/FailedLoginUsernameBase.php index a774ed47..a3c32cae 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginUsernameBase.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginUsernameBase.php @@ -16,7 +16,7 @@ class FailedLoginUsernameBase extends \yii\db\ActiveRecord /** * @inheritdoc */ - public static function tableName() + public static function tableName(): string { return 'failed_login_username'; } @@ -24,7 +24,7 @@ public static function tableName() /** * @inheritdoc */ - public function rules() + public function rules(): array { return [ [['username', 'occurred_at_utc'], 'required'], @@ -36,7 +36,7 @@ public function rules() /** * @inheritdoc */ - public function attributeLabels() + public function attributeLabels(): array { return [ 'id' => Yii::t('app', 'ID'), diff --git a/modules/silauth/lib/Auth/Source/saml/User.php b/modules/silauth/lib/Auth/Source/saml/User.php index 5986f5d1..864fe118 100644 --- a/modules/silauth/lib/Auth/Source/saml/User.php +++ b/modules/silauth/lib/Auth/Source/saml/User.php @@ -11,13 +11,14 @@ public static function convertToSamlFieldNames( string $email, string $uuid, string $idpDomainName, - $passwordExpirationDate, + ?string $passwordExpirationDate, array $mfa, array $method, - $managerEmail, - $profileReview, + ?string $managerEmail, + string $profileReview, array $member - ) { + ): array + { // eduPersonUniqueId (only alphanumeric allowed) $alphaNumericUuid = str_replace('-', '', $uuid); diff --git a/modules/silauth/lib/Auth/Source/system/System.php b/modules/silauth/lib/Auth/Source/system/System.php index ea576186..cfe11950 100644 --- a/modules/silauth/lib/Auth/Source/system/System.php +++ b/modules/silauth/lib/Auth/Source/system/System.php @@ -11,19 +11,19 @@ class System { - protected $logger; + protected LoggerInterface|NullLogger $logger; /** * Constructor. * * @param LoggerInterface|null $logger (Optional:) A PSR-3 compatible logger. */ - public function __construct($logger = null) + public function __construct(LoggerInterface $logger = null) { $this->logger = $logger ?? new NullLogger(); } - protected function isDatabaseOkay() + protected function isDatabaseOkay(): bool { try { FailedLoginIpAddress::getMostRecentFailedLoginFor(''); @@ -34,7 +34,7 @@ protected function isDatabaseOkay() } } - protected function isRequiredConfigPresent() + protected function isRequiredConfigPresent(): bool { $globalConfig = Configuration::getInstance(); @@ -57,7 +57,7 @@ protected function isRequiredConfigPresent() * * @throws \Exception */ - public function reportStatus() + public function reportStatus(): void { if ( ! $this->isRequiredConfigPresent()) { $this->reportError('Config problem', 1485984755); @@ -75,7 +75,7 @@ public function reportStatus() * * @param string $message The error message. */ - protected function logError($message) + protected function logError(string $message): void { $this->logger->error($message); } @@ -88,7 +88,7 @@ protected function logError($message) * @param int $code An error code. * @throws \Exception */ - protected function reportError($message, $code) + protected function reportError(string $message, int $code): void { $this->logError($message); throw new \Exception($message, $code); diff --git a/modules/silauth/lib/Auth/Source/tests/fakes/FakeFailedIdBroker.php b/modules/silauth/lib/Auth/Source/tests/fakes/FakeFailedIdBroker.php index a2a04fc7..aee14acd 100644 --- a/modules/silauth/lib/Auth/Source/tests/fakes/FakeFailedIdBroker.php +++ b/modules/silauth/lib/Auth/Source/tests/fakes/FakeFailedIdBroker.php @@ -5,7 +5,7 @@ class FakeFailedIdBroker extends FakeIdBroker { - public function getAuthenticatedUser(string $username, string $password) + public function getAuthenticatedUser(string $username, string $password): ?array { $this->logger->info('FAKE FAILURE: rejecting {username} and {password}.', [ 'username' => var_export($username, true), @@ -14,7 +14,7 @@ public function getAuthenticatedUser(string $username, string $password) return parent::getAuthenticatedUser($username, $password); } - protected function getDesiredResponse() + protected function getDesiredResponse(): Response { return new Response(400); } diff --git a/modules/silauth/lib/Auth/Source/tests/fakes/FakeInvalidIdBroker.php b/modules/silauth/lib/Auth/Source/tests/fakes/FakeInvalidIdBroker.php index 8e54e8f7..a22c85c1 100644 --- a/modules/silauth/lib/Auth/Source/tests/fakes/FakeInvalidIdBroker.php +++ b/modules/silauth/lib/Auth/Source/tests/fakes/FakeInvalidIdBroker.php @@ -5,13 +5,13 @@ class FakeInvalidIdBroker extends FakeIdBroker { - public function getAuthenticatedUser(string $username, string $password) + public function getAuthenticatedUser(string $username, string $password): ?array { $this->logger->info('FAKE ERROR: invalid/unexpected response.'); return parent::getAuthenticatedUser($username, $password); } - protected function getDesiredResponse() + protected function getDesiredResponse(): Response { return new Response(404); } diff --git a/modules/silauth/lib/Auth/Source/tests/fakes/FakeSuccessfulIdBroker.php b/modules/silauth/lib/Auth/Source/tests/fakes/FakeSuccessfulIdBroker.php index 21d36b25..d385fa11 100644 --- a/modules/silauth/lib/Auth/Source/tests/fakes/FakeSuccessfulIdBroker.php +++ b/modules/silauth/lib/Auth/Source/tests/fakes/FakeSuccessfulIdBroker.php @@ -5,7 +5,7 @@ class FakeSuccessfulIdBroker extends FakeIdBroker { - public function getAuthenticatedUser(string $username, string $password) + public function getAuthenticatedUser(string $username, string $password): ?array { $this->logger->info('FAKE SUCCESS: accepting {username} and {password}.', [ 'username' => var_export($username, true), @@ -14,7 +14,7 @@ public function getAuthenticatedUser(string $username, string $password) return parent::getAuthenticatedUser($username, $password); } - protected function getDesiredResponse() + protected function getDesiredResponse(): Response { return new Response(200, [], json_encode([ 'uuid' => '11111111-aaaa-1111-aaaa-111111111111', diff --git a/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummyFailedCaptcha.php b/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummyFailedCaptcha.php index b6d5387c..9ddb58e6 100644 --- a/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummyFailedCaptcha.php +++ b/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummyFailedCaptcha.php @@ -6,7 +6,7 @@ class DummyFailedCaptcha extends Captcha { - public function isValidIn(Request $request) + public function isValidIn(Request $request): bool { return false; } diff --git a/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummySuccessfulCaptcha.php b/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummySuccessfulCaptcha.php index 7a68b3d4..1bdfded2 100644 --- a/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummySuccessfulCaptcha.php +++ b/modules/silauth/lib/Auth/Source/tests/unit/captcha/DummySuccessfulCaptcha.php @@ -6,7 +6,7 @@ class DummySuccessfulCaptcha extends Captcha { - public function isValidIn(Request $request) + public function isValidIn(Request $request): bool { return true; } diff --git a/modules/silauth/lib/Auth/Source/tests/unit/http/DummyRequest.php b/modules/silauth/lib/Auth/Source/tests/unit/http/DummyRequest.php index ae2cfbf7..f2334110 100644 --- a/modules/silauth/lib/Auth/Source/tests/unit/http/DummyRequest.php +++ b/modules/silauth/lib/Auth/Source/tests/unit/http/DummyRequest.php @@ -12,12 +12,12 @@ class DummyRequest extends Request * * @return string[] A list containing the dummy IP address. */ - public function getIpAddresses() + public function getIpAddresses(): array { return [$this->dummyIpAddress]; } - public function setDummyIpAddress($dummyIpAddress) + public function setDummyIpAddress(string $dummyIpAddress): void { if ( ! self::isValidIpAddress($dummyIpAddress)) { throw new \InvalidArgumentException(sprintf( diff --git a/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginIpAddressTest.php b/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginIpAddressTest.php index 84f2163d..8b1c3daf 100644 --- a/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginIpAddressTest.php +++ b/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginIpAddressTest.php @@ -9,7 +9,7 @@ class FailedLoginIpAddressTest extends TestCase { - protected function setDbFixture($recordsData) + protected function setDbFixture(array $recordsData): void { FailedLoginIpAddress::deleteAll(); foreach ($recordsData as $recordData) { diff --git a/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginUsernameTest.php b/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginUsernameTest.php index bd403eff..b5b571c2 100644 --- a/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginUsernameTest.php +++ b/modules/silauth/lib/Auth/Source/tests/unit/models/FailedLoginUsernameTest.php @@ -9,7 +9,7 @@ class FailedLoginUsernameTest extends TestCase { - protected function setDbFixture($recordsData) + protected function setDbFixture(array $recordsData): void { FailedLoginUsername::deleteAll(); foreach ($recordsData as $recordData) { diff --git a/modules/silauth/lib/Auth/Source/text/Text.php b/modules/silauth/lib/Auth/Source/text/Text.php index eed1282c..6649ca65 100644 --- a/modules/silauth/lib/Auth/Source/text/Text.php +++ b/modules/silauth/lib/Auth/Source/text/Text.php @@ -10,7 +10,7 @@ class Text * @param string|mixed $input The input. * @return string The sanitized string. */ - public static function sanitizeString($input) + public static function sanitizeString(mixed $input): string { $inputAsString = is_string($input) ? $input : ''; $output = filter_var($inputAsString, FILTER_SANITIZE_STRING, [ @@ -26,7 +26,7 @@ public static function sanitizeString($input) * @param string $needle The string to search for. * @return boolean */ - public static function startsWith(string $haystack, string $needle) + public static function startsWith(string $haystack, string $needle): bool { $length = mb_strlen($needle); return (mb_substr($haystack, 0, $length) === $needle); diff --git a/modules/silauth/lib/Auth/Source/time/UtcTime.php b/modules/silauth/lib/Auth/Source/time/UtcTime.php index af727882..e269c14e 100644 --- a/modules/silauth/lib/Auth/Source/time/UtcTime.php +++ b/modules/silauth/lib/Auth/Source/time/UtcTime.php @@ -39,7 +39,7 @@ public function __toString() * @throws Exception If an invalid date/time string is provided, an * \Exception will be thrown. */ - public static function format(string $dateTimeString = 'now') + public static function format(string $dateTimeString = 'now'): string { return (string)(new UtcTime($dateTimeString)); } @@ -55,7 +55,7 @@ public static function format(string $dateTimeString = 'now') * passed. * @return int The number of seconds remaining. */ - public static function getRemainingSeconds(int $totalSeconds, int $elapsedSeconds) + public static function getRemainingSeconds(int $totalSeconds, int $elapsedSeconds): int { $remainingSeconds = $totalSeconds - $elapsedSeconds; return max($remainingSeconds, 0); @@ -71,7 +71,7 @@ public static function getRemainingSeconds(int $totalSeconds, int $elapsedSecond * (presumably in the past, though not necessarily). * @return int The number of seconds */ - public function getSecondsSince(UtcTime $otherUtcTime) + public function getSecondsSince(UtcTime $otherUtcTime): int { return $this->getTimestamp() - $otherUtcTime->getTimestamp(); } @@ -85,7 +85,7 @@ public function getSecondsSince(UtcTime $otherUtcTime) * \Exception will be thrown. * @throws \InvalidArgumentException */ - public static function getSecondsSinceDateTime(string $dateTimeString) + public static function getSecondsSinceDateTime(string $dateTimeString): int { if (empty($dateTimeString)) { throw new \InvalidArgumentException(sprintf( @@ -98,12 +98,12 @@ public static function getSecondsSinceDateTime(string $dateTimeString) return $nowUtc->getSecondsSince($dateTimeUtc); } - public function getSecondsUntil(UtcTime $otherUtcTime) + public function getSecondsUntil(UtcTime $otherUtcTime): int { return $otherUtcTime->getTimestamp() - $this->getTimestamp(); } - public function getTimestamp() + public function getTimestamp(): int { return $this->dateTime->getTimestamp(); } @@ -113,7 +113,7 @@ public function getTimestamp() * * @return string */ - public static function now() + public static function now(): string { return self::format('now'); } diff --git a/modules/silauth/lib/Auth/Source/time/WaitTime.php b/modules/silauth/lib/Auth/Source/time/WaitTime.php index ba5094f3..216b46ba 100644 --- a/modules/silauth/lib/Auth/Source/time/WaitTime.php +++ b/modules/silauth/lib/Auth/Source/time/WaitTime.php @@ -11,8 +11,8 @@ class WaitTime const UNIT_MINUTE = 'minute'; const UNIT_SECOND = 'second'; - private $friendlyNumber = null; - private $unit = null; + private int $friendlyNumber; + private string $unit; /** * Constructor. @@ -22,7 +22,7 @@ class WaitTime * * @param int $secondsToWait The number of seconds the user must wait. */ - public function __construct($secondsToWait) + public function __construct(int $secondsToWait) { if ($secondsToWait <= 5) { $this->friendlyNumber = 5; @@ -36,7 +36,7 @@ public function __construct($secondsToWait) } } - public function getFriendlyNumber() + public function getFriendlyNumber(): int { return $this->friendlyNumber; } @@ -48,7 +48,7 @@ public function getFriendlyNumber() * seconds. * @return WaitTime */ - public static function getLongestWaitTime(array $durationsInSeconds) + public static function getLongestWaitTime(array $durationsInSeconds): WaitTime { if (empty($durationsInSeconds)) { throw new \InvalidArgumentException('No durations given.', 1487605801); @@ -56,7 +56,7 @@ public static function getLongestWaitTime(array $durationsInSeconds) return new WaitTime(max($durationsInSeconds)); } - public function getUnit() + public function getUnit(): string { return $this->unit; } diff --git a/modules/silauth/lib/Auth/Source/traits/LoggerAwareTrait.php b/modules/silauth/lib/Auth/Source/traits/LoggerAwareTrait.php index 65fabe5a..c07c72c8 100644 --- a/modules/silauth/lib/Auth/Source/traits/LoggerAwareTrait.php +++ b/modules/silauth/lib/Auth/Source/traits/LoggerAwareTrait.php @@ -7,9 +7,9 @@ trait LoggerAwareTrait { /** @var LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; - public function initializeLogger() + public function initializeLogger(): void { if (empty($this->logger)) { $this->logger = new NullLogger(); @@ -22,7 +22,7 @@ public function initializeLogger() * @param LoggerInterface $logger A PSR-3 compliant logger. * @return null */ - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } diff --git a/modules/sildisco/lib/Auth/Process/AddIdp2NameId.php b/modules/sildisco/lib/Auth/Process/AddIdp2NameId.php index 0f816211..e3c8408e 100644 --- a/modules/sildisco/lib/Auth/Process/AddIdp2NameId.php +++ b/modules/sildisco/lib/Auth/Process/AddIdp2NameId.php @@ -2,6 +2,7 @@ namespace SimpleSAML\Module\sildisco\Auth\Process; +use SAML2\XML\saml\NameID; use Sil\SspUtils\Metadata; /** @@ -39,7 +40,7 @@ class AddIdp2NameId extends \SimpleSAML\Auth\ProcessingFilter { * * @var string|bool */ - private $nameQualifier; + private string|bool $nameQualifier; /** @@ -51,7 +52,7 @@ class AddIdp2NameId extends \SimpleSAML\Auth\ProcessingFilter { * * @var string|bool */ - private $spNameQualifier; + private sring|bool $spNameQualifier; /** @@ -61,7 +62,7 @@ class AddIdp2NameId extends \SimpleSAML\Auth\ProcessingFilter { * * @var string */ - protected $format; + protected ?string $format; /** @@ -70,7 +71,7 @@ class AddIdp2NameId extends \SimpleSAML\Auth\ProcessingFilter { * @param array $config Configuration information about this filter. * @param mixed $reserved For future use. */ - public function __construct($config, $reserved) { + public function __construct(array $config, mixed $reserved) { parent::__construct($config, $reserved); assert('is_array($config)'); @@ -93,12 +94,13 @@ public function __construct($config, $reserved) { } /** - * @param $nameId \SAML2\XML\saml\NameID + * @param $nameId NameID * @param $IDPNamespace string * * Modifies the nameID object by adding text to the end of its value attribute */ - public function appendIdp($nameId, $IDPNamespace) { + public function appendIdp(NameID $nameId, string $IDPNamespace): void + { $suffix = self::DELIMITER . $IDPNamespace; $value = $nameId->getValue(); @@ -112,7 +114,8 @@ public function appendIdp($nameId, $IDPNamespace) { * * @param array &$state The current state array */ - public function process(&$state) { + public function process(&$state): void + { assert('is_array($state)'); $samlIDP = $state[self::IDP_KEY]; diff --git a/modules/sildisco/lib/Auth/Process/LogUser.php b/modules/sildisco/lib/Auth/Process/LogUser.php index c1cc2a55..ce65fd29 100644 --- a/modules/sildisco/lib/Auth/Process/LogUser.php +++ b/modules/sildisco/lib/Auth/Process/LogUser.php @@ -38,13 +38,13 @@ class LogUser extends \SimpleSAML\Auth\ProcessingFilter // The host of the aws dynamodb - private $dynamoEndpoint; + private ?string $dynamoEndpoint; // The region of the aws dynamodb - private $dynamoRegion; + private ?string $dynamoRegion; // The name of the aws dynamodb table that stores the login data - private $dynamoLogTable; + private ?string $dynamoLogTable; /** * Initialize this filter, parse configuration. @@ -52,9 +52,9 @@ class LogUser extends \SimpleSAML\Auth\ProcessingFilter * @param array $config Configuration information about this filter. * @param mixed $reserved For future use. */ - public function __construct($config, $reserved) { + public function __construct(array $config, mixed $reserved) + { parent::__construct($config, $reserved); - assert(is_array($config)); $this->dynamoEndpoint = $config[self::DYNAMO_ENDPOINT_KEY] ?? null; $this->dynamoRegion = $config[self::DYNAMO_REGION_KEY] ?? null; @@ -62,11 +62,12 @@ public function __construct($config, $reserved) { } /** - * Log info for a user's login to Dyanmodb + * Log info for a user's login to Dynamodb * * @param array &$state The current state array */ - public function process(&$state) { + public function process(&$state): void + { if (! $this->configsAreValid()) { return; } @@ -137,7 +138,8 @@ public function process(&$state) { } } - private function configsAreValid() { + private function configsAreValid(): bool + { $msg = ' config value not provided to LogUser.'; if (empty($this->dynamoRegion)) { @@ -153,7 +155,8 @@ private function configsAreValid() { return true; } - private function getIdp(&$state) { + private function getIdp(array &$state) + { if (empty($state[self::IDP_KEY])) { return 'No IDP available'; } @@ -182,7 +185,8 @@ private function getIdp(&$state) { } // Get the current user's common name attribute and/or eduPersonPrincipalName and/or employeeNumber - private function getUserAttributes($state) { + private function getUserAttributes(array $state): array + { $attributes = $state['Attributes']; $cn = $this->getAttributeFrom($attributes, 'urn:oid:2.5.4.3', 'cn'); @@ -208,7 +212,8 @@ private function getUserAttributes($state) { return $userAttrs; } - private function getAttributeFrom($attributes, $oidKey, $friendlyKey) { + private function getAttributeFrom(array $attributes, string $oidKey, string $friendlyKey): string + { if (!empty($attributes[$oidKey])) { return $attributes[$oidKey][0]; } @@ -222,7 +227,7 @@ private function getAttributeFrom($attributes, $oidKey, $friendlyKey) { // Dynamodb seems to complain when a value is an empty string. // This ensures that only attributes with a non empty value get included. - private function addUserAttribute($attributes, $attrKey, $attr) { + private function addUserAttribute(array $attributes, string $attrKey, string $attr): array { if (!empty($attr)) { $attributes[$attrKey] = $attr; } diff --git a/modules/sildisco/lib/Auth/Process/TagGroup.php b/modules/sildisco/lib/Auth/Process/TagGroup.php index 59402907..5d8ccf68 100644 --- a/modules/sildisco/lib/Auth/Process/TagGroup.php +++ b/modules/sildisco/lib/Auth/Process/TagGroup.php @@ -16,7 +16,8 @@ class TagGroup extends \SimpleSAML\Auth\ProcessingFilter { const IDP_CODE_KEY = 'IDPNamespace'; - public function prependIdp2Groups($attributes, $attributeLabel, $idpLabel) { + public function prependIdp2Groups(array $attributes, string $attributeLabel, string $idpLabel): array + { $newGroups = []; $delimiter = '|'; diff --git a/modules/sildisco/lib/IdPDisco.php b/modules/sildisco/lib/IdPDisco.php index d1f735a8..4808a8ff 100644 --- a/modules/sildisco/lib/IdPDisco.php +++ b/modules/sildisco/lib/IdPDisco.php @@ -18,23 +18,23 @@ class IdPDisco extends \SimpleSAML\XHTML\IdPDisco { /* The session type for this class */ - public static $sessionType = 'sildisco:authentication'; + public static string $sessionType = 'sildisco:authentication'; /* The session key for checking if the current user has the beta_tester cookie */ - public static $betaTesterSessionKey = 'beta_tester'; + public static string $betaTesterSessionKey = 'beta_tester'; /* The idp metadata key that says whether an IDP is betaEnabled */ - public static $betaEnabledMdKey = 'betaEnabled'; + public static string $betaEnabledMdKey = 'betaEnabled'; /* The idp metadata key that says whether an IDP is enabled */ - public static $enabledMdKey = 'enabled'; + public static string $enabledMdKey = 'enabled'; /* The sp metadata key that gives the name of the SP */ - public static $spNameMdKey = 'name'; + public static string $spNameMdKey = 'name'; /* Used to get the SP Entity ID, e.g. $spEntityId = $this->session->getData($sessionDataType, $sessionKeyForSP); */ - public static $sessionDataType = 'sildisco:authentication'; - public static $sessionKeyForSP = 'spentityid'; + public static string $sessionDataType = 'sildisco:authentication'; + public static string $sessionKeyForSP = 'spentityid'; /** @@ -44,7 +44,7 @@ class IdPDisco extends \SimpleSAML\XHTML\IdPDisco * * @param string $message The message which should be logged. */ - protected function log($message) + protected function log($message): void { \SimpleSAML\Logger::info('SildiscoIdPDisco.'.$this->instance.': '.$message); } @@ -54,7 +54,7 @@ private function getMetadataPath() { return __DIR__ . '/../../../metadata/'; } - private function getSPEntityIDAndReducedIdpList() + private function getSPEntityIDAndReducedIdpList(): array { $idpList = $this->getIdPList(); @@ -76,7 +76,7 @@ private function getSPEntityIDAndReducedIdpList() * * The IdP disco parameters should be set before calling this function. */ - public function handleRequest() + public function handleRequest(): void { $this->start(); @@ -127,14 +127,15 @@ public function handleRequest() /** * @param array $idpList the IDPs with their metadata - * @param bool $isBetaTester optional (default=null) just for unit testing + * @param bool|null $isBetaTester optional (default=null) just for unit testing * @return array $idpList * * If the current user has the beta_tester cookie, then for each IDP in * the idpList that has 'betaEnabled' => true, give it 'enabled' => true * */ - public static function enableBetaEnabled($idpList, $isBetaTester=null) { + public static function enableBetaEnabled(array $idpList, ?bool $isBetaTester=null): array + { if ( $isBetaTester === null) { $session = \SimpleSAML\Session::getSessionFromRequest(); @@ -168,7 +169,7 @@ public static function enableBetaEnabled($idpList, $isBetaTester=null) { * * @return string|null The entity id if it is valid, null if not. */ - protected function validateIdP($idp) + protected function validateIdP($idp): ?string { if ($idp === null) { return null; @@ -203,7 +204,6 @@ protected function validateIdP($idp) return null; } - if (array_key_exists($idp, $idpList) && $idpList[$idp]['enabled']) { return $idp; } From adbc87ba04351c7bcc0a46c0a07f329af03eba96 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:15:04 +0400 Subject: [PATCH 25/46] update simpleSAMLphp files to version 1.19.8 --- composer.json | 2 - composer.lock | 299 +---------------------- dockerbuild/config/config.php | 29 ++- installed-packages.json | 28 +-- modules/sildisco/lib/Auth/Source/SP.php | 4 +- modules/sildisco/lib/IdP/SAML2.php | 6 +- modules/sildisco/www/disco.php | 2 +- modules/sildisco/www/sp/discoresp.php | 5 +- modules/sildisco/www/sp/saml2-acs.php | 4 +- modules/sildisco/www/sp/saml2-logout.php | 2 +- 10 files changed, 37 insertions(+), 344 deletions(-) diff --git a/composer.json b/composer.json index 9db562a0..19c2dddb 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,6 @@ "simplesamlphp/composer-module-installer": "1.1.8", "rlanvin/php-ip": "^1.0", "silinternational/ssp-utilities": "^1.1.0", - "silinternational/simplesamlphp-module-material": "^8.1.1", - "silinternational/simplesamlphp-module-sildisco": "^4.0.0", "silinternational/php-env": "^3.1.0", "silinternational/psr3-adapters": "^3.1", "silinternational/yii2-json-log-targets": "^2.0", diff --git a/composer.lock b/composer.lock index bfda34c2..4dd1c3ad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,157 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ae5996cffda6f7fd16dea5eb0e2a1665", + "content-hash": "9304b2b3f55f63eaa63c7ae2b6b44ea9", "packages": [ - { - "name": "aws/aws-crt-php", - "version": "v1.2.2", - "source": { - "type": "git", - "url": "https://github.com/awslabs/aws-crt-php.git", - "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/2f1dc7b7eda080498be96a4a6d683a41583030e9", - "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", - "yoast/phpunit-polyfills": "^1.0" - }, - "suggest": { - "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "AWS SDK Common Runtime Team", - "email": "aws-sdk-common-runtime@amazon.com" - } - ], - "description": "AWS Common Runtime for PHP", - "homepage": "https://github.com/awslabs/aws-crt-php", - "keywords": [ - "amazon", - "aws", - "crt", - "sdk" - ], - "support": { - "issues": "https://github.com/awslabs/aws-crt-php/issues", - "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.2" - }, - "time": "2023-07-20T16:49:55+00:00" - }, - { - "name": "aws/aws-sdk-php", - "version": "3.269.0", - "source": { - "type": "git", - "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", - "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", - "shasum": "" - }, - "require": { - "aws/aws-crt-php": "^1.0.4", - "ext-json": "*", - "ext-pcre": "*", - "ext-simplexml": "*", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "mtdowling/jmespath.php": "^2.6", - "php": ">=5.5" - }, - "require-dev": { - "andrewsville/php-token-reflection": "^1.4", - "aws/aws-php-sns-message-validator": "~1.0", - "behat/behat": "~3.0", - "composer/composer": "^1.10.22", - "dms/phpunit-arraysubset-asserts": "^0.4.0", - "doctrine/cache": "~1.4", - "ext-dom": "*", - "ext-openssl": "*", - "ext-pcntl": "*", - "ext-sockets": "*", - "nette/neon": "^2.3", - "paragonie/random_compat": ">= 2", - "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", - "psr/cache": "^1.0", - "psr/http-message": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3 || ^4.0", - "yoast/phpunit-polyfills": "^1.0" - }, - "suggest": { - "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", - "doctrine/cache": "To use the DoctrineCacheAdapter", - "ext-curl": "To send requests using cURL", - "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", - "ext-sockets": "To use client-side monitoring" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Aws\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" - } - ], - "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", - "keywords": [ - "amazon", - "aws", - "cloud", - "dynamodb", - "ec2", - "glacier", - "s3", - "sdk" - ], - "support": { - "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", - "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.269.0" - }, - "time": "2023-04-26T18:21:04+00:00" - }, { "name": "cebe/markdown", "version": "1.2.1", @@ -1304,67 +1155,6 @@ ], "time": "2022-06-09T08:53:42+00:00" }, - { - "name": "mtdowling/jmespath.php", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", - "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.17" - }, - "require-dev": { - "composer/xdebug-handler": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^7.5.15" - }, - "bin": [ - "bin/jp.php" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "files": [ - "src/JmesPath.php" - ], - "psr-4": { - "JmesPath\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Declaratively specify how to extract elements from a JSON document", - "keywords": [ - "json", - "jsonpath" - ], - "support": { - "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1" - }, - "time": "2021-06-14T00:11:39+00:00" - }, { "name": "paragonie/random_compat", "version": "v9.99.100", @@ -2842,93 +2632,6 @@ }, "time": "2022-08-24T14:44:38+00:00" }, - { - "name": "silinternational/simplesamlphp-module-material", - "version": "8.1.1", - "source": { - "type": "git", - "url": "https://github.com/silinternational/simplesamlphp-module-material.git", - "reference": "0cb61d2fa2be01f72ab1b44970c2d5c344b32066" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silinternational/simplesamlphp-module-material/zipball/0cb61d2fa2be01f72ab1b44970c2d5c344b32066", - "reference": "0cb61d2fa2be01f72ab1b44970c2d5c344b32066", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=7.0", - "silinternational/ssp-utilities": "^1.0", - "simplesamlphp/composer-module-installer": "^1.1.5", - "simplesamlphp/simplesamlphp": "~1.18.6 || ~1.19.0" - }, - "require-dev": { - "roave/security-advisories": "dev-master" - }, - "type": "simplesamlphp-module", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Billy Clark", - "email": "billy_clark@sil.org" - } - ], - "description": "Material Design theme for IdP Hub based on SimpleSAMLphp", - "support": { - "issues": "https://github.com/silinternational/simplesamlphp-module-material/issues", - "source": "https://github.com/silinternational/simplesamlphp-module-material/tree/8.1.1" - }, - "time": "2023-06-12T17:37:14+00:00" - }, - { - "name": "silinternational/simplesamlphp-module-sildisco", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/silinternational/simplesamlphp-module-sildisco.git", - "reference": "b9586d375272108d3006ae3df73ca4379c5f9353" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silinternational/simplesamlphp-module-sildisco/zipball/b9586d375272108d3006ae3df73ca4379c5f9353", - "reference": "b9586d375272108d3006ae3df73ca4379c5f9353", - "shasum": "" - }, - "require": { - "aws/aws-sdk-php": "^3.0", - "ext-dom": "*", - "php": ">=7.0", - "silinternational/php-env": "^1.0||^2.0||^3.0", - "silinternational/ssp-utilities": "^1.0", - "simplesamlphp/composer-module-installer": "^1.1.5", - "simplesamlphp/simplesamlphp": "~1.18.6||^1.19" - }, - "require-dev": { - "phpunit/phpunit": "^8.4", - "roave/security-advisories": "dev-master" - }, - "type": "simplesamlphp-module", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phillip Shipley", - "email": "phillip.shipley@gmail.com" - } - ], - "description": "IdP Discovery module for simpleSAMLphp with extra business logic", - "support": { - "issues": "https://github.com/silinternational/simplesamlphp-module-sildisco/issues", - "source": "https://github.com/silinternational/simplesamlphp-module-sildisco/tree/4.0.0" - }, - "time": "2023-04-25T18:31:08+00:00" - }, { "name": "silinternational/ssp-utilities", "version": "1.1.0", diff --git a/dockerbuild/config/config.php b/dockerbuild/config/config.php index 6d3d347e..79ca5ac9 100644 --- a/dockerbuild/config/config.php +++ b/dockerbuild/config/config.php @@ -332,9 +332,8 @@ * SAML messages will be logged, including plaintext versions of encrypted * messages. * - * - 'backtraces': this action controls the logging of error backtraces. If you - * want to log backtraces so that you can debug any possible errors happening in - * SimpleSAMLphp, enable this action (add it to the array or set it to true). + * - 'backtraces': this action controls the logging of error backtraces so you + * can debug any possible errors happening in SimpleSAMLphp. * * - 'validatexml': this action allows you to validate SAML documents against all * the relevant XML schemas. SAML 1.1 messages or SAML metadata parsed with @@ -555,6 +554,8 @@ * Which functionality in SimpleSAMLphp do you want to enable. Normally you would enable only * one of the functionalities below, but in some cases you could run multiple functionalities. * In example when you are setting up a federation bridge. + * + * Note that shib13-idp has been deprecated and will be removed in SimpleSAMLphp 2.0. */ 'enable.saml20-idp' => $SAML20_IDP_ENABLE, 'enable.shib13-idp' => false, @@ -675,10 +676,17 @@ * the RFC6265bis SameSite cookie attribute. If set to null, no SameSite * attribute will be sent. * + * A value of "None" is required to properly support cross-domain POST + * requests which are used by different SAML bindings. Because some older + * browsers do not support this value, the canSetSameSiteNone function + * can be called to only set it for compatible browsers. + * + * You must also set the 'session.cookie.secure' value above to true. + * * Example: * 'session.cookie.samesite' => 'None', */ - 'session.cookie.samesite' => null, + 'session.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null, /* * Options to override the default settings for php sessions. @@ -874,7 +882,7 @@ * ], * * establishing that if a translation for the "no" language code is - * not available, we look for translations in "nb" (Norwegian Bokmål), + * not available, we look for translations in "nb", * and so on, in that order. */ 'priorities' => [ @@ -884,6 +892,8 @@ 'se' => ['nb', 'no', 'nn', 'en'], 'nr' => ['zu', 'en'], 'nd' => ['zu', 'en'], + 'tw' => ['st', 'en'], + 'nso' => ['st', 'en'], ], ], @@ -893,7 +903,7 @@ 'language.available' => [ 'en', 'no', 'nn', 'se', 'da', 'de', 'sv', 'fi', 'es', 'ca', 'fr', 'it', 'nl', 'lb', 'cs', 'sl', 'lt', 'hr', 'hu', 'pl', 'pt', 'pt-br', 'tr', 'ja', 'zh', 'zh-tw', 'ru', - 'et', 'he', 'id', 'sr', 'lv', 'ro', 'eu', 'el', 'af', 'zu', 'xh', + 'et', 'he', 'id', 'sr', 'lv', 'ro', 'eu', 'el', 'af', 'zu', 'xh', 'st', ], 'language.rtl' => ['ar', 'dv', 'fa', 'ur', 'he'], 'language.default' => 'en', @@ -910,10 +920,10 @@ 'language.cookie.name' => 'language', 'language.cookie.domain' => null, 'language.cookie.path' => '/', - 'language.cookie.secure' => false, + 'language.cookie.secure' => true, 'language.cookie.httponly' => false, 'language.cookie.lifetime' => (60 * 60 * 24 * 900), - 'language.cookie.samesite' => null, + 'language.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null, /** * Custom getLanguage function called from SimpleSAML\Locale\Language::getLanguage(). @@ -1035,7 +1045,7 @@ ], /* - * If using the material theme, which color scheme to use + * color scheme to use for the material theme * Options: https://github.com/silinternational/simplesamlphp-module-material/blob/develop/README.md#branding */ 'theme.color-scheme' => $THEME_COLOR_SCHEME, @@ -1118,6 +1128,7 @@ ], // 48 => *** WARNING: For Hubs this entry is added at the end of this file + // 49 => *** WARNING: For Hubs this entry is added at the end of this file // If no attributes are requested in the SP metadata, then these will be sent through diff --git a/installed-packages.json b/installed-packages.json index 252d82c9..576bd6a6 100644 --- a/installed-packages.json +++ b/installed-packages.json @@ -17,7 +17,7 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.16.0" + "version": "v4.17.0" }, { "name": "fillup/fake-bower-assets", @@ -115,10 +115,6 @@ "name": "ralouphie/getallheaders", "version": "3.0.3" }, - { - "name": "ramsey/uuid", - "version": "3.9.7" - }, { "name": "rlanvin/php-ip", "version": "v1.0.1" @@ -133,7 +129,7 @@ }, { "name": "silinternational/idp-id-broker-php-client", - "version": "4.3.1" + "version": "4.3.2" }, { "name": "silinternational/php-env", @@ -143,26 +139,10 @@ "name": "silinternational/psr3-adapters", "version": "3.1.0" }, - { - "name": "silinternational/simplesamlphp-module-expirychecker", - "version": "3.1.3" - }, { "name": "silinternational/simplesamlphp-module-material", "version": "8.1.1" }, - { - "name": "silinternational/simplesamlphp-module-mfa", - "version": "5.2.1" - }, - { - "name": "silinternational/simplesamlphp-module-profilereview", - "version": "2.1.0" - }, - { - "name": "silinternational/simplesamlphp-module-silauth", - "version": "7.1.1" - }, { "name": "silinternational/simplesamlphp-module-sildisco", "version": "4.0.0" @@ -393,7 +373,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0" + "version": "v1.29.0" }, { "name": "symfony/polyfill-php81", @@ -437,7 +417,7 @@ }, { "name": "yiisoft/yii2", - "version": "2.0.48.1" + "version": "2.0.49.3" }, { "name": "yiisoft/yii2-composer", diff --git a/modules/sildisco/lib/Auth/Source/SP.php b/modules/sildisco/lib/Auth/Source/SP.php index b9781797..6cf2863b 100644 --- a/modules/sildisco/lib/Auth/Source/SP.php +++ b/modules/sildisco/lib/Auth/Source/SP.php @@ -2,7 +2,7 @@ /** * Modified from origin: modules/saml/lib/Auth/Source/SP.php - * 2022-09-26 -- Merged with simplesamlphp 1.19.6, lines/sections marked with GTIS are modified + * 2024-06-06 -- Merged with simplesamlphp 1.19.8, lines/sections marked with GTIS are modified */ declare(strict_types=1); @@ -561,6 +561,7 @@ private function startSSO2(Configuration $idpMetadata, array $state): void if (isset($state['saml:Audience'])) { $ar->setAudiences($state['saml:Audience']); } + if (isset($state['ForceAuthn'])) { $ar->setForceAuthn((bool) $state['ForceAuthn']); } @@ -866,7 +867,6 @@ public function reauthenticate(array &$state) { $session = Session::getSessionFromRequest(); $data = $session->getAuthState($this->authId); - $data = $session->getAuthState($this->authId); if ($data === null) { throw new Error\NoState(); } diff --git a/modules/sildisco/lib/IdP/SAML2.php b/modules/sildisco/lib/IdP/SAML2.php index 4d975bd6..9f7f2345 100644 --- a/modules/sildisco/lib/IdP/SAML2.php +++ b/modules/sildisco/lib/IdP/SAML2.php @@ -5,7 +5,7 @@ * Copied from the built-in simplesamlphp module modules/saml/lib/IdP/SAML2.php with code inserted. * See comment below about GTIS. * - * 2022-09-26 -- Merged with simplesamlphp 1.19.6, lines marked with GTIS are modified + * 2024-06-06 -- Merged with simplesamlphp 1.19.8, lines marked with GTIS are modified */ declare(strict_types=1); @@ -487,7 +487,7 @@ public static function receiveAuthnRequest(\SimpleSAML\IdP $idp) * to authenticate through any of the IDP's that have so far * been used for authentication. * - * In order for this for this to avoid forcing authentication + * In order for this to avoid forcing authentication * in every case, the hub's saml20-idp-hosted.php entry needs * to include an authproc entry that adds each authenticating * IDP to a list in the session. @@ -1512,4 +1512,4 @@ private static function buildResponse( return $r; } -} \ No newline at end of file +} diff --git a/modules/sildisco/www/disco.php b/modules/sildisco/www/disco.php index 6c3c08f0..e6b87f69 100644 --- a/modules/sildisco/www/disco.php +++ b/modules/sildisco/www/disco.php @@ -1,7 +1,7 @@ handleResponse($state, $issuer, $attributes); -assert(false); \ No newline at end of file +assert(false); diff --git a/modules/sildisco/www/sp/saml2-logout.php b/modules/sildisco/www/sp/saml2-logout.php index 05d9c14b..53219f92 100644 --- a/modules/sildisco/www/sp/saml2-logout.php +++ b/modules/sildisco/www/sp/saml2-logout.php @@ -6,7 +6,7 @@ * This endpoint handles both logout requests and logout responses. * * Similar to modules/saml/www/sp/saml2-logout.php - * 2022-09-26 -- Merged with simplesamlphp 1.19.6, lines marked with GTIS are modified + * 2024-06-06 -- Merged with simplesamlphp 1.19.8, lines marked with GTIS are modified */ if (!array_key_exists('PATH_INFO', $_SERVER)) { From 18dc22c8cfca1952908eed491f1d8448978187b7 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:45:32 +0400 Subject: [PATCH 26/46] change session.cookie.samesite back to `null` to fix tests --- dockerbuild/config/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerbuild/config/config.php b/dockerbuild/config/config.php index 79ca5ac9..7ebca3ac 100644 --- a/dockerbuild/config/config.php +++ b/dockerbuild/config/config.php @@ -686,7 +686,7 @@ * Example: * 'session.cookie.samesite' => 'None', */ - 'session.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null, + 'session.cookie.samesite' => null, /* * Options to override the default settings for php sessions. From 2c9a87d794aa4404d808267a61cb109343345756 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:39:02 +0400 Subject: [PATCH 27/46] allow null in typehint --- modules/silauth/lib/Auth/Source/auth/Authenticator.php | 2 +- modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/silauth/lib/Auth/Source/auth/Authenticator.php b/modules/silauth/lib/Auth/Source/auth/Authenticator.php index 2e91ed7e..fd084978 100644 --- a/modules/silauth/lib/Auth/Source/auth/Authenticator.php +++ b/modules/silauth/lib/Auth/Source/auth/Authenticator.php @@ -274,7 +274,7 @@ protected function isBlockedByRateLimit(string $username, array $ipAddresses): b FailedLoginIpAddress::isRateLimitBlockingAnyOfThese($ipAddresses); } - public static function isCaptchaRequired(string $username, array $ipAddresses): bool + public static function isCaptchaRequired(?string $username, array $ipAddresses): bool { return FailedLoginUsername::isCaptchaRequiredFor($username) || FailedLoginIpAddress::isCaptchaRequiredForAnyOfThese($ipAddresses); diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php index 26615487..4b67faac 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php @@ -108,7 +108,7 @@ public static function isRateLimitBlocking(string $username): bool return ($secondsUntilUnblocked > 0); } - public static function isCaptchaRequiredFor(string $username): bool + public static function isCaptchaRequiredFor(?string $username): bool { if (empty($username)) { return false; From 4cb55ef43d7abca18f260081aac2a2fb8439efea Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:25:29 +0800 Subject: [PATCH 28/46] Revert "share the use of static functions in Mfa authproc" This reverts commit 9d6bd38d15b51cbfb0787836bd4bb2c80346c802. --- modules/mfa/lib/Auth/Process/Mfa.php | 8 +-- .../lib/Auth/Process/ProfileReview.php | 66 +++++++++++++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index 0f1d681a..4e629d1a 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -320,7 +320,7 @@ public static function getTemplateFor(string $mfaType): string * @param array $state * @return string */ - public static function getRelayStateUrl(array $state): string + protected static function getRelayStateUrl($state) { if (array_key_exists('saml:RelayState', $state)) { $samlRelayState = $state['saml:RelayState']; @@ -407,12 +407,12 @@ protected function initComposerAutoloader(): void } } - public static function isHeadedToUrl(array $state, string $url): bool + protected static function isHeadedToMfaSetupUrl($state, $mfaSetupUrl) { if (array_key_exists('saml:RelayState', $state)) { $currentDestination = self::getRelayStateUrl($state); if (! empty($currentDestination)) { - return (strpos($currentDestination, $url) === 0); + return (strpos($currentDestination, $mfaSetupUrl) === 0); } } return false; @@ -579,7 +579,7 @@ public function process(&$state): void // Get the necessary info from the state data. $employeeId = $this->getAttribute($this->employeeIdAttr, $state); $mfa = $this->getAttributeAllValues('mfa', $state); - $isHeadedToMfaSetupUrl = self::isHeadedToUrl( + $isHeadedToMfaSetupUrl = self::isHeadedToMfaSetupUrl( $state, $this->mfaSetupUrl ); diff --git a/modules/profilereview/lib/Auth/Process/ProfileReview.php b/modules/profilereview/lib/Auth/Process/ProfileReview.php index b34e0567..789f6838 100644 --- a/modules/profilereview/lib/Auth/Process/ProfileReview.php +++ b/modules/profilereview/lib/Auth/Process/ProfileReview.php @@ -7,7 +7,6 @@ use SimpleSAML\Auth\ProcessingFilter; use SimpleSAML\Auth\State; use SimpleSAML\Module; -use SimpleSAML\Module\mfa\Auth\Process\Mfa; use SimpleSAML\Module\profilereview\LoggerFactory; use SimpleSAML\Session; use SimpleSAML\Utils\HTTP; @@ -68,8 +67,8 @@ protected function loadValuesFromConfig(array $config, array $attributes): void { foreach ($attributes as $attribute) { $this->$attribute = $config[$attribute] ?? null; - - Mfa::validateConfigValue( + + self::validateConfigValue( $attribute, $this->$attribute, $this->logger @@ -77,6 +76,28 @@ protected function loadValuesFromConfig(array $config, array $attributes): void } } + /** + * Validate the given config value + * + * @param string $attribute The name of the attribute. + * @param mixed $value The value to check. + * @param LoggerInterface $logger The logger. + * @throws \Exception + */ + public static function validateConfigValue($attribute, $value, $logger) + { + if (empty($value) || !is_string($value)) { + $exception = new \Exception(sprintf( + 'The value we have for %s (%s) is empty or is not a string', + $attribute, + var_export($value, true) + ), 1507146042); + + $logger->critical($exception->getMessage()); + throw $exception; + } + } + /** * Get the specified attribute from the given state data. * @@ -118,6 +139,30 @@ protected function getAttributeAllValues($attributeName, $state) return is_null($attributeData) ? null : (array)$attributeData; } + /** + * Return the saml:RelayState if it begins with "http" or "https". Otherwise + * return an empty string. + * + * @param array $state + * @returns string + * @return mixed|string + */ + protected static function getRelayStateUrl($state) + { + if (array_key_exists('saml:RelayState', $state)) { + $samlRelayState = $state['saml:RelayState']; + + if (strpos($samlRelayState, "http://") === 0) { + return $samlRelayState; + } + + if (strpos($samlRelayState, "https://") === 0) { + return $samlRelayState; + } + } + return ''; + } + protected function initComposerAutoloader() { $path = __DIR__ . '/../../../vendor/autoload.php'; @@ -125,6 +170,17 @@ protected function initComposerAutoloader() require_once $path; } } + + protected static function isHeadedToProfileUrl($state, $ProfileUrl) + { + if (array_key_exists('saml:RelayState', $state)) { + $currentDestination = self::getRelayStateUrl($state); + if (! empty($currentDestination)) { + return (strpos($currentDestination, $ProfileUrl) === 0); + } + } + return false; + } /** * Redirect the user to set up profile. @@ -135,7 +191,7 @@ public static function redirectToProfile(&$state) { $profileUrl = $state['ProfileUrl']; // Tell the profile-setup URL where the user is ultimately trying to go (if known). - $currentDestination = Mfa::getRelayStateUrl($state); + $currentDestination = self::getRelayStateUrl($state); if (! empty($currentDestination)) { $profileUrl = HTTP::addURLParameters( $profileUrl, @@ -165,7 +221,7 @@ public function process(&$state) { // Get the necessary info from the state data. $employeeId = $this->getAttribute($this->employeeIdAttr, $state); - $isHeadedToProfileUrl = Mfa::isHeadedToUrl($state, $this->profileUrl); + $isHeadedToProfileUrl = self::isHeadedToProfileUrl($state, $this->profileUrl); $mfa = $this->getAttributeAllValues('mfa', $state); $method = $this->getAttributeAllValues('method', $state); From 3d28b7cdffb033efabfaff701f61fff82cba8e2d Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:31:21 +0800 Subject: [PATCH 29/46] array return is not needed Co-authored-by: Jason Jackson <35783387+jason-jackson@users.noreply.github.com> --- modules/expirychecker/lib/Utilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/expirychecker/lib/Utilities.php b/modules/expirychecker/lib/Utilities.php index 72b8a95b..e7ca7e8b 100644 --- a/modules/expirychecker/lib/Utilities.php +++ b/modules/expirychecker/lib/Utilities.php @@ -53,7 +53,7 @@ public static function haveSameDomain(string $url1, string $start_marker1, * original url are different, it appends the StateId to the output. */ public static function convertOriginalUrl(string $passwordChangeUrl, - string $originalUrlParam, string $originalUrl, string $stateId ): array|string + string $originalUrlParam, string $originalUrl, string $stateId): string { $sameDomain = self::haveSameDomain($passwordChangeUrl, '//', '/', $originalUrl, '//', '/'); From 8eb1fca5399a494c6a6466cb9289c61930c65b6a Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:34:36 +0800 Subject: [PATCH 30/46] add typehint on setUserAttributes Co-authored-by: Jason Jackson <35783387+jason-jackson@users.noreply.github.com> --- modules/silauth/lib/Auth/Source/auth/Authenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/silauth/lib/Auth/Source/auth/Authenticator.php b/modules/silauth/lib/Auth/Source/auth/Authenticator.php index fd084978..dc8d10ca 100644 --- a/modules/silauth/lib/Auth/Source/auth/Authenticator.php +++ b/modules/silauth/lib/Auth/Source/auth/Authenticator.php @@ -353,7 +353,7 @@ protected function setErrorUsernameRequired(): void $this->setError(AuthError::CODE_USERNAME_REQUIRED); } - protected function setUserAttributes($attributes): void + protected function setUserAttributes(?array $attributes): void { $this->userAttributes = $attributes; } From 191478cd958112eef23e495ca6924a814ad0b3d6 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:40:20 +0800 Subject: [PATCH 31/46] typehint removeCategory Co-authored-by: Jason Jackson <35783387+jason-jackson@users.noreply.github.com> --- modules/silauth/lib/Auth/Source/config/ConfigManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/silauth/lib/Auth/Source/config/ConfigManager.php b/modules/silauth/lib/Auth/Source/config/ConfigManager.php index 553cac78..4216b01d 100644 --- a/modules/silauth/lib/Auth/Source/config/ConfigManager.php +++ b/modules/silauth/lib/Auth/Source/config/ConfigManager.php @@ -100,7 +100,7 @@ public static function initializeYii2WebApp(array $customConfig = []): void $app->log->getLogger(); } - public static function removeCategory($key): bool|string|null + public static function removeCategory(?string $key): bool|string|null { if ($key === null) { return null; From c19a67b3a0b2f6a8e6796ee278eb5cea2fb05176 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:59:33 +0800 Subject: [PATCH 32/46] restrict return type to int --- .../lib/Auth/Source/models/FailedLoginIpAddress.php | 8 ++++++-- .../lib/Auth/Source/models/FailedLoginUsername.php | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php index e62a0ef0..eefb2573 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginIpAddress.php @@ -38,13 +38,17 @@ public function behaviors(): array ]; } - public static function countRecentFailedLoginsFor(string $ipAddress): string|int|bool|null + public static function countRecentFailedLoginsFor(string $ipAddress): int { - return self::find()->where([ + $count = self::find()->where([ 'ip_address' => strtolower($ipAddress), ])->andWhere([ '>=', 'occurred_at_utc', UtcTime::format('-60 minutes') ])->count(); + if (!is_numeric($count)) { + throw new \Exception('expected a numeric value for recent failed logins by IP address, got '. $count); + } + return (int)$count; } public static function getFailedLoginsFor(string $ipAddress): array diff --git a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php index 4b67faac..771e1076 100644 --- a/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php +++ b/modules/silauth/lib/Auth/Source/models/FailedLoginUsername.php @@ -36,13 +36,17 @@ public function behaviors(): array ]; } - public static function countRecentFailedLoginsFor(string $username): bool|int|string|null + public static function countRecentFailedLoginsFor(string $username): int { - return self::find()->where([ + $count = self::find()->where([ 'username' => strtolower($username), ])->andWhere([ '>=', 'occurred_at_utc', UtcTime::format('-60 minutes') ])->count(); + if (!is_numeric($count)) { + throw new \Exception('expected a numeric value for recent failed logins by username, got '. $count); + } + return (int)$count; } /** From cbe50dc518e45d74f086934dc64fbcac1013a0d9 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:02:11 +0800 Subject: [PATCH 33/46] Update modules/sildisco/www/sp/discoresp.php Co-authored-by: Jason Jackson <35783387+jason-jackson@users.noreply.github.com> --- modules/sildisco/www/sp/discoresp.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/sildisco/www/sp/discoresp.php b/modules/sildisco/www/sp/discoresp.php index f9b30573..ff200bd8 100644 --- a/modules/sildisco/www/sp/discoresp.php +++ b/modules/sildisco/www/sp/discoresp.php @@ -1,8 +1,7 @@ Date: Tue, 11 Jun 2024 14:59:31 +0800 Subject: [PATCH 34/46] save a screenshot when a test fails --- .gitignore | 1 + features/bootstrap/FeatureContext.php | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 9f34c408..af9e176a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ composer.phar *.aes dockercfg node_modules/ +features/screenshots/ diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 748733c7..2734d304 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -22,6 +22,8 @@ class FeatureContext extends MinkContext protected const SP2_LOGIN_PAGE = 'http://ssp-sp2.local/module.php/core/authenticate.php?as=ssp-hub'; protected const SP3_LOGIN_PAGE = 'http://ssp-sp3.local/module.php/core/authenticate.php?as=ssp-hub'; + const SCREENSHOTS_PATH = '/data/features/screenshots/'; + /** @var Session */ protected $session; @@ -42,11 +44,27 @@ public function __construct() /** @AfterStep */ public function afterStep(AfterStepScope $scope) { - if (! $scope->getTestResult()->getResultCode() === StepResult::FAILED) { + if ($scope->getTestResult()->getResultCode() === StepResult::FAILED) { $this->showPageDetails(); + $this->takeScreenshot(); } } - + + /** + * Store a screenshot. + */ + private function takeScreenshot() { + $screenshot = $this->getSession()->getDriver()->getScreenshot(); + if (!is_dir(self::SCREENSHOTS_PATH)) { + mkdir(self::SCREENSHOTS_PATH); + } + if (is_dir(self::SCREENSHOTS_PATH)) { + $path = self::SCREENSHOTS_PATH . date('d-m-y') . '-' . uniqid() . '.png'; + file_put_contents($path, $screenshot); + print "\n\nScreenshot: " . $path; + } + } + protected function showPageDetails() { echo '[' . $this->session->getStatusCode() . '] '; @@ -251,9 +269,8 @@ public function iLogIn() $this->submitLoginForm($page); } catch (ElementNotFoundException $e) { Assert::true(false, sprintf( - "Did not find that element in the page.\nError: %s\nPage content: %s", - $e->getMessage(), - $page->getContent() + "Did not find that element in the page.\nError: %s", + $e->getMessage() )); } } From e983c4ce522229498ea2dfdd63b8476955264b30 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:48:27 +0800 Subject: [PATCH 35/46] fix simplewebauthn/browser.js path --- modules/mfa/www/prompt-for-mfa.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mfa/www/prompt-for-mfa.php b/modules/mfa/www/prompt-for-mfa.php index bd077c48..69592bfa 100644 --- a/modules/mfa/www/prompt-for-mfa.php +++ b/modules/mfa/www/prompt-for-mfa.php @@ -124,7 +124,7 @@ $t->data['stateId'] = $stateId; $t->data['supportsWebAuthn'] = LoginBrowser::supportsWebAuthn($userAgent); $browserJsHash = md5_file(__DIR__ . '/simplewebauthn/browser.js'); -$t->data['browserJsPath'] = 'simplewebauthn/browser.js?v=' . $browserJsHash; +$t->data['browserJsPath'] = '/module.php/mfa/simplewebauthn/browser.js?v=' . $browserJsHash; $t->data['managerEmail'] = $state['managerEmail']; $t->data['otherOptions'] = $otherOptions; $t->show(); From 0a7de22e0de0966374a119ac4cc9de59b279b16f Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:27:45 +0800 Subject: [PATCH 36/46] allow mfa submission to be an array as well as a string --- features/fakes/FakeIdBrokerClient.php | 5 +---- modules/mfa/lib/Auth/Process/Mfa.php | 2 +- modules/mfa/www/prompt-for-mfa.php | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/features/fakes/FakeIdBrokerClient.php b/features/fakes/FakeIdBrokerClient.php index 78654f93..c404fb9e 100644 --- a/features/fakes/FakeIdBrokerClient.php +++ b/features/fakes/FakeIdBrokerClient.php @@ -32,12 +32,9 @@ public function __construct( /** * Verify an MFA value - * @param int $id - * @param string $value - * @return array * @throws ServiceException */ - public function mfaVerify($id, $employeeId, $value) + public function mfaVerify(int $id, string $employeeId, string|array $value):array { if ($id === self::RATE_LIMITED_MFA_ID) { throw new ServiceException('Too many recent failures for this MFA', 0, 429); diff --git a/modules/mfa/lib/Auth/Process/Mfa.php b/modules/mfa/lib/Auth/Process/Mfa.php index 4e629d1a..cdd743d2 100644 --- a/modules/mfa/lib/Auth/Process/Mfa.php +++ b/modules/mfa/lib/Auth/Process/Mfa.php @@ -438,7 +438,7 @@ protected static function isHeadedToMfaSetupUrl($state, $mfaSetupUrl) public static function validateMfaSubmission( int $mfaId, string $employeeId, - string $mfaSubmission, + string|array $mfaSubmission, array $state, bool $rememberMe, LoggerInterface $logger, diff --git a/modules/mfa/www/prompt-for-mfa.php b/modules/mfa/www/prompt-for-mfa.php index 69592bfa..a736fd69 100644 --- a/modules/mfa/www/prompt-for-mfa.php +++ b/modules/mfa/www/prompt-for-mfa.php @@ -67,6 +67,7 @@ // If the user has submitted their MFA value... if (filter_has_var(INPUT_POST, 'submitMfa')) { + /* @var string|array $mfaSubmission */ $mfaSubmission = filter_input(INPUT_POST, 'mfaSubmission'); if (substr($mfaSubmission, 0, 1) == '{') { $mfaSubmission = json_decode($mfaSubmission, true); From ba13825aef526d05b86374dd3819a42273686bc5 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:56:17 +0800 Subject: [PATCH 37/46] revert the namespace change on migration files Yii wanted to re-run all migrations because of the change --- .../Auth/Source/migrations/M161213135750CreateInitialTables.php | 2 +- .../Source/migrations/M161213150831SwitchToUtcForDateTimes.php | 2 +- .../Source/migrations/M170214141109CreateFailedLoginsTable.php | 2 +- .../lib/Auth/Source/migrations/M170214145629RemoveOldTables.php | 2 +- .../Source/migrations/M170215141724SplitFailedLoginsTable.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php b/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php index efd4a2b3..a123a8f6 100644 --- a/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php +++ b/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php @@ -1,6 +1,6 @@ Date: Wed, 12 Jun 2024 02:30:15 +0800 Subject: [PATCH 38/46] move the migration files to the old namespace to avoid yii treating them as new migrations --- composer.json | 3 ++- dockerbuild/run-idp.sh | 6 +++--- .../M161213135750CreateInitialTables.php | 0 .../M161213150831SwitchToUtcForDateTimes.php | 0 .../M170214141109CreateFailedLoginsTable.php | 0 .../M170214145629RemoveOldTables.php | 0 .../M170215141724SplitFailedLoginsTable.php | 0 7 files changed, 5 insertions(+), 4 deletions(-) rename {modules/silauth/lib/Auth/Source/migrations => migrations}/M161213135750CreateInitialTables.php (100%) rename {modules/silauth/lib/Auth/Source/migrations => migrations}/M161213150831SwitchToUtcForDateTimes.php (100%) rename {modules/silauth/lib/Auth/Source/migrations => migrations}/M170214141109CreateFailedLoginsTable.php (100%) rename {modules/silauth/lib/Auth/Source/migrations => migrations}/M170214145629RemoveOldTables.php (100%) rename {modules/silauth/lib/Auth/Source/migrations => migrations}/M170215141724SplitFailedLoginsTable.php (100%) diff --git a/composer.json b/composer.json index 19c2dddb..fd5271e9 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,8 @@ "vendor/yiisoft/yii2/Yii.php" ], "psr-4": { - "Sil\\SspBase\\Features\\": "features/" + "Sil\\SspBase\\Features\\": "features/", + "Sil\\SilAuth\\migrations\\": "migrations/" } }, "config": { diff --git a/dockerbuild/run-idp.sh b/dockerbuild/run-idp.sh index d08922d0..2f65d0f0 100755 --- a/dockerbuild/run-idp.sh +++ b/dockerbuild/run-idp.sh @@ -7,10 +7,10 @@ set -x set -e # Try to run database migrations -cd /data/vendor/simplesamlphp/simplesamlphp/modules/silauth/lib/Auth/Source -chmod a+x ./yii +cd /data/vendor/simplesamlphp/simplesamlphp +chmod a+x ./modules/silauth/lib/Auth/Source/yii -./yii migrate --interactive=0 +./modules/silauth/lib/Auth/Source/yii migrate --interactive=0 cd /data ./run.sh diff --git a/modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php b/migrations/M161213135750CreateInitialTables.php similarity index 100% rename from modules/silauth/lib/Auth/Source/migrations/M161213135750CreateInitialTables.php rename to migrations/M161213135750CreateInitialTables.php diff --git a/modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php b/migrations/M161213150831SwitchToUtcForDateTimes.php similarity index 100% rename from modules/silauth/lib/Auth/Source/migrations/M161213150831SwitchToUtcForDateTimes.php rename to migrations/M161213150831SwitchToUtcForDateTimes.php diff --git a/modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php b/migrations/M170214141109CreateFailedLoginsTable.php similarity index 100% rename from modules/silauth/lib/Auth/Source/migrations/M170214141109CreateFailedLoginsTable.php rename to migrations/M170214141109CreateFailedLoginsTable.php diff --git a/modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php b/migrations/M170214145629RemoveOldTables.php similarity index 100% rename from modules/silauth/lib/Auth/Source/migrations/M170214145629RemoveOldTables.php rename to migrations/M170214145629RemoveOldTables.php diff --git a/modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php b/migrations/M170215141724SplitFailedLoginsTable.php similarity index 100% rename from modules/silauth/lib/Auth/Source/migrations/M170215141724SplitFailedLoginsTable.php rename to migrations/M170215141724SplitFailedLoginsTable.php From 282dc5aa8965d10858795860da61aea7afe6066a Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 12 Jun 2024 03:21:32 +0800 Subject: [PATCH 39/46] move migrations into silauth directory and update yii2-config.php with new migrations location --- composer.json | 2 +- modules/silauth/lib/Auth/Source/config/yii2-config.php | 3 ++- .../silauth/migrations}/M161213135750CreateInitialTables.php | 0 .../migrations}/M161213150831SwitchToUtcForDateTimes.php | 0 .../migrations}/M170214141109CreateFailedLoginsTable.php | 0 .../silauth/migrations}/M170214145629RemoveOldTables.php | 0 .../migrations}/M170215141724SplitFailedLoginsTable.php | 0 7 files changed, 3 insertions(+), 2 deletions(-) rename {migrations => modules/silauth/migrations}/M161213135750CreateInitialTables.php (100%) rename {migrations => modules/silauth/migrations}/M161213150831SwitchToUtcForDateTimes.php (100%) rename {migrations => modules/silauth/migrations}/M170214141109CreateFailedLoginsTable.php (100%) rename {migrations => modules/silauth/migrations}/M170214145629RemoveOldTables.php (100%) rename {migrations => modules/silauth/migrations}/M170215141724SplitFailedLoginsTable.php (100%) diff --git a/composer.json b/composer.json index fd5271e9..126b0925 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ ], "psr-4": { "Sil\\SspBase\\Features\\": "features/", - "Sil\\SilAuth\\migrations\\": "migrations/" + "Sil\\SilAuth\\migrations\\": "modules/silauth/migrations/" } }, "config": { diff --git a/modules/silauth/lib/Auth/Source/config/yii2-config.php b/modules/silauth/lib/Auth/Source/config/yii2-config.php index b3f361c5..32a42f1b 100644 --- a/modules/silauth/lib/Auth/Source/config/yii2-config.php +++ b/modules/silauth/lib/Auth/Source/config/yii2-config.php @@ -8,6 +8,7 @@ 'id' => 'SilAuth', 'aliases' => [ '@SimpleSAML/Module/silauth/Auth/Source' => __DIR__ . '/..', + '@Sil/SilAuth' => __DIR__ . '/../../../..', ], 'bootstrap' => [ 'gii', @@ -57,7 +58,7 @@ 'migrate' => [ 'class' => 'yii\console\controllers\MigrateController', 'migrationNamespaces' => [ - 'SimpleSAML\\Module\\silauth\\Auth\\Source\\migrations\\', + 'Sil\\SilAuth\\migrations\\', ], // Disable non-namespaced migrations. diff --git a/migrations/M161213135750CreateInitialTables.php b/modules/silauth/migrations/M161213135750CreateInitialTables.php similarity index 100% rename from migrations/M161213135750CreateInitialTables.php rename to modules/silauth/migrations/M161213135750CreateInitialTables.php diff --git a/migrations/M161213150831SwitchToUtcForDateTimes.php b/modules/silauth/migrations/M161213150831SwitchToUtcForDateTimes.php similarity index 100% rename from migrations/M161213150831SwitchToUtcForDateTimes.php rename to modules/silauth/migrations/M161213150831SwitchToUtcForDateTimes.php diff --git a/migrations/M170214141109CreateFailedLoginsTable.php b/modules/silauth/migrations/M170214141109CreateFailedLoginsTable.php similarity index 100% rename from migrations/M170214141109CreateFailedLoginsTable.php rename to modules/silauth/migrations/M170214141109CreateFailedLoginsTable.php diff --git a/migrations/M170214145629RemoveOldTables.php b/modules/silauth/migrations/M170214145629RemoveOldTables.php similarity index 100% rename from migrations/M170214145629RemoveOldTables.php rename to modules/silauth/migrations/M170214145629RemoveOldTables.php diff --git a/migrations/M170215141724SplitFailedLoginsTable.php b/modules/silauth/migrations/M170215141724SplitFailedLoginsTable.php similarity index 100% rename from migrations/M170215141724SplitFailedLoginsTable.php rename to modules/silauth/migrations/M170215141724SplitFailedLoginsTable.php From 9320d6d410f745fcccba58b15d6a1afe84a01df9 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:54:54 +0800 Subject: [PATCH 40/46] ADMIN_EMAIL (technicalcontact_email) is not mandatory or required --- dockerbuild/config/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerbuild/config/config.php b/dockerbuild/config/config.php index 7ebca3ac..e046fb95 100644 --- a/dockerbuild/config/config.php +++ b/dockerbuild/config/config.php @@ -22,7 +22,7 @@ try { // Required to be defined in environment variables - $ADMIN_EMAIL = Env::requireEnv('ADMIN_EMAIL'); + $ADMIN_EMAIL = Env::get('ADMIN_EMAIL', 'na@example.org'); $ADMIN_PASS = Env::requireEnv('ADMIN_PASS'); $SECRET_SALT = Env::requireEnv('SECRET_SALT'); $IDP_NAME = Env::requireEnv('IDP_NAME'); From 750fdf56f51732dab629495c01dc671e3324d87e Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:53:32 +0800 Subject: [PATCH 41/46] avoid a logged error in nag controller --- modules/profilereview/www/nag.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/profilereview/www/nag.php b/modules/profilereview/www/nag.php index 7c0e91c0..7418418b 100644 --- a/modules/profilereview/www/nag.php +++ b/modules/profilereview/www/nag.php @@ -32,8 +32,8 @@ $t = new Template($globalConfig, 'profilereview:' . $state['template']); $t->data['profileUrl'] = $state['profileUrl']; -$t->data['methodOptions'] = $state['methodOptions']; -$t->data['mfaOptions'] = $state['mfaOptions']; +$t->data['methodOptions'] = $state['methodOptions'] ?? []; +$t->data['mfaOptions'] = $state['mfaOptions'] ?? []; $t->data['mfaLearnMoreUrl'] = $state['mfaLearnMoreUrl']; $t->show(); From 03f3b5edf8a59c3dd5b54409fd807e8903ae23c3 Mon Sep 17 00:00:00 2001 From: briskt <3172830+briskt@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:54:04 +0800 Subject: [PATCH 42/46] better error handling for manager code request --- .../themes/material/mfa/send-manager-mfa.php | 18 ++++++++++++++++++ modules/mfa/lib/Auth/Process/Mfa.php | 5 +++-- modules/mfa/www/send-manager-mfa.php | 3 ++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/material/themes/material/mfa/send-manager-mfa.php b/modules/material/themes/material/mfa/send-manager-mfa.php index 8bb30a8c..14c44cac 100644 --- a/modules/material/themes/material/mfa/send-manager-mfa.php +++ b/modules/material/themes/material/mfa/send-manager-mfa.php @@ -33,6 +33,24 @@

+ data['errorMessage']; + + if (! empty($message)) { + ?> +
+

+ error + + + + +

+
+ +