diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8c77114cc..b4186e33f 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -12,7 +12,8 @@ parameters: ignoreErrors: - '#Call to an undefined method CodeIgniter\\Database\\ConnectionInterface::[A-Za-z].+\(\)#' - '#Cannot access property [\$a-z_]+ on (array|object)#' - - + - '#Call to an undefined method CodeIgniter\\Shield\\Models\\UserModel::getLastQuery\(\)#' + - message: '#Call to deprecated function random_string\(\):#' paths: - src/Authentication/Actions/Email2FA.php diff --git a/src/Entities/User.php b/src/Entities/User.php index 00a420fe7..2c966e424 100644 --- a/src/Entities/User.php +++ b/src/Entities/User.php @@ -117,6 +117,11 @@ public function getIdentities(string $type = 'all'): array return $identities; } + public function setIdentities(array $identities): void + { + $this->identities = $identities; + } + /** * Creates a new identity for this user with an email/password * combination. diff --git a/src/Models/UserModel.php b/src/Models/UserModel.php index ab5eb49c2..3c6f5f518 100644 --- a/src/Models/UserModel.php +++ b/src/Models/UserModel.php @@ -113,7 +113,8 @@ protected function fetchIdentities(array $data): array */ private function assignIdentities(array $data, array $identities): array { - $mappedUsers = []; + $mappedUsers = []; + $userIdentities = []; $users = $data['singleton'] ? [$data['data']] : $data['data']; @@ -122,15 +123,17 @@ private function assignIdentities(array $data, array $identities): array } unset($users); - // Now assign the identities to the user + // Now group the identities by user foreach ($identities as $identity) { - $userId = $identity->user_id; - - $newIdentities = $mappedUsers[$userId]->identities; - $newIdentities[] = $identity; + $userIdentities[$identity->user_id][] = $identity; + } + unset($identities); - $mappedUsers[$userId]->identities = $newIdentities; + // Now assign the identities to the user + foreach ($userIdentities as $userId => $identityArray) { + $mappedUsers[$userId]->identities = $identityArray; } + unset($userIdentities); return $mappedUsers; } diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php index b10860ebc..784800063 100644 --- a/tests/Unit/UserTest.php +++ b/tests/Unit/UserTest.php @@ -87,6 +87,35 @@ public function testModelFindByIdWithIdentities(): void $this->assertCount(2, $user->identities); } + public function testModelFindAllWithIdentitiesWhereInQuery(): void + { + fake(UserIdentityModel::class, ['user_id' => $this->user->id, 'type' => 'password']); + fake(UserIdentityModel::class, ['user_id' => $this->user->id, 'type' => 'access_token']); + + // Grab the user again, using the model's identity helper + $users = model(UserModel::class)->withIdentities()->findAll(); + + $identities = []; + + foreach ($users as $user) { + if ($user->id !== $this->user->id) { + continue; + } + + $identities = $user->identities; + + // Check the last query and see if a proper type of query was used + $query = (string) model(UserModel::class)->getLastQuery(); + $this->assertMatchesRegularExpression( + '/WHERE\s+.*\s+IN\s+\([^)]+\)/i', + $query, + 'Identities were not obtained with the single query (missing "WHERE ... IN" condition)' + ); + } + + $this->assertCount(2, $identities); + } + public function testModelFindByIdWithIdentitiesUserNotExists(): void { $user = model(UserModel::class)->where('active', 0)->withIdentities()->findById(1);