add support for soft delete of users and changing users passwords.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `m_appuser`
|
||||
ADD COLUMN `is_deleted` TINYINT(1) DEFAULT 0 NOT NULL AFTER `id`;
|
||||
@@ -58,14 +58,14 @@ public class UsersApiResource {
|
||||
|
||||
@Autowired
|
||||
private PlatformSecurityContext context;
|
||||
|
||||
private static final Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "officeId", "officeName", "username", "firstname", "lastname", "email", "selectedRoles"));
|
||||
|
||||
@GET
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String retrieveUsers(@Context final UriInfo uriInfo) {
|
||||
|
||||
Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "officeId", "officeName", "username", "firstname", "lastname", "email"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
@@ -83,8 +83,6 @@ public class UsersApiResource {
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String retrieveUser(@PathParam("userId") final Long userId, @Context final UriInfo uriInfo) {
|
||||
|
||||
Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "officeId", "officeName", "username", "firstname", "lastname", "email"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
@@ -96,27 +94,29 @@ public class UsersApiResource {
|
||||
if (template) {
|
||||
List<OfficeLookup> offices = new ArrayList<OfficeLookup>(this.officeReadPlatformService.retrieveAllOfficesForLookup());
|
||||
user = new AppUserData(user, offices);
|
||||
responseParameters.add("allowedOffices");
|
||||
responseParameters.add("availableRoles");
|
||||
responseParameters.add("selectedRoles");
|
||||
addTemplateRelatedParameters(responseParameters);
|
||||
}
|
||||
|
||||
return this.apiJsonSerializerService.serializeAppUserDataToJson(prettyPrint, responseParameters, user);
|
||||
}
|
||||
|
||||
private void addTemplateRelatedParameters(final Set<String> responseParameters) {
|
||||
responseParameters.add("allowedOffices");
|
||||
responseParameters.add("availableRoles");
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String newUserDetails(@Context final UriInfo uriInfo) {
|
||||
|
||||
Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "officeId", "officeName", "username", "firstname", "lastname", "email",
|
||||
"allowedOffices", "availableRoles", "selectedRoles"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
}
|
||||
addTemplateRelatedParameters(responseParameters);
|
||||
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
AppUserData user = this.appUserReadPlatformService.retrieveNewUserDetails();
|
||||
|
||||
-15
@@ -55,19 +55,4 @@ public class BasicPasswordEncodablePlatformUser implements PlatformUser {
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFirstTimeLoginRemaining() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUsernamePasswordOnFirstTimeLogin(final String newUsername, final String newPasswordEncoded) {
|
||||
// dummy method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePasswordOnFirstTimeLogin(final String newPasswordEncoded) {
|
||||
// dummy method
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -7,9 +7,5 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
*/
|
||||
public interface PlatformUser extends UserDetails {
|
||||
|
||||
boolean isFirstTimeLoginRemaining();
|
||||
|
||||
void updateUsernamePasswordOnFirstTimeLogin(String newUsername, String newPasswordEncoded);
|
||||
|
||||
void updatePasswordOnFirstTimeLogin(String newPasswordEncoded);
|
||||
// void updatePasswordOnFirstTimeLogin(String newPasswordEncoded);
|
||||
}
|
||||
@@ -63,9 +63,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Embedded
|
||||
private final LoanProductRelatedDetail loanRepaymentScheduleDetail;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "term_frequency", nullable = false)
|
||||
private Integer termFrequency;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name = "term_period_frequency_enum", nullable = false)
|
||||
private PeriodFrequencyType termPeriodFrequencyType;
|
||||
@@ -81,6 +83,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Column(name = "rejectedon_date")
|
||||
private Date rejectedOnDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "withdrawnon_date")
|
||||
private Date withdrawnOnDate;
|
||||
@@ -520,7 +523,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoanOverpayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
private void handleLoanOverpayment(@SuppressWarnings("unused") final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
@@ -44,7 +44,7 @@ public class AppUser extends AbstractAuditableCustom<AppUser, Long> implements P
|
||||
private String password;
|
||||
|
||||
@Column(name = "nonexpired", nullable=false)
|
||||
private final boolean accountNonExpired;
|
||||
private boolean accountNonExpired;
|
||||
|
||||
@Column(name = "nonlocked", nullable=false)
|
||||
private final boolean accountNonLocked;
|
||||
@@ -53,10 +53,14 @@ public class AppUser extends AbstractAuditableCustom<AppUser, Long> implements P
|
||||
private final boolean credentialsNonExpired;
|
||||
|
||||
@Column(name = "enabled", nullable=false)
|
||||
private final boolean enabled;
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "firsttime_login_remaining", nullable=false)
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "firsttime_login_remaining", nullable=false)
|
||||
private boolean firstTimeLoginRemaining;
|
||||
|
||||
@Column(name = "is_deleted", nullable=false)
|
||||
private boolean deleted;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "office_id")
|
||||
@@ -65,7 +69,6 @@ public class AppUser extends AbstractAuditableCustom<AppUser, Long> implements P
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
|
||||
private Set<Role> roles;
|
||||
|
||||
|
||||
public static AppUser createNew(final Office office,
|
||||
final Set<Role> allRoles, final String username, final String email,
|
||||
@@ -111,33 +114,9 @@ public class AppUser extends AbstractAuditableCustom<AppUser, Long> implements P
|
||||
this.firstTimeLoginRemaining = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFirstTimeLoginRemaining() {
|
||||
return this.firstTimeLoginRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUsernamePasswordOnFirstTimeLogin(final String newUsername,
|
||||
final String newPasswordEncoded) {
|
||||
if (this.username.equals(newUsername)) {
|
||||
throw new UsernameMustBeDifferentException();
|
||||
}
|
||||
this.username = newUsername;
|
||||
updatePasswordOnFirstTimeLogin(newPasswordEncoded);
|
||||
this.firstTimeLoginRemaining = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePasswordOnFirstTimeLogin(final String newPasswordEncoded) {
|
||||
if (this.password.equals(newPasswordEncoded)) {
|
||||
throw new PasswordMustBeDifferentException();
|
||||
}
|
||||
this.password = newPasswordEncoded;
|
||||
this.firstTimeLoginRemaining = false;
|
||||
}
|
||||
|
||||
public void updatePassword(final String encodePassword) {
|
||||
this.password = encodePassword;
|
||||
this.firstTimeLoginRemaining = false;
|
||||
}
|
||||
|
||||
public void update(final Set<Role> allRoles, final Office office, final UserCommand userCommand) {
|
||||
@@ -162,6 +141,23 @@ public class AppUser extends AbstractAuditableCustom<AppUser, Long> implements P
|
||||
this.email = userCommand.getEmail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete is a <i>soft delete</i>. Updates flag so it wont appear in query/report results.
|
||||
*
|
||||
* Any fields with unique constraints and prepended with id of record.
|
||||
*/
|
||||
public void delete() {
|
||||
this.deleted = true;
|
||||
this.enabled = false;
|
||||
this.accountNonExpired = false;
|
||||
this.firstTimeLoginRemaining = true;
|
||||
this.username = this.getId() + "_DELETED_" + this.username;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GrantedAuthority> getAuthorities() {
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@ public class AppUserReadPlatformServiceImpl implements AppUserReadPlatformServic
|
||||
|
||||
AppUserMapper mapper = new AppUserMapper(offices);
|
||||
String sql = "select " + mapper.schema()
|
||||
+ " where u.office_id in (" + officeIdsList + ") order by u.id";
|
||||
+ " where u.office_id in (" + officeIdsList + ") and u.is_deleted = 0 order by u.id";
|
||||
|
||||
return this.jdbcTemplate.query(sql, mapper, new Object[] {});
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class AppUserReadPlatformServiceImpl implements AppUserReadPlatformServic
|
||||
context.authenticatedUser();
|
||||
|
||||
AppUser user = this.appUserRepository.findOne(userId);
|
||||
if (user == null) {
|
||||
if (user == null || user.isDeleted()) {
|
||||
throw new UserNotFoundException(userId);
|
||||
}
|
||||
|
||||
|
||||
+10
-11
@@ -29,7 +29,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -88,7 +87,7 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
|
||||
|
||||
return appUser.getId();
|
||||
} catch (DataIntegrityViolationException dve) {
|
||||
handleAppUserDataIntegrityIssues(command, dve);
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return Long.valueOf(-1);
|
||||
} catch (PlatformEmailSendException e) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
@@ -134,13 +133,13 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
|
||||
|
||||
String newPasswordEncoded = this.platformPasswordEncoder.encode(dummyPlatformUser);
|
||||
|
||||
userToUpdate.updatePasswordOnFirstTimeLogin(newPasswordEncoded);
|
||||
userToUpdate.updatePassword(newPasswordEncoded);
|
||||
this.appUserRepository.saveAndFlush(userToUpdate);
|
||||
}
|
||||
|
||||
return userToUpdate.getId();
|
||||
} catch (DataIntegrityViolationException dve) {
|
||||
handleAppUserDataIntegrityIssues(command, dve);
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return Long.valueOf(-1);
|
||||
}
|
||||
}
|
||||
@@ -170,23 +169,23 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
|
||||
return allRoles;
|
||||
}
|
||||
|
||||
// FIXME - KW - move this to a soft delete of user marking a is_deleted field as true so doesnt appear in results
|
||||
// more than making a user disabled, locked?
|
||||
@Transactional
|
||||
@Override
|
||||
public void deleteUser(final Long userId) {
|
||||
try {
|
||||
this.appUserRepository.delete(userId);
|
||||
} catch (InvalidDataAccessApiUsageException exception) {
|
||||
// spring data jpa throws this when deleting user that doesnt exist
|
||||
|
||||
AppUser user = this.appUserRepository.findOne(userId);
|
||||
if (user == null || user.isDeleted()) {
|
||||
throw new UserNotFoundException(userId);
|
||||
}
|
||||
|
||||
user.delete();
|
||||
this.appUserRepository.save(user);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||
*/
|
||||
private void handleAppUserDataIntegrityIssues(final UserCommand command, final DataIntegrityViolationException dve) {
|
||||
private void handleDataIntegrityIssues(final UserCommand command, final DataIntegrityViolationException dve) {
|
||||
|
||||
Throwable realCause = dve.getMostSpecificCause();
|
||||
if (realCause.getMessage().contains("username_org")) {
|
||||
|
||||
Reference in New Issue
Block a user