From 916f315b2331148d00b4c8ff3b8985ba3b9236fe Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 20 Nov 2023 14:18:17 +0800 Subject: [PATCH 01/26] Add test for checking localization parameters are not translated --- .../Language/AbstractTranslationTestCase.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/Language/AbstractTranslationTestCase.php b/tests/Language/AbstractTranslationTestCase.php index 0d8efe48c..1f2b370b3 100644 --- a/tests/Language/AbstractTranslationTestCase.php +++ b/tests/Language/AbstractTranslationTestCase.php @@ -304,6 +304,61 @@ final public function testAllConfiguredLanguageKeysAreInOrder(string $locale): v )); } + /** + * @see https://codeigniter4.github.io/CodeIgniter4/outgoing/localization.html#replacing-parameters + * + * @dataProvider localesProvider + */ + final public function testAllLocalizationParametersAreNotTranslated(string $locale): void + { + $diffs = []; + + foreach ($this->foundSets($locale) as $file) { + $original = $this->loadFile($file); + $translated = $this->loadFile($file, $locale); + + foreach ($original as $key => $translation) { + if (! array_key_exists($key, $translated)) { + continue; + } + + preg_match_all('/(\{[^\}]+\})/', $translation, $matches); + array_shift($matches); + + if ($matches === []) { + unset($matches); + + continue; + } + + foreach ($matches as $match) { + foreach ($match as $parameter) { + if (strpos($translated[$key], (string) $parameter) === false) { + $id = sprintf('%s.%s', substr($file, 0, -4), $key); + + $diffs[$id] ??= []; + + $diffs[$id][] = $parameter; + } + } + } + + unset($matches); + } + } + + ksort($diffs); + + $this->assertEmpty($diffs, sprintf( + "Failed asserting that parameters of translation keys are not translated:\n%s", + implode("\n", array_map( + static fn (string $key, array $values): string => sprintf(' * %s => %s', $key, implode(', ', $values)), + array_keys($diffs), + array_values($diffs) + )) + )); + } + /** * @return string[][] */ From 3c2ea0a2b00f67c28d4280b2763a8b397356f8a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:57:38 +0000 Subject: [PATCH 02/26] chore(deps-dev): update rector/rector requirement Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.18.10...0.18.11) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f7eed49ca..0e0e9e650 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "mockery/mockery": "^1.0", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-strict-rules": "^1.5", - "rector/rector": "0.18.10" + "rector/rector": "0.18.11" }, "provide": { "codeigniter4/authentication-implementation": "1.0" From 7b690719bb4b17a0b06333f7e044520c8afdc9f8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Dec 2023 09:18:36 +0900 Subject: [PATCH 03/26] test: update assertion It seems the order of array items changed by the library upgrade. --- tests/Unit/Authentication/JWT/JWTManagerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Authentication/JWT/JWTManagerTest.php b/tests/Unit/Authentication/JWT/JWTManagerTest.php index 6acd82717..684281898 100644 --- a/tests/Unit/Authentication/JWT/JWTManagerTest.php +++ b/tests/Unit/Authentication/JWT/JWTManagerTest.php @@ -182,9 +182,9 @@ public function testIssueAddHeader(): void $this->assertIsString($token); $headers = $this->decodeJWT($token, 'header'); - $this->assertSame([ - 'extra_key' => 'extra_value', + $this->assertEqualsCanonicalizing([ 'typ' => 'JWT', + 'extra_key' => 'extra_value', 'alg' => 'HS256', ], $headers); } From 2b4078bc2582a6dd0725a87aff65278a73189442 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 26 Nov 2023 06:21:00 +0900 Subject: [PATCH 04/26] refactor: remove unneeded deleted_at in $allowedFields --- src/Models/UserModel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Models/UserModel.php b/src/Models/UserModel.php index 73d293033..21fe2b3d5 100644 --- a/src/Models/UserModel.php +++ b/src/Models/UserModel.php @@ -36,7 +36,6 @@ class UserModel extends BaseModel 'status_message', 'active', 'last_active', - 'deleted_at', ]; protected $useTimestamps = true; protected $afterFind = ['fetchIdentities']; From e34bfe8f758ee6d5e9a5431f6681b99b109e9e9a Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 26 Nov 2023 06:37:15 +0900 Subject: [PATCH 05/26] chore: remove ignoreErrors --- phpstan-baseline.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index a261d48ba..d6c3d7580 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -311,31 +311,11 @@ 'count' => 1, 'path' => __DIR__ . '/src/Models/UserIdentityModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Cannot unset offset \'email\' on array\\{username\\: string, status\\: string, status_message\\: string, active\\: bool, last_active\\: string, deleted_at\\: string\\}\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Models/UserModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Cannot unset offset \'password_hash\' on array\\{username\\: string, status\\: string, status_message\\: string, active\\: bool, last_active\\: string, deleted_at\\: string\\}\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Models/UserModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 2, 'path' => __DIR__ . '/src/Models/UserModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Offset \'email\' does not exist on array\\{username\\: string, status\\: string, status_message\\: string, active\\: bool, last_active\\: string, deleted_at\\: string\\}\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Models/UserModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Offset \'password_hash\' does not exist on array\\{username\\: string, status\\: string, status_message\\: string, active\\: bool, last_active\\: string, deleted_at\\: string\\}\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Models/UserModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Parameter \\#1 \\$data \\(array\\|CodeIgniter\\\\Shield\\\\Entities\\\\User\\) of method CodeIgniter\\\\Shield\\\\Models\\\\UserModel\\:\\:insert\\(\\) should be contravariant with parameter \\$data \\(array\\|object\\|null\\) of method CodeIgniter\\\\Model\\:\\:insert\\(\\)$#', 'count' => 1, From 9f715f9c86f3d345405fcb61ea75c8e1aac46383 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 3 Dec 2023 08:57:00 +0900 Subject: [PATCH 06/26] docs: add @var to tell PHPStan correct type --- src/Models/UserModel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Models/UserModel.php b/src/Models/UserModel.php index 21fe2b3d5..2cc1210e3 100644 --- a/src/Models/UserModel.php +++ b/src/Models/UserModel.php @@ -204,6 +204,7 @@ public function findByCredentials(array $credentials): ?User } if ($email !== null) { + /** @var array|null $data */ $data = $this->select( sprintf('%1$s.*, %2$s.secret as email, %2$s.secret2 as password_hash', $this->table, $this->tables['identities']) ) From e4fc382ecc9d0480304d0fa20cc9ee98602e4177 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:31:40 +0000 Subject: [PATCH 07/26] chore(deps-dev): update rector/rector requirement Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.18.11...0.18.12) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0e0e9e650..afab5d1e4 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "mockery/mockery": "^1.0", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-strict-rules": "^1.5", - "rector/rector": "0.18.11" + "rector/rector": "0.18.12" }, "provide": { "codeigniter4/authentication-implementation": "1.0" From 1420dfceb6c457435b2025336bb63e35545e5588 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:33:21 +0000 Subject: [PATCH 08/26] chore(deps): bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b596d4a9a..3f55bd999 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 0 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.x - name: Install Dependencies From a191391293ef84e344e6bd450a6a8095177486fd Mon Sep 17 00:00:00 2001 From: Radoslav Georgiev Date: Thu, 7 Dec 2023 13:24:30 +0200 Subject: [PATCH 09/26] lang: [bg] Adjust translations for bg/Auth.php --- src/Language/bg/Auth.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Language/bg/Auth.php b/src/Language/bg/Auth.php index 8aa8f898d..b34e37f86 100644 --- a/src/Language/bg/Auth.php +++ b/src/Language/bg/Auth.php @@ -39,7 +39,7 @@ 'password' => 'Парола', 'passwordConfirm' => 'Парола (отново)', 'haveAccount' => 'Вече имате акаунт?', - 'token' => '(To be translated) Token', + 'token' => 'Токен', // Бутони 'confirm' => 'Потвърди', @@ -61,7 +61,7 @@ 'magicLinkExpired' => 'Съжаляваме, линкът е изтекъл.', 'checkYourEmail' => 'Проверете вашия имейл!', 'magicLinkDetails' => 'Току що ви изпратихме имейл с линк за вход. Линкът ще бъде валиден само {0} минути.', - 'magicLinkDisabled' => '(To be translated) Use of MagicLink is currently not allowed.', + 'magicLinkDisabled' => 'Използването на линк за вход в момента не е разрешено.', 'successLogout' => 'Успешно излязохте от системата.', 'backToLogin' => 'Обратно към входа', @@ -83,7 +83,7 @@ 'resetTokenExpired' => 'Съжаляваме. Вашият токен за нулиране на паролата е изтекъл.', // Глобални променливи за електронна поща - 'emailInfo' => 'Някаква информации за потребителя:', + 'emailInfo' => 'Информации за потребител:', 'emailIpAddress' => 'IP Адрес:', 'emailDevice' => 'Устройство:', 'emailDate' => 'Дата:', From fef02d1e7554813e60724f450ad274d9bdb93965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20=C4=B0mzal=C4=B1?= Date: Sat, 30 Sep 2023 10:57:26 +0300 Subject: [PATCH 10/26] supportOldDangerousPassword support removed --- src/Authentication/Authenticators/Session.php | 14 ++-------- src/Authentication/Passwords.php | 15 ---------- src/Config/Auth.php | 10 ------- .../SessionAuthenticatorTest.php | 28 ------------------- 4 files changed, 2 insertions(+), 65 deletions(-) diff --git a/src/Authentication/Authenticators/Session.php b/src/Authentication/Authenticators/Session.php index 98e4a29a0..8802b7e9e 100644 --- a/src/Authentication/Authenticators/Session.php +++ b/src/Authentication/Authenticators/Session.php @@ -343,30 +343,20 @@ public function check(array $credentials): Result /** @var Passwords $passwords */ $passwords = service('passwords'); - // This is only for supportOldDangerousPassword. - $needsRehash = false; - // Now, try matching the passwords. if (! $passwords->verify($givenPassword, $user->password_hash)) { - if ( - ! setting('Auth.supportOldDangerousPassword') - || ! $passwords->verifyDanger($givenPassword, $user->password_hash) // @phpstan-ignore-line - ) { return new Result([ 'success' => false, 'reason' => lang('Auth.invalidPassword'), ]); - } - - // Passed with old dangerous password. - $needsRehash = true; + } // Check to see if the password needs to be rehashed. // This would be due to the hash algorithm or hash // cost changing since the last time that a user // logged in. - if ($passwords->needsRehash($user->password_hash) || $needsRehash) { + if ($passwords->needsRehash($user->password_hash)) { $user->password_hash = $passwords->hash($givenPassword); $this->provider->save($user); } diff --git a/src/Authentication/Passwords.php b/src/Authentication/Passwords.php index 994c192a8..42f9fe0ed 100644 --- a/src/Authentication/Passwords.php +++ b/src/Authentication/Passwords.php @@ -90,21 +90,6 @@ public function verify(string $password, string $hash): bool return password_verify($password, $hash); } - /** - * Verifies a password against a previously hashed password. - * - * @param string $password The password we're checking - * @param string $hash The previously hashed password - * - * @deprecated This is only for backward compatibility. - */ - public function verifyDanger(string $password, string $hash): bool - { - return password_verify(base64_encode( - hash('sha384', $password, true) - ), $hash); - } - /** * Checks to see if a password should be rehashed. */ diff --git a/src/Config/Auth.php b/src/Config/Auth.php index 22a052b01..bf4c9ca02 100644 --- a/src/Config/Auth.php +++ b/src/Config/Auth.php @@ -374,16 +374,6 @@ class Auth extends BaseConfig */ public int $hashCost = 12; - /** - * If you need to support passwords saved in versions prior to Shield v1.0.0-beta.4. - * set this to true. - * - * See https://github.com/codeigniter4/shield/security/advisories/GHSA-c5vj-f36q-p9vg - * - * @deprecated This is only for backward compatibility. - */ - public bool $supportOldDangerousPassword = false; - /** * //////////////////////////////////////////////////////////////////// * OTHER SETTINGS diff --git a/tests/Authentication/Authenticators/SessionAuthenticatorTest.php b/tests/Authentication/Authenticators/SessionAuthenticatorTest.php index ca1a8b53e..d181d398f 100644 --- a/tests/Authentication/Authenticators/SessionAuthenticatorTest.php +++ b/tests/Authentication/Authenticators/SessionAuthenticatorTest.php @@ -313,34 +313,6 @@ public function testCheckSuccess(): void $this->assertSame($this->user->id, $foundUser->id); } - public function testCheckSuccessOldDangerousPassword(): void - { - /** @var Auth $config */ - $config = config('Auth'); - $config->supportOldDangerousPassword = true; // @phpstan-ignore-line - - fake( - UserIdentityModel::class, - [ - 'user_id' => $this->user->id, - 'type' => Session::ID_TYPE_EMAIL_PASSWORD, - 'secret' => 'foo@example.com', - 'secret2' => '$2y$10$WswjNNcR24cJvsXvBc5TveVVVQ9/EYC0eq.Ad9e/2cVnmeSEYBOEm', - ] - ); - - $result = $this->auth->check([ - 'email' => 'foo@example.com', - 'password' => 'passw0rd!', - ]); - - $this->assertInstanceOf(Result::class, $result); - $this->assertTrue($result->isOK()); - - $foundUser = $result->extraInfo(); - $this->assertSame($this->user->id, $foundUser->id); - } - public function testAttemptCannotFindUser(): void { $result = $this->auth->attempt([ From f54d7bf40aac6459ac27c492c92ab7c2cc119431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20=C4=B0mzal=C4=B1?= Date: Sat, 30 Sep 2023 11:10:00 +0300 Subject: [PATCH 11/26] supportOldDangerousPassword support removed && style fixes according to style guide --- src/Authentication/Authenticators/Session.php | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/Authentication/Authenticators/Session.php b/src/Authentication/Authenticators/Session.php index 8802b7e9e..6d040b494 100644 --- a/src/Authentication/Authenticators/Session.php +++ b/src/Authentication/Authenticators/Session.php @@ -102,8 +102,8 @@ private function checkSecurityConfig(): void if ($securityConfig->csrfProtection === 'cookie') { throw new SecurityException( 'Config\Security::$csrfProtection is set to \'cookie\'.' - . ' Same-site attackers may bypass the CSRF protection.' - . ' Please set it to \'session\'.' + . ' Same-site attackers may bypass the CSRF protection.' + . ' Please set it to \'session\'.' ); } } @@ -137,7 +137,7 @@ public function attempt(array $credentials): Result $result = $this->check($credentials); // Credentials mismatch. - if (! $result->isOK()) { + if (!$result->isOK()) { // Always record a login attempt, whether success or not. $this->recordLoginAttempt($credentials, false, $ipAddress, $userAgent); @@ -180,7 +180,7 @@ public function attempt(array $credentials): Result $this->issueRememberMeToken(); - if (! $this->hasAction()) { + if (!$this->hasAction()) { $this->completeLogin($user); } @@ -291,10 +291,10 @@ private function recordLoginAttempt( $field = array_pop($field); - if (! in_array($field, ['email', 'username'], true)) { + if (!in_array($field, ['email', 'username'], true)) { $idType = $field; } else { - $idType = (! isset($credentials['email']) && isset($credentials['username'])) + $idType = (!isset($credentials['email']) && isset($credentials['username'])) ? self::ID_TYPE_USERNAME : self::ID_TYPE_EMAIL_PASSWORD; } @@ -344,12 +344,11 @@ public function check(array $credentials): Result $passwords = service('passwords'); // Now, try matching the passwords. - if (! $passwords->verify($givenPassword, $user->password_hash)) { - return new Result([ - 'success' => false, - 'reason' => lang('Auth.invalidPassword'), - ]); - + if (!$passwords->verify($givenPassword, $user->password_hash)) { + return new Result([ + 'success' => false, + 'reason' => lang('Auth.invalidPassword'), + ]); } // Check to see if the password needs to be rehashed. @@ -651,10 +650,10 @@ public function startLogin(User $user): void if ($userId !== null) { throw new LogicException( 'The user has User Info in Session, so already logged in or in pending login state.' - . ' If a logged in user logs in again with other account, the session data of the previous' - . ' user will be used as the new user.' - . ' Fix your code to prevent users from logging in without logging out or delete the session data.' - . ' user_id: ' . $userId + . ' If a logged in user logs in again with other account, the session data of the previous' + . ' user will be used as the new user.' + . ' Fix your code to prevent users from logging in without logging out or delete the session data.' + . ' user_id: ' . $userId ); } @@ -739,18 +738,18 @@ public function login(User $user): void if ($this->getIdentitiesForAction($user) !== []) { throw new LogicException( 'The user has identities for action, so cannot complete login.' - . ' If you want to start to login with auth action, use startLogin() instead.' - . ' Or delete identities for action in database.' - . ' user_id: ' . $user->id + . ' If you want to start to login with auth action, use startLogin() instead.' + . ' Or delete identities for action in database.' + . ' user_id: ' . $user->id ); } // Check auth_action in Session if ($this->getSessionKey('auth_action')) { throw new LogicException( 'The user has auth action in session, so cannot complete login.' - . ' If you want to start to login with auth action, use startLogin() instead.' - . ' Or delete `auth_action` and `auth_action_message` in session data.' - . ' user_id: ' . $user->id + . ' If you want to start to login with auth action, use startLogin() instead.' + . ' Or delete `auth_action` and `auth_action_message` in session data.' + . ' user_id: ' . $user->id ); } @@ -893,7 +892,7 @@ public function getPendingUser(): ?User */ public function recordActiveDate(): void { - if (! $this->user instanceof User) { + if (!$this->user instanceof User) { throw new InvalidArgumentException( __METHOD__ . '() requires logged in user before calling.' ); From c528352d3c3c8c03f15e05bac4e941da47ac5699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20=C4=B0mzal=C4=B1?= Date: Sat, 30 Sep 2023 16:20:39 +0300 Subject: [PATCH 12/26] PHPStan changed to 8 and composer fix --- phpstan-baseline.php | 2 +- src/Authentication/Authenticators/Session.php | 12 ++++++------ .../Authenticators/SessionAuthenticatorTest.php | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index d6c3d7580..dd4fafb64 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -348,7 +348,7 @@ ]; $ignoreErrors[] = [ 'message' => '#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with \'CodeIgniter\\\\\\\\Shield\\\\\\\\Result\' and CodeIgniter\\\\Shield\\\\Result will always evaluate to true\\.$#', - 'count' => 9, + 'count' => 8, 'path' => __DIR__ . '/tests/Authentication/Authenticators/SessionAuthenticatorTest.php', ]; $ignoreErrors[] = [ diff --git a/src/Authentication/Authenticators/Session.php b/src/Authentication/Authenticators/Session.php index 6d040b494..c480dae5c 100644 --- a/src/Authentication/Authenticators/Session.php +++ b/src/Authentication/Authenticators/Session.php @@ -137,7 +137,7 @@ public function attempt(array $credentials): Result $result = $this->check($credentials); // Credentials mismatch. - if (!$result->isOK()) { + if (! $result->isOK()) { // Always record a login attempt, whether success or not. $this->recordLoginAttempt($credentials, false, $ipAddress, $userAgent); @@ -180,7 +180,7 @@ public function attempt(array $credentials): Result $this->issueRememberMeToken(); - if (!$this->hasAction()) { + if (! $this->hasAction()) { $this->completeLogin($user); } @@ -291,10 +291,10 @@ private function recordLoginAttempt( $field = array_pop($field); - if (!in_array($field, ['email', 'username'], true)) { + if (! in_array($field, ['email', 'username'], true)) { $idType = $field; } else { - $idType = (!isset($credentials['email']) && isset($credentials['username'])) + $idType = (! isset($credentials['email']) && isset($credentials['username'])) ? self::ID_TYPE_USERNAME : self::ID_TYPE_EMAIL_PASSWORD; } @@ -344,7 +344,7 @@ public function check(array $credentials): Result $passwords = service('passwords'); // Now, try matching the passwords. - if (!$passwords->verify($givenPassword, $user->password_hash)) { + if (! $passwords->verify($givenPassword, $user->password_hash)) { return new Result([ 'success' => false, 'reason' => lang('Auth.invalidPassword'), @@ -892,7 +892,7 @@ public function getPendingUser(): ?User */ public function recordActiveDate(): void { - if (!$this->user instanceof User) { + if (! $this->user instanceof User) { throw new InvalidArgumentException( __METHOD__ . '() requires logged in user before calling.' ); diff --git a/tests/Authentication/Authenticators/SessionAuthenticatorTest.php b/tests/Authentication/Authenticators/SessionAuthenticatorTest.php index d181d398f..3fb04a386 100644 --- a/tests/Authentication/Authenticators/SessionAuthenticatorTest.php +++ b/tests/Authentication/Authenticators/SessionAuthenticatorTest.php @@ -21,7 +21,6 @@ use CodeIgniter\Shield\Entities\User; use CodeIgniter\Shield\Exceptions\LogicException; use CodeIgniter\Shield\Models\RememberModel; -use CodeIgniter\Shield\Models\UserIdentityModel; use CodeIgniter\Shield\Models\UserModel; use CodeIgniter\Shield\Result; use CodeIgniter\Test\Mock\MockEvents; From 2d4c315aab439952253a2d028b19b73b5085bf63 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Dec 2023 07:08:33 +0900 Subject: [PATCH 13/26] docs: add UPGRADING.md --- UPGRADING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index e004c9d2c..c0248a97f 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,5 +1,13 @@ # Upgrade Guide +## Version 1.0.0-beta.8 to 1.0.0 + +## Removed Deprecated Items + +The [$supportOldDangerousPassword](#if-you-want-to-allow-login-with-existing-passwords) +feature for backward compatiblity has been removed. The old passwords saved in +Shield v1.0.0-beta.3 or earlier are no longer supported. + ## Version 1.0.0-beta.7 to 1.0.0-beta.8 ### Mandatory Config Changes From 5020b54de8192ddc4828ae9a0dea172508b73c23 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Dec 2023 07:25:30 +0900 Subject: [PATCH 14/26] docs: add explanations --- .../adding_attributes_to_users.md | 2 + docs/customization/user_provider.md | 38 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/customization/adding_attributes_to_users.md b/docs/customization/adding_attributes_to_users.md index 05530c6f3..83488afd2 100644 --- a/docs/customization/adding_attributes_to_users.md +++ b/docs/customization/adding_attributes_to_users.md @@ -74,6 +74,8 @@ php spark db:table users See [Customizing User Provider](./user_provider.md). +Don't forget to add the added attributes to the `$allowedFields` property. + ## Update Validation Rules You need to update the [validation rules](./validation_rules.md) for registration. diff --git a/docs/customization/user_provider.md b/docs/customization/user_provider.md index 12246043c..2de9345b8 100644 --- a/docs/customization/user_provider.md +++ b/docs/customization/user_provider.md @@ -1,5 +1,7 @@ # Customizing User Provider +## Creating Your Own UserModel + If you want to customize user attributes, you need to create your own [User Provider](../getting_started/concepts.md#user-providers) class. The only requirement is that your new class MUST extend the provided `CodeIgniter\Shield\Models\UserModel`. @@ -13,7 +15,41 @@ php spark shield:model UserModel The class name is optional. If none is provided, the generated class name would be `UserModel`. -After creating the class, set the `$userProvider` property in **app/Config/Auth.php** as follows: +## Customizing Your UserModel + +Customize your model as you like. + +If you add attributes, don't forget to add the attributes to the `$allowedFields` +property. + +```php +allowedFields = [ + ...$this->allowedFields, + 'first_name', // Added + 'last_name', // Added + ]; + } +} +``` + +## Configuring to Use Your UserModel + +After creating the class, set your model classname to the `$userProvider` property +in **app/Config/Auth.php**: ```php public string $userProvider = \App\Models\UserModel::class; From 550414b13b73c83b8a698402f7724d5259d8ba59 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 9 Dec 2023 19:13:45 +0900 Subject: [PATCH 15/26] docs: move "Configuring to Use Your UserModel" up --- docs/customization/user_provider.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/customization/user_provider.md b/docs/customization/user_provider.md index 2de9345b8..379574e80 100644 --- a/docs/customization/user_provider.md +++ b/docs/customization/user_provider.md @@ -15,6 +15,15 @@ php spark shield:model UserModel The class name is optional. If none is provided, the generated class name would be `UserModel`. +## Configuring to Use Your UserModel + +After creating the class, set your model classname to the `$userProvider` property +in **app/Config/Auth.php**: + +```php +public string $userProvider = \App\Models\UserModel::class; +``` + ## Customizing Your UserModel Customize your model as you like. @@ -45,12 +54,3 @@ class UserModel extends ShieldUserModel } } ``` - -## Configuring to Use Your UserModel - -After creating the class, set your model classname to the `$userProvider` property -in **app/Config/Auth.php**: - -```php -public string $userProvider = \App\Models\UserModel::class; -``` From a88cc97302969cb5a22e9acebaed9755228da6f1 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sun, 17 Dec 2023 11:16:05 +0100 Subject: [PATCH 16/26] update docs color scheme --- docs/assets/css/codeigniter.css | 18 ++++++++++++++ ...ark_mode.css => codeigniter_dark_mode.css} | 24 ++++++++++++------- mkdocs.yml | 15 ++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 docs/assets/css/codeigniter.css rename docs/assets/css/{dark_mode.css => codeigniter_dark_mode.css} (77%) diff --git a/docs/assets/css/codeigniter.css b/docs/assets/css/codeigniter.css new file mode 100644 index 000000000..98952073d --- /dev/null +++ b/docs/assets/css/codeigniter.css @@ -0,0 +1,18 @@ +[data-md-color-scheme="codeigniter"] { + --md-primary-fg-color: #dd4814; + --md-primary-fg-color--light: #ECB7B7; + --md-primary-fg-color--dark: #90030C; + + --md-default-bg-color: #fcfcfc; + + --md-typeset-a-color: #e74c3c; + --md-accent-fg-color: #97310e; + + --md-accent-fg-color--transparent: #ECB7B7; + + --md-code-bg-color: #ffffff; + + .md-typeset code { + border: 1px solid #e1e4e5; + } +} diff --git a/docs/assets/css/dark_mode.css b/docs/assets/css/codeigniter_dark_mode.css similarity index 77% rename from docs/assets/css/dark_mode.css rename to docs/assets/css/codeigniter_dark_mode.css index 8bc72d6a8..88ff364be 100644 --- a/docs/assets/css/dark_mode.css +++ b/docs/assets/css/codeigniter_dark_mode.css @@ -1,8 +1,17 @@ [data-md-color-scheme="slate"] { - --md-primary-fg-color: #6a290d; + --md-primary-fg-color: #b13a10; --md-primary-fg-color--light: #8d7474; --md-primary-fg-color--dark: #6d554d; + --md-default-bg-color: #1e2129; + + --md-typeset-a-color: #ed6436; + --md-accent-fg-color: #f18a67; + + --md-accent-fg-color--transparent: #625151; + + --md-code-bg-color: #282b2d; + .hljs-title, .hljs-title.class_, .hljs-title.class_.inherited__, @@ -43,31 +52,30 @@ color: #ddba52 } - .md-typeset .note > .admonition-title, - .md-typeset .note > summary { - background-color: #0000001a; + .md-typeset code { + border: 1px solid #3f4547; } .md-typeset .admonition.note, .md-typeset details.note { - border-color: #675647; + border-color: #2c5293; } .md-typeset .note > .admonition-title:before, .md-typeset .note > summary:before { - background-color: #65686d; + background-color: #2c5293; -webkit-mask-image: var(--md-admonition-icon--note); mask-image: var(--md-admonition-icon--note); } .md-typeset .admonition.warning, .md-typeset details.warning { - border-color: #776144; + border-color: #97631e; } .md-typeset .warning > .admonition-title:before, .md-typeset .warning > summary:before { - background-color: #d9913bc2; + background-color: #97631e; -webkit-mask-image: var(--md-admonition-icon--warning); mask-image: var(--md-admonition-icon--warning); } diff --git a/mkdocs.yml b/mkdocs.yml index 24808cccf..b39c00e3f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,17 +12,17 @@ theme: palette: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" - scheme: default - primary: deep orange - accent: orange + scheme: codeigniter + primary: custom + accent: custom toggle: icon: material/brightness-7 name: Switch to dark mode # Palette toggle for dark mode - media: "(prefers-color-scheme: dark)" scheme: slate - primary: deep orange - accent: orange + primary: custom + accent: custom toggle: icon: material/brightness-4 name: Switch to light mode @@ -68,8 +68,9 @@ markdown_extensions: - pymdownx.details extra_css: - - https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.8.0/build/styles/default.min.css - - assets/css/dark_mode.css + - https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.8.0/build/styles/github.min.css + - assets/css/codeigniter.css + - assets/css/codeigniter_dark_mode.css extra_javascript: - https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.8.0/build/highlight.min.js From d3e43d811c146925b8abe92582d1b90241646d59 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sun, 17 Dec 2023 16:31:16 +0100 Subject: [PATCH 17/26] add CNAME --- docs/CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/CNAME diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..c30bf3c28 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +shield.codeigniter.com From a0ec47fd7dfc2116cbdf62ebca4a0592906d10b0 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Tue, 19 Dec 2023 07:13:43 -0600 Subject: [PATCH 18/26] docs: Remove emojis from index to be consistent with other docs in our packages (#983) --- docs/index.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/index.md b/docs/index.md index dc4865884..10a91e15e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,12 +1,12 @@ # Shield Documentation -## What is Shield? 🤔 +## What is Shield? Shield is the official authentication and authorization framework for CodeIgniter 4. While it does provide a base set of tools that are commonly used in websites, it is designed to be flexible and easily customizable. -### Primary Goals 🥅 +### Primary Goals The primary goals for Shield are: @@ -14,27 +14,27 @@ The primary goals for Shield are: 2. It must have security at its core. It is an auth lib after all. 3. To cover many auth needs right out of the box, but be simple to add additional functionality to. -### Important Features 🌠 +### Important Features -* **Session-based Authentication** (traditional **ID/Password** with **Remember-me**) -* **Stateless Authentication** using **Access Token**, **HMAC SHA256 Token**, or **JWT** -* Optional **Email verification** on account registration -* Optional **Email-based Two-Factor Authentication** after login -* **Magic Link Login** when a user forgets their password -* Flexible **Group-based Access Control** (think Roles, but more flexible), and users can be granted additional **Permissions** -* A simple **Auth Helper** that provides access to the most common auth actions -* Save initial settings in your code, so it can be in version control, but can also be updated in the database, thanks to our [Settings](https://github.com/codeigniter4/settings) library -* Highly configurable -* **User Entity** and **User Provider** (`UserModel`) ready for you to use or extend -* Built to extend and modify - * Easily extendable controllers - * All required views that can be used as is or swapped out for your own +- **Session-based Authentication** (traditional **ID/Password** with **Remember-me**) +- **Stateless Authentication** using **Access Token**, **HMAC SHA256 Token**, or **JWT** +- Optional **Email verification** on account registration +- Optional **Email-based Two-Factor Authentication** after login +- **Magic Link Login** when a user forgets their password +- Flexible **Group-based Access Control** (think Roles, but more flexible), and users can be granted additional **Permissions** +- A simple **Auth Helper** that provides access to the most common auth actions +- Save initial settings in your code, so it can be in version control, but can also be updated in the database, thanks to our [Settings](https://github.com/codeigniter4/settings) library +- Highly configurable +- **User Entity** and **User Provider** (`UserModel`) ready for you to use or extend +- Built to extend and modify + - Easily extendable controllers + - All required views that can be used as is or swapped out for your own -### License 📑 +### License Shield is licensed under the MIT License - see the [LICENSE](https://github.com/codeigniter4/shield/blob/develop/LICENSE) file for details. -### Acknowledgements 🙌🏼 +### Acknowledgements Every open-source project depends on it's contributors to be a success. The following users have contributed in one manner or another in making Shield: @@ -48,7 +48,7 @@ Made with [contrib.rocks](https://contrib.rocks). The following articles/sites have been fundamental in shaping the security and best practices used within this library, in no particular order: -- [Google Cloud: 13 best practices for user account, authentication, and password management, 2021 edition](https://cloud.google.com/blog/products/identity-security/account-authentication-and-password-management-best-practices) -- [NIST Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html) -- [Implementing Secure User Authentication in PHP Applications with Long-Term Persistence (Login with "Remember Me" Cookies) ](https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence) -- [Password Storage - OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html) +- [Google Cloud: 13 best practices for user account, authentication, and password management, 2021 edition](https://cloud.google.com/blog/products/identity-security/account-authentication-and-password-management-best-practices) +- [NIST Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html) +- [Implementing Secure User Authentication in PHP Applications with Long-Term Persistence (Login with "Remember Me" Cookies) ](https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence) +- [Password Storage - OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html) From 5bc4df33ec87bc89097f63392d51ed9abaacc45c Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Thu, 21 Dec 2023 00:09:03 -0600 Subject: [PATCH 19/26] Allow specifying namespace when generating routes. (#985) --- docs/customization/route_config.md | 10 ++++++++++ src/Auth.php | 4 +++- tests/Unit/AuthRoutesTest.php | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/customization/route_config.md b/docs/customization/route_config.md index 09a15da09..7b06c7d70 100644 --- a/docs/customization/route_config.md +++ b/docs/customization/route_config.md @@ -19,6 +19,16 @@ $routes->get('register', '\App\Controllers\Auth\RegisterController::registerView After customization, check your routes with the [spark routes](https://codeigniter.com/user_guide/incoming/routing.html#spark-routes) command. +## Change Namespace + +If you are overriding all of the auth controllers, you can specify the namespace as an option to the `routes()` helper: + +```php +service('auth')->routes($routes, ['namespace' => '\App\Controllers\Auth']); +``` + +This will generate the routes with the specified namespace instead of the default Shield namespace. This can be combined with any other options, like `except`. + ## Use Locale Routes You can use the `{locale}` placeholder in your routes diff --git a/src/Auth.php b/src/Auth.php index f64e7cbaa..dc4ebb6a9 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -138,7 +138,9 @@ public function routes(RouteCollection &$routes, array $config = []): void { $authRoutes = config('AuthRoutes')->routes; - $routes->group('/', ['namespace' => 'CodeIgniter\Shield\Controllers'], static function (RouteCollection $routes) use ($authRoutes, $config): void { + $namespace = $config['namespace'] ?? 'CodeIgniter\Shield\Controllers'; + + $routes->group('/', ['namespace' => $namespace], static function (RouteCollection $routes) use ($authRoutes, $config): void { foreach ($authRoutes as $name => $row) { if (! isset($config['except']) || ! in_array($name, $config['except'], true)) { foreach ($row as $params) { diff --git a/tests/Unit/AuthRoutesTest.php b/tests/Unit/AuthRoutesTest.php index 9012b731e..c0c5f2741 100644 --- a/tests/Unit/AuthRoutesTest.php +++ b/tests/Unit/AuthRoutesTest.php @@ -51,4 +51,16 @@ public function testRoutesExcept(): void $this->assertArrayHasKey('logout', $routes); $this->assertArrayHasKey('auth/a/show', $routes); } + + public function testRoutesCustomNamespace(): void + { + $collection = single_service('routes'); + $auth = service('auth'); + + $auth->routes($collection, ['namespace' => 'Auth']); + + $routes = $collection->getRoutes('get'); + + $this->assertSame('\Auth\RegisterController::registerView', $routes['register']); + } } From bfd3e2c3b540b76fbc08b2abbfebbce80fed3923 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:17:25 +0000 Subject: [PATCH 20/26] chore(deps-dev): update rector/rector requirement Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/0.18.12...0.18.13) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index afab5d1e4..5c89f271f 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "mockery/mockery": "^1.0", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-strict-rules": "^1.5", - "rector/rector": "0.18.12" + "rector/rector": "0.18.13" }, "provide": { "codeigniter4/authentication-implementation": "1.0" From 636b46f3eb66599f0688a4464e92103ed64e6176 Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 22 Dec 2023 10:08:53 +0100 Subject: [PATCH 21/26] docs: add site_url --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index b39c00e3f..db037c7b5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,7 +55,7 @@ extra: link: https://join.slack.com/t/codeigniterchat/shared_invite/zt-244xrrslc-l_I69AJSi5y2a2RVN~xIdQ name: Slack - +site_url: https://shield.codeigniter.com/ repo_url: https://github.com/codeigniter4/shield edit_uri: edit/develop/docs/ copyright: Copyright © 2023 CodeIgniter Foundation. From c8849279cfb649760d773622b390011542492608 Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 22 Dec 2023 10:09:26 +0100 Subject: [PATCH 22/26] docs: remove navigation.instant.prefetch feature --- mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index db037c7b5..bfd21dc99 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,7 +28,6 @@ theme: name: Switch to light mode features: - navigation.instant - - navigation.instant.prefetch - content.code.copy - navigation.footer - content.action.edit From d4400cd4d938f38460054b76ce1dee11ba5ef5d6 Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 22 Dec 2023 10:09:54 +0100 Subject: [PATCH 23/26] docs: fix hljs init --- docs/assets/js/hljs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/js/hljs.js b/docs/assets/js/hljs.js index 6f9098ac1..56159c4c9 100644 --- a/docs/assets/js/hljs.js +++ b/docs/assets/js/hljs.js @@ -1,3 +1,3 @@ -document.addEventListener('DOMContentLoaded', (event) => { +window.document$.subscribe(() => { hljs.highlightAll(); }); From 92ad2afb5a60a750dcf307cebd7154ae01f11fe1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Dec 2023 09:11:00 +0900 Subject: [PATCH 24/26] docs: composer cs-fix --- src/Views/email_2fa_show.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/email_2fa_show.php b/src/Views/email_2fa_show.php index b030cfde8..58d69ae54 100644 --- a/src/Views/email_2fa_show.php +++ b/src/Views/email_2fa_show.php @@ -22,7 +22,7 @@
+ value="email) ?>" required>
From e8252d6046506e201fede2e33566fcc05c5033dc Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Dec 2023 09:12:34 +0900 Subject: [PATCH 25/26] docs: add commands to prepare --- admin/RELEASE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/RELEASE.md b/admin/RELEASE.md index 5940307c3..123d57d6d 100644 --- a/admin/RELEASE.md +++ b/admin/RELEASE.md @@ -38,6 +38,8 @@ the changelog. * [ ] Clone **codeigniter4/shield** and resolve any necessary PRs ```console + rm -rf shield.bk + mv shield shield.bk git clone git@github.com:codeigniter4/shield.git ``` * [ ] Merge any Security Advisory PRs in private forks From 75068c164d4bd063d3a2fad6935e54663b92f014 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 26 Dec 2023 09:21:32 +0900 Subject: [PATCH 26/26] Prep for 1.0.0 release --- src/Auth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth.php b/src/Auth.php index dc4ebb6a9..a082c8e9a 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -41,7 +41,7 @@ class Auth /** * The current version of CodeIgniter Shield */ - public const SHIELD_VERSION = '1.0.0-beta.8'; + public const SHIELD_VERSION = '1.0.0'; protected AuthConfig $config; protected ?Authentication $authenticate = null;