public class AdminUserPersistenceHandler extends OperationAwarePersistenceHandler
AdminUser
data to update User
.
This is designed to perform a replacement of existing data within this service from the admin
user service.Constructor and Description |
---|
AdminUserPersistenceHandler(UserService<User> userService,
AuthorizedClientService<AuthorizedClient> clientService,
com.fasterxml.jackson.databind.ObjectMapper mapper,
com.broadleafcommerce.common.extension.TypeFactory typeFactory,
UserLockoutService userLockoutService) |
Modifier and Type | Method and Description |
---|---|
protected void |
attemptCreateOrThrow(@NonNull User newUser)
For creation, we need a guarantee that only one attempt will succeed even in the event of
concurrent duplicate messages or out-of-order messages.
|
protected void |
attemptReplaceOrThrow(@NonNull String id,
@NonNull User replacement,
@NonNull Instant changeTimestamp)
We cannot perform a normal "create or update" save operation here due to concurrency
concerns.
|
protected User |
buildUserForCreate(AdminUser request,
Instant lastUpdated) |
protected AdminUser |
deserialize(com.fasterxml.jackson.databind.JsonNode jsonNode) |
protected Optional<User> |
findExistingAdminUser(String serviceId) |
protected String |
getAuthorizationServerId(AdminUser request) |
protected AuthorizedClientService<AuthorizedClient> |
getClientService() |
String[] |
getSupportedSimpleTypeNames() |
protected com.broadleafcommerce.common.extension.TypeFactory |
getTypeFactory() |
protected UserLockoutService |
getUserLockoutService() |
protected UserService<User> |
getUserService() |
void |
hook(String entityJson) |
protected void |
logReceiptOfPersistenceMessage(@NonNull com.broadleafcommerce.data.tracking.core.type.OperationType operationType,
@NonNull Instant changeTimestamp,
@NonNull String entityId) |
protected boolean |
mapUpdatesFromRequest(AdminUser request,
User target)
Perform any necessary updates of the admin user.
|
protected void |
modifyForArchival(User target)
Mimic what
UserService.archive(String) would do to mark this record as archived. |
protected void |
performArchivalReplacementIfEligible(@NonNull User existing,
@NonNull Instant changeTimestamp) |
protected void |
performReplacementIfEligible(@NonNull User existing,
@NonNull AdminUser request,
@NonNull Instant changeTimestamp) |
protected void |
processCreateOperation(@NonNull String entityId,
@NonNull Instant changeTimestamp,
@NonNull com.fasterxml.jackson.databind.JsonNode entityJson)
Invoked if the persistence message had an
OperationType of
OperationType.CREATE . |
protected void |
processDeleteOperation(@NonNull String entityId,
@NonNull Instant changeTimestamp,
@NonNull com.fasterxml.jackson.databind.JsonNode entityJson)
Archives the given entity.
|
protected void |
processUpdateOperation(@NonNull String entityId,
@NonNull Instant changeTimestamp,
@NonNull com.fasterxml.jackson.databind.JsonNode entityJson)
Invoked if the persistence message had an
OperationType of
OperationType.UPDATE . |
protected UserPermissionRef |
toAuthPermissionRef(AdminPermissionRef adminPermissionRef) |
protected RestrictedPermission |
toAuthRestrictedPermission(AdminRestrictedPermission adminRestrictedPermission) |
protected RestrictedRole |
toAuthRestrictedRole(AdminRestrictedRole adminRestrictedRole) |
protected Restriction |
toAuthRestriction(AdminRestriction adminRestriction) |
protected Restriction |
toAuthRestriction(String restrictionType,
Set<String> restrictionTargets) |
protected UserRoleRef |
toAuthRoleRef(AdminRoleRef adminRoleRef) |
protected boolean |
updatePermissions(AdminUser request,
User user)
We will not validate these references by querying for them, as the associated permissions may
not yet be synchronized to the authentication service.
|
protected boolean |
updateUserInfo(AdminUser request,
User user) |
protected boolean |
updateUserRestrictedPermissions(AdminUser request,
User user) |
protected boolean |
updateUserRestrictedRoles(AdminUser request,
User user) |
protected boolean |
updateUserRestrictions(AdminUser request,
User user) |
protected boolean |
updateUserRoles(AdminUser request,
User user)
We will not validate these references by querying for them, as the associated roles may not
yet be synchronized to the authentication service.
|
protected boolean |
updateUserStatus(AdminUser request,
User user) |
protected boolean |
updateUserTenantRestrictions(AdminUser request,
User user) |
getChangeTimestamp, getEntityIdOrThrow, getNonNullField, getValidOperationTypeOrThrow, processStateChange, processStateChange
getObjectMapper, handle, isNodeNull, isValidType
public AdminUserPersistenceHandler(UserService<User> userService, AuthorizedClientService<AuthorizedClient> clientService, com.fasterxml.jackson.databind.ObjectMapper mapper, com.broadleafcommerce.common.extension.TypeFactory typeFactory, UserLockoutService userLockoutService)
@StreamListener(value="persistenceInputAdminUser") public void hook(String entityJson)
protected void processCreateOperation(@NonNull @NonNull String entityId, @NonNull @NonNull Instant changeTimestamp, @NonNull @NonNull com.fasterxml.jackson.databind.JsonNode entityJson) throws IOException
OperationAwarePersistenceHandler
OperationType
of
OperationType.CREATE
.processCreateOperation
in class OperationAwarePersistenceHandler
entityId
- the ID found on the persistence message. Guaranteed non-empty.changeTimestamp
- the timestamp found on the persistence message. Guaranteed non-null.entityJson
- the persistence message json itself. Guaranteed non-null.IOException
protected boolean mapUpdatesFromRequest(AdminUser request, User target)
request
- The updated admin usertarget
- The user to apply changes toprotected AdminUser deserialize(com.fasterxml.jackson.databind.JsonNode jsonNode) throws IOException
IOException
protected void attemptCreateOrThrow(@NonNull @NonNull User newUser)
Thus, we need to use a method here that will create a record with the given non-null
User.serviceId
and User.type
with the minimum guarantee that only one record
with the given values will ever be created (even if the requests are highly concurrent).
This means that create can fail for two main reasons, either another operation beat us to it
or one of the entity's references (ex: User.getRoles()
) is not present. In either
case, the exception thrown by this method should be bubbled up to trigger a retry. If it was
due to another operation concurrently creating the record, the retry will resolve quietly
after the eager fetch (in processCreateOperation(String, Instant, JsonNode)
,
processUpdateOperation(String, Instant, JsonNode)
, or
processDeleteOperation(String, Instant, JsonNode)
) finds the now-existing record and
compares against its lastUpdated. If the error was due to missing references, the retry will
keep trying until the referenced records have been created.
TODO https://github.com/BroadleafCommerce/AuthenticationServices/issues/182
newUser
- the entity to create. Must have a non-null User.serviceId
,
User.type
, and User.lastUpdated
.RuntimeException
- if the create failed due to a concurrent create happening, or if one
of the entity's references was not foundprotected void logReceiptOfPersistenceMessage(@NonNull @NonNull com.broadleafcommerce.data.tracking.core.type.OperationType operationType, @NonNull @NonNull Instant changeTimestamp, @NonNull @NonNull String entityId)
protected void processUpdateOperation(@NonNull @NonNull String entityId, @NonNull @NonNull Instant changeTimestamp, @NonNull @NonNull com.fasterxml.jackson.databind.JsonNode entityJson) throws IOException
OperationAwarePersistenceHandler
OperationType
of
OperationType.UPDATE
.processUpdateOperation
in class OperationAwarePersistenceHandler
entityId
- the entity ID found on the persistence message. Guaranteed non-empty.changeTimestamp
- the timestamp found on the persistence message. Guaranteed non-null.entityJson
- the persistence message json itself. Guaranteed non-null.IOException
protected void performReplacementIfEligible(@NonNull @NonNull User existing, @NonNull @NonNull AdminUser request, @NonNull @NonNull Instant changeTimestamp)
protected void attemptReplaceOrThrow(@NonNull @NonNull String id, @NonNull @NonNull User replacement, @NonNull @NonNull Instant changeTimestamp)
Thus, we invoke a special method that will directly attempt an update that the data store will reject if the entity is not found or has a lastUpdated ahead of the current change.
This means that replace can fail for two main reasons, either the user wasn't found with a
valid lastUpdated or one of the entity's references (ex: User.getRoles()
) is not
present. In either case, the exception thrown by this method should be bubbled up to trigger
a retry. If it was due to another operation concurrently modifying the record (which would
change its lastUpdated), the retry will resolve quietly after the eager fetch (in
processCreateOperation(String, Instant, JsonNode)
,
processUpdateOperation(String, Instant, JsonNode)
, or
processDeleteOperation(String, Instant, JsonNode)
) finds the updated record and
compares against its lastUpdated. If the error was due to missing references, the retry will
keep trying until the referenced record has been created.
TODO https://github.com/BroadleafCommerce/AuthenticationServices/issues/182
id
- the User.getId()
of the entity to replacereplacement
- the replacement entitychangeTimestamp
- the lastUpdated of the current changeRuntimeException
- if the replacement failed due to the entity not existing or having
an invalid lastUpdated, or if one of the entity's references was not foundprotected void processDeleteOperation(@NonNull @NonNull String entityId, @NonNull @NonNull Instant changeTimestamp, @NonNull @NonNull com.fasterxml.jackson.databind.JsonNode entityJson) throws IOException
Note that if an existing record is not found, one will be created in an archived state. The motivation for this is to protect against out-of-order messages (ex: "Delete" message on an entity arrives before the "Create"). By establishing a pre-existing record in an archived state with a timestamp, any subsequent persistence message received can be compared against this record's timestamp and appropriately discarded as outdated rather than triggering creation of a new record.
processDeleteOperation
in class OperationAwarePersistenceHandler
entityId
- the entity ID found on the persistence message. Guaranteed non-empty.changeTimestamp
- the timestamp found on the persistence message. Guaranteed non-null.entityJson
- the persistence message json itself. Guaranteed non-null.IOException
protected void performArchivalReplacementIfEligible(@NonNull @NonNull User existing, @NonNull @NonNull Instant changeTimestamp)
protected void modifyForArchival(User target)
UserService.archive(String)
would do to mark this record as archived.target
- the instance to modifyprotected boolean updatePermissions(AdminUser request, User user)
TODO https://github.com/BroadleafCommerce/AuthenticationServices/issues/182
request
- the request whose permissions should be used to replace the existing datauser
- the target on which the permissions will be replacedprotected UserPermissionRef toAuthPermissionRef(AdminPermissionRef adminPermissionRef)
protected boolean updateUserRoles(AdminUser request, User user)
TODO https://github.com/BroadleafCommerce/AuthenticationServices/issues/182
request
- the request whose roles should be used to replace the existing datauser
- the target on which the roles will be replacedprotected UserRoleRef toAuthRoleRef(AdminRoleRef adminRoleRef)
protected boolean updateUserTenantRestrictions(AdminUser request, User user)
protected Restriction toAuthRestriction(AdminRestriction adminRestriction)
protected RestrictedRole toAuthRestrictedRole(AdminRestrictedRole adminRestrictedRole)
protected boolean updateUserRestrictedPermissions(AdminUser request, User user)
protected RestrictedPermission toAuthRestrictedPermission(AdminRestrictedPermission adminRestrictedPermission)
protected Restriction toAuthRestriction(String restrictionType, Set<String> restrictionTargets)
public String[] getSupportedSimpleTypeNames()
protected UserService<User> getUserService()
protected AuthorizedClientService<AuthorizedClient> getClientService()
protected com.broadleafcommerce.common.extension.TypeFactory getTypeFactory()
protected UserLockoutService getUserLockoutService()
Copyright © 2021. All rights reserved.