Package com.xebialabs.xlrelease.service
Class UserProfileService
java.lang.Object
com.xebialabs.xlrelease.service.UserProfileService
-
Field Summary
Fields -
Constructor Summary
ConstructorsConstructorDescriptionUserProfileService(com.xebialabs.xlrelease.repository.UserProfileRepository userProfileRepository, PrincipalDataProvider principalDataProvider, com.xebialabs.license.service.LicenseService licenseService, SessionService sessionService, com.xebialabs.xlrelease.events.EventBus eventBus, WelcomeTemplateHandler welcomeTemplateHandler, com.xebialabs.xlrelease.config.XlrConfig xlrConfig) -
Method Summary
Modifier and TypeMethodDescriptionbooleanintcom.xebialabs.xlrelease.domain.UserProfilecreateOrUpdate(String username) voiddeleteByUsername(String username) com.xebialabs.xlrelease.domain.UserProfilevoidensureCreated(String username) List<com.xebialabs.xlrelease.domain.UserProfile> com.xebialabs.xlrelease.domain.UserProfilefindByUsername(String username) com.xebialabs.xlrelease.domain.UserProfilefindByUsernameForUpdate(String canonicalId) Retrieves user profile with pessimistic row-level lock (FOR UPDATE).static booleanhasExternalPropertiesChanged(com.xebialabs.xlrelease.domain.UserProfile original, com.xebialabs.xlrelease.domain.UserProfile updated) com.xebialabs.xlrelease.domain.UserProfileresolveUserProfile(String username) com.xebialabs.xlrelease.domain.UserProfileresolveUserProfile(String username, boolean resolveWithDataProvider) voidsave(com.xebialabs.xlrelease.domain.UserProfile profile) List<com.xebialabs.xlrelease.domain.UserProfile> search(String email, String fullName, Boolean loginAllowed, Date lastActiveAfter, Date lastActiveBefore, Long page, Long resultsPerPage) org.springframework.data.domain.Page<com.xebialabs.xlrelease.domain.UserProfile> searchUserAccounts(com.xebialabs.xlrelease.views.users.UserFilters userFilters, org.springframework.data.domain.Pageable pageable) intupdateFailedLoginAttempt(String canonicalId, int newCount, Boolean newLocked, Date lastFailedAttemptAt, Boolean cacheEvict) booleanupdateLastActive(String canonicalId, Date lastActive, Boolean cacheEvict) intupdateLastActiveBatch(Map<String, Date> entries) voidupdateProfile(com.xebialabs.xlrelease.domain.UserProfile... profiles) voidvalidate(com.xebialabs.xlrelease.domain.UserProfile profile)
-
Field Details
-
ROOT
-
-
Constructor Details
-
UserProfileService
@Autowired public UserProfileService(com.xebialabs.xlrelease.repository.UserProfileRepository userProfileRepository, PrincipalDataProvider principalDataProvider, com.xebialabs.license.service.LicenseService licenseService, SessionService sessionService, com.xebialabs.xlrelease.events.EventBus eventBus, WelcomeTemplateHandler welcomeTemplateHandler, com.xebialabs.xlrelease.config.XlrConfig xlrConfig)
-
-
Method Details
-
hasExternalPropertiesChanged
public static boolean hasExternalPropertiesChanged(com.xebialabs.xlrelease.domain.UserProfile original, com.xebialabs.xlrelease.domain.UserProfile updated) -
findAll
-
deleteByUsername
-
countUserWithLoginAllowed
public int countUserWithLoginAllowed() -
areLicensesAvailable
public boolean areLicensesAvailable() -
search
-
searchUserAccounts
public org.springframework.data.domain.Page<com.xebialabs.xlrelease.domain.UserProfile> searchUserAccounts(com.xebialabs.xlrelease.views.users.UserFilters userFilters, org.springframework.data.domain.Pageable pageable) -
findByUsername
-
ensureCreated
-
discover
-
resolveUserProfile
-
resolveUserProfile
public com.xebialabs.xlrelease.domain.UserProfile resolveUserProfile(String username, boolean resolveWithDataProvider) -
createOrUpdate
-
save
public void save(com.xebialabs.xlrelease.domain.UserProfile profile) -
updateProfile
public void updateProfile(com.xebialabs.xlrelease.domain.UserProfile... profiles) -
updateLastActive
-
findByUsernameForUpdate
Retrieves user profile with pessimistic row-level lock (FOR UPDATE).⚠️ MUST be called within an active transaction. The UPDATE must occur in the SAME transaction:
Purpose:
This method prevents race conditions during concurrent failed login attempts by:- Acquiring an exclusive row lock (FOR UPDATE / WITH (UPDLOCK, ROWLOCK))
- Blocking other transactions from reading/modifying the same row
- Ensuring atomic read-modify-write operations
Correct Usage Example:
@Transactional(propagation = REQUIRED, isolation = READ_COMMITTED) public void recordFailedAttempt(String username) { UserProfile profile = userProfileService.findByUsernameForUpdate(username); // Acquires lock int newCount = profile.getLastFailedLoginAttemptCount() + 1; userProfileService.updateFailedLoginAttempt(username, newCount, ...); // Same transaction // Lock released on commit }❌ INCORRECT Usage (Will cause data inconsistency):
// DON'T DO THIS - No transaction! val profile = userProfilePersistence.findByUsernameForUpdate(username) // Lock released here, race condition possible Thread.sleep(1000) // Another thread could modify data userProfilePersistence.updateFailedLoginAttempt(...) // Too late! // DON'T DO THIS - Separate transactions! @Transactional def read() = userProfilePersistence.findByUsernameForUpdate(username) @Transactional // New transaction = new lock = race condition def update() = userProfilePersistence.updateFailedLoginAttempt(...)Database-Specific Locking:
- PostgreSQL/MySQL/Oracle: Uses standard
FOR UPDATEclause - SQL Server: Uses
WITH (UPDLOCK, ROWLOCK)hint
- Parameters:
canonicalId- The username to lock and retrieve- Returns:
- UserProfile with account lock data, or null if not found
-
updateFailedLoginAttempt
-
updateLastActiveBatch
-
validate
public void validate(com.xebialabs.xlrelease.domain.UserProfile profile)
-