Skip to content

Commit 55e0b34

Browse files
committed
Add caching capabilities to UserRepository
1 parent fe49beb commit 55e0b34

File tree

6 files changed

+63
-7
lines changed

6 files changed

+63
-7
lines changed

config-templates/module_oidc.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,13 @@
288288
// 60 * 60 * 6, // Default lifetime in seconds (used when particular cache item doesn't define its own lifetime)
289289
//],
290290

291+
// Cache duration for user entities (authenticated users data). If not set, cache duration will be the same as
292+
// session duration. This is used to avoid fetching user data from database on every authentication event.
293+
// This is only relevant if protocol cache adapter is set up. For duration format info, check
294+
// https://www.php.net/manual/en/dateinterval.construct.php.
295+
// ModuleConfig::OPTION_PROTOCOL_USER_ENTITY_CACHE_DURATION => 'PT1H', // 1 hour
296+
ModuleConfig::OPTION_PROTOCOL_USER_ENTITY_CACHE_DURATION => null, // fallback to session duration
297+
291298
/**
292299
* Cron related options.
293300
*/

routing/services/services.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,8 @@ services:
115115
SimpleSAML\OpenID\Federation:
116116
factory: [ '@SimpleSAML\Module\oidc\Factories\FederationFactory', 'build' ]
117117
SimpleSAML\OpenID\Jwks:
118-
factory: [ '@SimpleSAML\Module\oidc\Factories\JwksFactory', 'build' ]
118+
factory: [ '@SimpleSAML\Module\oidc\Factories\JwksFactory', 'build' ]
119+
120+
# SSP
121+
SimpleSAML\Database:
122+
factory: [ 'SimpleSAML\Database', 'getInstance' ]

src/Controller/Federation/Test.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace SimpleSAML\Module\oidc\Controller\Federation;
88

9+
use SimpleSAML\Database;
910
use SimpleSAML\Module\oidc\Codebooks\RegistrationTypeEnum;
1011
use SimpleSAML\Module\oidc\Factories\CoreFactory;
1112
use SimpleSAML\Module\oidc\Factories\Entities\ClientEntityFactory;
@@ -33,6 +34,7 @@ public function __construct(
3334
protected ?FederationCache $federationCache,
3435
protected LoggerService $loggerService,
3536
protected Jwks $jwks,
37+
protected Database $database,
3638
protected ClientEntityFactory $clientEntityFactory,
3739
protected CoreFactory $coreFactory,
3840
protected \DateInterval $maxCacheDuration = new \DateInterval('PT30S'),

src/ModuleConfig.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class ModuleConfig
8080
final public const OPTION_FEDERATION_ENTITY_STATEMENT_CACHE_DURATION = 'federation_entity_statement_cache_duration';
8181
final public const OPTION_PROTOCOL_CACHE_ADAPTER = 'protocol_cache_adapter';
8282
final public const OPTION_PROTOCOL_CACHE_ADAPTER_ARGUMENTS = 'protocol_cache_adapter_arguments';
83+
final public const OPTION_PROTOCOL_USER_ENTITY_CACHE_DURATION = 'protocol_user_entity_cache_duration';
8384

8485
protected static array $standardScopes = [
8586
ScopesEnum::OpenId->value => [
@@ -630,4 +631,20 @@ public function getProtocolCacheAdapterArguments(): array
630631
{
631632
return $this->config()->getOptionalArray(self::OPTION_PROTOCOL_CACHE_ADAPTER_ARGUMENTS, []);
632633
}
634+
635+
/**
636+
* Get cache duration for user entities (user data). If not set in configuration, it will fall back to SSP session
637+
* duration.
638+
*
639+
* @throws \Exception
640+
*/
641+
public function getUserEntityCacheDuration(): DateInterval
642+
{
643+
return new DateInterval(
644+
$this->config()->getOptionalString(
645+
self::OPTION_PROTOCOL_USER_ENTITY_CACHE_DURATION,
646+
null,
647+
) ?? "PT{$this->sspConfig()->getInteger('session.duration')}S",
648+
);
649+
}
633650
}

src/Repositories/UserRepository.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public function getTableName(): string
4848
return $this->database->applyPrefix(self::TABLE_NAME);
4949
}
5050

51+
public function getCacheKey(string $identifier): string
52+
{
53+
return $this->getTableName() . '_' . $identifier;
54+
}
55+
5156
/**
5257
* @param string $identifier
5358
*
@@ -56,6 +61,13 @@ public function getTableName(): string
5661
*/
5762
public function getUserEntityByIdentifier(string $identifier): ?UserEntity
5863
{
64+
/** @var ?array $cachedState */
65+
$cachedState = $this->protocolCache?->get(null, $this->getCacheKey($identifier));
66+
67+
if (is_array($cachedState)) {
68+
return $this->userEntityFactory->fromState($cachedState);
69+
}
70+
5971
$stmt = $this->database->read(
6072
"SELECT * FROM {$this->getTableName()} WHERE id = :id",
6173
[
@@ -99,21 +111,29 @@ public function add(UserEntity $userEntity): void
99111
$stmt,
100112
$userEntity->getState(),
101113
);
114+
115+
$this->protocolCache?->set(
116+
$userEntity->getState(),
117+
$this->moduleConfig->getUserEntityCacheDuration(),
118+
$this->getCacheKey($userEntity->getIdentifier()),
119+
);
102120
}
103121

104-
public function delete(UserEntity $user): void
122+
public function delete(UserEntity $userEntity): void
105123
{
106124
$this->database->write(
107125
"DELETE FROM {$this->getTableName()} WHERE id = :id",
108126
[
109-
'id' => $user->getIdentifier(),
127+
'id' => $userEntity->getIdentifier(),
110128
],
111129
);
130+
131+
$this->protocolCache?->delete($this->getCacheKey($userEntity->getIdentifier()));
112132
}
113133

114-
public function update(UserEntity $user, ?DateTimeImmutable $updatedAt = null): void
134+
public function update(UserEntity $userEntity, ?DateTimeImmutable $updatedAt = null): void
115135
{
116-
$user->setUpdatedAt($updatedAt ?? $this->helpers->dateTime()->getUtc());
136+
$userEntity->setUpdatedAt($updatedAt ?? $this->helpers->dateTime()->getUtc());
117137

118138
$stmt = sprintf(
119139
"UPDATE %s SET claims = :claims, updated_at = :updated_at, created_at = :created_at WHERE id = :id",
@@ -122,7 +142,13 @@ public function update(UserEntity $user, ?DateTimeImmutable $updatedAt = null):
122142

123143
$this->database->write(
124144
$stmt,
125-
$user->getState(),
145+
$userEntity->getState(),
146+
);
147+
148+
$this->protocolCache?->set(
149+
$userEntity->getState(),
150+
$this->moduleConfig->getUserEntityCacheDuration(),
151+
$this->getCacheKey($userEntity->getIdentifier()),
126152
);
127153
}
128154
}

tests/integration/src/Repositories/Traits/RevokeTokenByAuthCodeIdTraitTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public function getDatabase(): Database
168168
$moduleConfig,
169169
$database,
170170
null,
171-
$clientEntityFactoryMock
171+
$clientEntityFactoryMock,
172172
);
173173

174174
$this->accessTokenRepository = new AccessTokenRepository(

0 commit comments

Comments
 (0)