Package net.shibboleth.idp.plugin.authn.webauthn.storage.impl

# Class IdPStorageServiceCredentialRepository

java.lang.Object
net.shibboleth.shared.component.AbstractInitializableComponent
net.shibboleth.shared.component.AbstractIdentifiedInitializableComponent
net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent
net.shibboleth.idp.plugin.authn.webauthn.storage.impl.IdPStorageServiceCredentialRepository

All Implemented Interfaces:
  `com.yubico.webauthn.CredentialRepository`, `WebAuthnCredentialRepository`, `Component`, `DestructableComponent`, `IdentifiableComponent`, `IdentifiedComponent`, `InitializableComponent`

Direct Known Subclasses:
  `StrategyBasedIdPStorageServiceCredentialRepository`

@ThreadSafeAfterInit public class IdPStorageServiceCredentialRepository extends AbstractIdentifiableInitializableComponent implements WebAuthnCredentialRepository
A `CredentialRepository` adaptor for the Shibboleth `StorageService`.

The IdP username (which might have been canonicalized by this point) is used as the key to the storage context. The object to store (value of the storage record) is a set of `registered credentials`. Storage records do not expire.

Any exception is wrapped in an unchecked `CredentialRepositoryException`. If the caller does not deem this terminal, they should catch and handle that error appropriately.

Secondary username indexes are stored in two different caches. The first stores userHandle to username mappings, the second stores credentialId to username mappings. The cache is updated immediately after a lookup or add operation. Cache entries are removed only when they are found to be invalid on lookup.

This repository is thread-safe after it is initialised.

## Nested Class Summary

Nested Classes
Modifier and Type
Class
Description
`private class`
`IdPStorageServiceCredentialRepository.VersionedCredentialSet`
A class to wrap a set of deserialized credentials alongside the storage record version reported by the storage service.

## Field Summary

Fields
Modifier and Type
Field
Description
`private CacheService`
`credentialIdMappingCacheService`
The service used to manage the credentialId to username mapping cache.
`private ReentrantReadWriteLock`
`lock`
A shared lock to synchronize access to read and write operations.
`private final org.slf4j.Logger`
`log`
Class logger.
`private StorageSerializer<Set<CredentialRecord>>`
`serializer`
Storage record serializer.
`protected static final String`
`STORAGE_CONTEXT`
The context to use to partition the storage records.
`private Function<String,List<StorageRecord<Set<CredentialRecord>>>>`
`storageRecordCacheLoader`
A cache loader to be used when this repository is initialised to load mappings into the cache.
`private EnumeratableStorageService`
`storageService`
The composed storage Service.
`private CacheService`
`userHandleMappingCacheService`
The service used to manage the userHandle to username mapping cache.

## Constructor Summary

Constructors
Constructor
Description
`IdPStorageServiceCredentialRepository()`
Package-private Constructor.

## Method Summary

Modifier and Type
Method
Description
`boolean`
`addRegistrationByUsername(String username, CredentialRecord reg)`
Add a new credential registration for a given user, by their username.
`private boolean`
`addRegistrationByUsernameImpl(String username, CredentialRecord registration)`
Add a new credential registration to the user.
`protected void`
`doDestroy()`
`protected void`
`doInitialize()`
`Set<CredentialRecord>`
`getAllRegistrations()`
Get all credential registrations known to the repository.
`protected CacheService`
`getCredentialIdMappingCacheService()`
Get the credentialId to username cache service for testing.
`Set<com.yubico.webauthn.data.PublicKeyCredentialDescriptor>`
`getCredentialIdsForUsername(String username)`
`protected ReentrantReadWriteLock`
`getLock()`
Return the read write lock to use.
`Optional<CredentialRecord>`
`getRegistrationByUserHandleAndCredentialId(com.yubico.webauthn.data.ByteArray credentialId, com.yubico.webauthn.data.ByteArray userHandle)`
Get the credential belonging to the user by userHandle and credentialID.
`Optional<CredentialRecord>`
`getRegistrationByUsernameAndCredentialId(String username, com.yubico.webauthn.data.ByteArray id)`
Get the credential belonging to the user by its credential identifier.
`protected Set<CredentialRecord>`
`getRegistrationsByCredentialId(com.yubico.webauthn.data.ByteArray credentialId)`
Get registrations from any user by credentialId.
`protected Collection<CredentialRecord>`
`getRegistrationsByUserHandle(com.yubico.webauthn.data.ByteArray userHandle)`
Get registrations from any user by userHandle.
`Set<CredentialRecord>`
`getRegistrationsByUsername(String username)`
Get credential registrations by username.
`private IdPStorageServiceCredentialRepository.VersionedCredentialSet`
`getRegistrationsByUsernameWithVersion(String username)`
Get credential registrations by username and return the storage record version.
`protected StorageSerializer<Set<CredentialRecord>>`
`getSerializer()`
Get the storage serializer to use.
`Optional<com.yubico.webauthn.data.ByteArray>`
`getUserHandleForUsername(String username)`
`protected CacheService`
`getUserHandleMappingCacheService()`
Get the userHandle to username cache service for testing.
`Optional<String>`
`getUsernameForUserHandle(com.yubico.webauthn.data.ByteArray userHandle)`
`protected String`
`getUsernameFromCredentialIdCache(com.yubico.webauthn.data.ByteArray credentialId)`
Return the username for the credentialId from the cache.
`protected String`
`getUsernameFromUserHandleCache(com.yubico.webauthn.data.ByteArray userHandle)`
Return the username for the given userHandle from the cache.
`private void`
`initialCacheLoad()`
Perform initial cache load from the configured cache loader.
`Optional<com.yubico.webauthn.RegisteredCredential>`
`lookup(com.yubico.webauthn.data.ByteArray credentialId, com.yubico.webauthn.data.ByteArray userHandle)`
`Set<com.yubico.webauthn.RegisteredCredential>`
`lookupAll(com.yubico.webauthn.data.ByteArray credentialId)`
`protected void`
`reloadCache(List<StorageRecord<Set<CredentialRecord>>> records)`
Reload the cache.
`int`
`removeRegistrationByCredentialId(com.yubico.webauthn.data.ByteArray credentialId)`
Remove the credential by its identifier for any user it is registered for.
`boolean`
`removeRegistrationByUsername(String username, CredentialRecord credentialRegistration)`
Remove the registration for the given user.
`boolean`
`removeRegistrationByUsernameAndCredentialId(String username, com.yubico.webauthn.data.ByteArray credentialId)`
Remove the credential with the given credential Id for the given user.
`void`
`setCredentialIdMappingCacheService(CacheService cache)`
Set the credentialId to username cache service.
`void`
`setSerializer(StorageSerializer<Set<CredentialRecord>> storageSerializer)`
Set the storage service serializer to handle `CredentialRecord`s.
`void`
`setStorageRecordCacheLoader(Function<String,List<StorageRecord<Set<CredentialRecord>>>> loader)`
Set a loader that loads credential records to be cached on initialisation of this storage service.
`void`
`setStorageService(StorageService service)`
Set the storage service to store credentials.
`void`
`setUserHandleMappingCacheService(CacheService cache)`
Set the userHandle to username cache service.
`protected void`
`updateCache(CredentialRecord update)`
Update the cache with the userHandle and username mappings found in the credential record.
`protected void`
`updateCredentialIdCache(Collection<CredentialRecord> updates)`
Update the cache with the credentialId to username mappings found in the credential record set.
`boolean`
`updateLastUsedTime(String username, com.yubico.webauthn.data.ByteArray credentialId, Instant lastUsedTime)`
Update the last used time for the credential that belongs to the given user.
`boolean`
`updateNickname(String username, com.yubico.webauthn.data.ByteArray credentialId, String nickname)`
Update the nickname for the credential that belongs to the given user.
`boolean`
`updateSignatureCounter(String username, com.yubico.webauthn.data.ByteArray credentialId, long newSignatureCount)`
Update the signature counter of the credential that belongs to the given user.
`protected void`
`updateUserHandleCache(Collection<CredentialRecord> updates)`
Update the cache with the userHandle and username mappings found in the credential record set.

### Methods inherited from class net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent

`setId`

### Methods inherited from class net.shibboleth.shared.component.AbstractIdentifiedInitializableComponent

`ensureId, getId, ifDestroyedThrowDestroyedComponentException, ifInitializedThrowUnmodifiabledComponentException, ifNotInitializedThrowUninitializedComponentException`

### Methods inherited from class net.shibboleth.shared.component.AbstractInitializableComponent

`checkComponentActive, checkSetterPreconditions, destroy, initialize, isDestroyed, isInitialized`

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

### Methods inherited from interface net.shibboleth.shared.component.IdentifiedComponent

`getId`

## Field Details

### STORAGE_CONTEXT

@Nonnull @NotEmpty protected static final String STORAGE_CONTEXT
The context to use to partition the storage records.

See Also:
  - Constant Field Values

### log

@Nonnull private final org.slf4j.Logger log
Class logger.

### serializer

@NonnullAfterInit private StorageSerializer<Set<CredentialRecord>> serializer
Storage record serializer.

### storageService

@NonnullAfterInit private EnumeratableStorageService storageService
The composed storage Service.

### lock

@NonnullAfterInit private ReentrantReadWriteLock lock
A shared lock to synchronize access to read and write operations. Needs to be a reentrant type so a read can occur within a write lock (by the same thread).

### userHandleMappingCacheService

@NonnullAfterInit private CacheService userHandleMappingCacheService
The service used to manage the userHandle to username mapping cache.

### credentialIdMappingCacheService

@NonnullAfterInit private CacheService credentialIdMappingCacheService
The service used to manage the credentialId to username mapping cache.

### storageRecordCacheLoader

@Nonnull private Function<String,List<StorageRecord<Set<CredentialRecord>>>> storageRecordCacheLoader
A cache loader to be used when this repository is initialised to load mappings into the cache. Does nothing by default.

## Constructor Details

### IdPStorageServiceCredentialRepository

IdPStorageServiceCredentialRepository()
Package-private Constructor.

Should only be instantiated by the `StorageServiceCredentialRepositoryFactory`.

## Method Details

### getLock

@NonnullAfterInit protected ReentrantReadWriteLock getLock()
Return the read write lock to use.

Returns:
  the lock.

### setUserHandleMappingCacheService

public void setUserHandleMappingCacheService(@Nonnull CacheService cache)
Set the userHandle to username cache service.

Parameters:
  `cache` - The userHandleMappingCache to set.

### getUserHandleMappingCacheService

@NonnullAfterInit protected CacheService getUserHandleMappingCacheService()
Get the userHandle to username cache service for testing.

Returns:
  the userHandle to username map

### setCredentialIdMappingCacheService

public void setCredentialIdMappingCacheService(@Nonnull CacheService cache)
Set the credentialId to username cache service.

Parameters:
  `cache` - The userHandleMappingCache to set.

### getCredentialIdMappingCacheService

@NonnullAfterInit protected CacheService getCredentialIdMappingCacheService()
Get the credentialId to username cache service for testing.

Returns:
  the userHandle to username map

### setStorageRecordCacheLoader

public void setStorageRecordCacheLoader(@Nonnull Function<String,List<StorageRecord<Set<CredentialRecord>>>> loader)
Set a loader that loads credential records to be cached on initialisation of this storage service.

Parameters:
  `loader` - The initial cache loader to set.

### setStorageService

public void setStorageService(@Nonnull StorageService service)
Set the storage service to store credentials.

Parameters:
  `service` - the storageService to set.

### setSerializer

public void setSerializer(@Nonnull StorageSerializer<Set<CredentialRecord>> storageSerializer)
Set the storage service serializer to handle `CredentialRecord`s.

Parameters:
  `storageSerializer` - the serializer to set.

### getSerializer

@NonnullAfterInit protected StorageSerializer<Set<CredentialRecord>> getSerializer()
Get the storage serializer to use.

Returns:
  the serializer.

### doDestroy

protected void doDestroy()

Overrides:
  `doDestroy` in class `AbstractInitializableComponent`

### doInitialize

protected void doInitialize() throws ComponentInitializationException

Overrides:
  `doInitialize` in class `AbstractIdentifiedInitializableComponent`
Throws:
  `ComponentInitializationException`

### initialCacheLoad

private void initialCacheLoad()
Perform initial cache load from the configured cache loader.

### getUsernameFromUserHandleCache

@Nullable protected String getUsernameFromUserHandleCache(@Nonnull com.yubico.webauthn.data.ByteArray userHandle)
Return the username for the given userHandle from the cache.

Checks if the mapping is still consistent, by looking up the credential by the cached username and validates it references the same userHandle and still contains the same username.

If the cache returns `null`, either the cache entry does not exist, or the cache is not enabled. `null` does not indicate there is no username for the given userHandle in the storage service.

Parameters:
  `userHandle` - the userHandle to find
Returns:
  the username if found in the cache or `null` if the username is either not found in the cache or the mapping is found to be inconsistent.

### getUsernameFromCredentialIdCache

@Nullable protected String getUsernameFromCredentialIdCache(@Nonnull com.yubico.webauthn.data.ByteArray credentialId)
Return the username for the credentialId from the cache.

Checks if the mapping is still consistent, by looking up the credential by the cached username and validates it references the same credentialId and still contains the same username.

If the cache returns `null`, either the cache entry does not exist, or the cache is not enabled. `null` does not indicate there is no username for the given credentialId in the storage service.

Parameters:
  `credentialId` - the credentialId to find
Returns:
  the username if found in the cache or `null` if the username is either not found in the cache or the mapping is found to be inconsistent.

### reloadCache

protected void reloadCache(@Nonnull List<StorageRecord<Set<CredentialRecord>>> records)
Reload the cache. First, the cache is cleared. Then, all mappings from the set of credential records are added.

Parameters:
  `records` - the credential records to update the cache with.

### updateUserHandleCache

protected void updateUserHandleCache(@Nonnull Collection<CredentialRecord> updates)
Update the cache with the userHandle and username mappings found in the credential record set.

Parameters:
  `updates` - the credential registrations used to update the cache.

### updateCredentialIdCache

protected void updateCredentialIdCache(@Nonnull Collection<CredentialRecord> updates)
Update the cache with the credentialId to username mappings found in the credential record set.

Parameters:
  `updates` - the credential registrations used to update the cache.

### updateCache

protected void updateCache(@Nonnull CredentialRecord update)
Update the cache with the userHandle and username mappings found in the credential record.

Parameters:
  `update` - the credential registration used to update the cache.

### getCredentialIdsForUsername

public Set<com.yubico.webauthn.data.PublicKeyCredentialDescriptor> getCredentialIdsForUsername(@Nullable String username)

Specified by:
  `getCredentialIdsForUsername` in interface `com.yubico.webauthn.CredentialRepository`

### getUserHandleForUsername

public Optional<com.yubico.webauthn.data.ByteArray> getUserHandleForUsername(@Nullable String username)

Specified by:
  `getUserHandleForUsername` in interface `com.yubico.webauthn.CredentialRepository`

### getUsernameForUserHandle

public Optional<String> getUsernameForUserHandle(com.yubico.webauthn.data.ByteArray userHandle)

Specified by:
  `getUsernameForUserHandle` in interface `com.yubico.webauthn.CredentialRepository`

### lookup

public Optional<com.yubico.webauthn.RegisteredCredential> lookup(com.yubico.webauthn.data.ByteArray credentialId, com.yubico.webauthn.data.ByteArray userHandle)

Specified by:
  `lookup` in interface `com.yubico.webauthn.CredentialRepository`

### getRegistrationsByUserHandle

protected Collection<CredentialRecord> getRegistrationsByUserHandle(com.yubico.webauthn.data.ByteArray userHandle)
Get registrations from any user by userHandle.

Parameters:
  `userHandle` - the userHandle to match
Returns:
  registrations that match the userHandle from any user.

### lookupAll

@Nonnull public Set<com.yubico.webauthn.RegisteredCredential> lookupAll(com.yubico.webauthn.data.ByteArray credentialId)

Specified by:
  `lookupAll` in interface `com.yubico.webauthn.CredentialRepository`

### getRegistrationsByCredentialId

@Nonnull @Unmodifiable protected Set<CredentialRecord> getRegistrationsByCredentialId(com.yubico.webauthn.data.ByteArray credentialId)
Get registrations from any user by credentialId.

Parameters:
  `credentialId` - the credentialId to match
Returns:
  registrations that match the credentialId.

### getRegistrationsByUsername

@Nonnull public Set<CredentialRecord> getRegistrationsByUsername(String username)
Get credential registrations by username.

Specified by:
  `getRegistrationsByUsername` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the username to find registrations for
Returns:
  the set of registered credentials associated to the user

### getRegistrationsByUsernameWithVersion

@Nonnull private IdPStorageServiceCredentialRepository.VersionedCredentialSet getRegistrationsByUsernameWithVersion(String username)
Get credential registrations by username and return the storage record version.

Parameters:
  `username` - the username to find registrations for
Returns:
  the set of registered credentials associated to the user and the version of the storage record

### getRegistrationByUsernameAndCredentialId

@Nonnull public Optional<CredentialRecord> getRegistrationByUsernameAndCredentialId(String username, com.yubico.webauthn.data.ByteArray id)
Get the credential belonging to the user by its credential identifier.

Specified by:
  `getRegistrationByUsernameAndCredentialId` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the username to find the credential for
  `id` - the identifier of the credential to find
Returns:
  the credential if found, otherwise an empty `Optional`.

### getRegistrationByUserHandleAndCredentialId

@Nonnull public Optional<CredentialRecord> getRegistrationByUserHandleAndCredentialId(com.yubico.webauthn.data.ByteArray credentialId, com.yubico.webauthn.data.ByteArray userHandle)
Get the credential belonging to the user by userHandle and credentialID.

Specified by:
  `getRegistrationByUserHandleAndCredentialId` in interface `WebAuthnCredentialRepository`
Parameters:
  `credentialId` - the identifier of the credential to find
  `userHandle` - the user handle (user.id)
Returns:
  the credential registration if found.

### addRegistrationByUsername

public boolean addRegistrationByUsername(@Nonnull String username, @Nonnull CredentialRecord reg)
Add a new credential registration for a given user, by their username.

In addition to the basic functionality, add the new registration to the cache.

Specified by:
  `addRegistrationByUsername` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the user to add the registration for
  `reg` - the credential to add
Returns:
  true iff the registration was added. False otherwise.

### addRegistrationByUsernameImpl

private boolean addRegistrationByUsernameImpl(@Nonnull String username, @Nonnull CredentialRecord registration)
Add a new credential registration to the user.

Parameters:
  `username` - the user to add the registration too
  `registration` - the credential to add
Returns:
  true if the credential was added, false otherwise

### removeRegistrationByUsername

public boolean removeRegistrationByUsername(String username, CredentialRecord credentialRegistration)
Description copied from interface: `WebAuthnCredentialRepository`
Remove the registration for the given user.

Specified by:
  `removeRegistrationByUsername` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the user to remove the registration for
  `credentialRegistration` - the credential to remove
Returns:
  true iff the credential was removed, false otherwise.

### removeRegistrationByCredentialId

public int removeRegistrationByCredentialId(com.yubico.webauthn.data.ByteArray credentialId)
Remove the credential by its identifier for any user it is registered for. This should, in practice, only be registered to a single user.

Specified by:
  `removeRegistrationByCredentialId` in interface `WebAuthnCredentialRepository`
Parameters:
  `credentialId` - the credential to remove
Returns:
  the number of credentials that have been removed. Should be 1 if removed from a single user, more than 1 if removed from more than one user, and 0 if not removed from any user.

### removeRegistrationByUsernameAndCredentialId

public boolean removeRegistrationByUsernameAndCredentialId(String username, com.yubico.webauthn.data.ByteArray credentialId)
Remove the credential with the given credential Id for the given user.

Specified by:
  `removeRegistrationByUsernameAndCredentialId` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the user to remove the credential from
  `credentialId` - the identifier of the credential to remove
Returns:
  true iff the credential was remove, false otherwise.

### updateSignatureCounter

public boolean updateSignatureCounter(@Nonnull String username, @Nonnull com.yubico.webauthn.data.ByteArray credentialId, long newSignatureCount)
Update the signature counter of the credential that belongs to the given user.

Specified by:
  `updateSignatureCounter` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the username of the user to update the signature counter for
  `credentialId` - the identifier of the credential to update the signature counter for
  `newSignatureCount` - the new signature counter value
Returns:
  true iff the registration was updated, false otherwise.

### updateLastUsedTime

public boolean updateLastUsedTime(@Nonnull String username, @Nonnull com.yubico.webauthn.data.ByteArray credentialId, Instant lastUsedTime)
Update the last used time for the credential that belongs to the given user.

Specified by:
  `updateLastUsedTime` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the username of the user to update the last used time for
  `credentialId` - the identifier of the credential to update the last used time for
  `lastUsedTime` - the new last used time value
Returns:
  true iff the registration was updated, false otherwise.

### updateNickname

public boolean updateNickname(@Nonnull String username, @Nonnull com.yubico.webauthn.data.ByteArray credentialId, String nickname)
Update the nickname for the credential that belongs to the given user.

Specified by:
  `updateNickname` in interface `WebAuthnCredentialRepository`
Parameters:
  `username` - the username of the user to update the nickname for
  `credentialId` - the identifier of the credential to update the nickname for
  `nickname` - the new nickname of the credential
Returns:
  true iff the registration was updated, false otherwise.

### getAllRegistrations

@Nonnull @NotLive @Unmodifiable public Set<CredentialRecord> getAllRegistrations()
Get all credential registrations known to the repository. Typically used to provide an administrative view of the repository.

Specified by:
  `getAllRegistrations` in interface `WebAuthnCredentialRepository`
Returns:
  all credential registrations known to the system.