MIFOSX-648: allow user to fetch their own account data no matter what permissions they hold.

This commit is contained in:
Keith Woodlock
2013-09-09 09:54:42 +01:00
parent bdb2162ab7
commit 0de4adf6e5
3 changed files with 472 additions and 512 deletions
@@ -73,7 +73,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
/**
* The interest period is the span of time at the end of which savings in a
* client's account earn interest.
*
*
* A value from the {@link SavingsCompoundingInterestPeriodType}
* enumeration.
*/
@@ -119,9 +119,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
@Column(name = "withdrawal_fee_type_enum", nullable = true)
private Integer withdrawalFeeType;
@Column(name = "withdrawal_fee_for_transfer")
@SuppressWarnings("unused")
private boolean withdrawalFeeApplicableForTransfer;
@Column(name = "annual_fee_amount", scale = 6, precision = 19, nullable = true)
@@ -138,12 +137,12 @@ public class SavingsProduct extends AbstractPersistable<Long> {
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, boolean withdrawalFeeApplicableForTransfer,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final boolean withdrawalFeeApplicableForTransfer,
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final AccountingRuleType accountingRuleType) {
return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeApplicableForTransfer, annualFeeAmount,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,withdrawalFeeApplicableForTransfer, annualFeeAmount,
annualFeeOnMonthDay, accountingRuleType);
}
@@ -376,7 +375,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
actualChanges.put(withdrawalFeeTypeParamName, newValue);
this.withdrawalFeeType = newValue != null ? SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue;
}
if(command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)){
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName);
actualChanges.put(withdrawalFeeForTransfersParamName, newValue);
@@ -484,16 +483,16 @@ public class SavingsProduct extends AbstractPersistable<Long> {
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
if (this.lockinPeriodFrequency == null) {
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).ignoreIfNull()
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).ignoreIfNull()
.inMinMaxRange(0, 3);
if (this.lockinPeriodFrequencyType != null) {
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
} else {
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequencyType).integerZeroOrGreater();
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequencyType).integerZeroOrGreater();
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).notNull()
.inMinMaxRange(0, 3);
}
@@ -75,25 +75,25 @@ public class UsersApiResource {
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveUsers(@Context final UriInfo uriInfo) {
public String retrieveAll(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final Collection<AppUserData> users = this.readPlatformService.retrieveAllUsers();
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, users, RESPONSE_DATA_PARAMETERS);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, users, this.RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{userId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveUser(@PathParam("userId") final Long userId, @Context final UriInfo uriInfo) {
public String retrieveOne(@PathParam("userId") final Long userId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions, userId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
AppUserData user = this.readPlatformService.retrieveUser(userId);
if (settings.isTemplate()) {
@@ -101,27 +101,27 @@ public class UsersApiResource {
user = AppUserData.template(user, offices);
}
return this.toApiJsonSerializer.serialize(settings, user, RESPONSE_DATA_PARAMETERS);
return this.toApiJsonSerializer.serialize(settings, user, this.RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String newUserDetails(@Context final UriInfo uriInfo) {
public String template(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final AppUserData user = this.readPlatformService.retrieveNewUserDetails();
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, user, RESPONSE_DATA_PARAMETERS);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, user, this.RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createUser(final String apiRequestBodyAsJson) {
public String create(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.createUser() //
@@ -137,7 +137,7 @@ public class UsersApiResource {
@Path("{userId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateUser(@PathParam("userId") final Long userId, final String apiRequestBodyAsJson) {
public String update(@PathParam("userId") final Long userId, final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.updateUser(userId) //
@@ -153,7 +153,7 @@ public class UsersApiResource {
@Path("{userId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteUser(@PathParam("userId") final Long userId) {
public String delete(@PathParam("userId") final Long userId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.deleteUser(userId) //
@@ -42,491 +42,452 @@ import org.springframework.security.core.userdetails.User;
@Table(name = "m_appuser", uniqueConstraints = @UniqueConstraint(columnNames = { "username" }, name = "username_org"))
public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
private final static Logger logger = LoggerFactory.getLogger(AppUser.class);
@Column(name = "email", nullable = false, length = 100)
private String email;
@Column(name = "username", nullable = false, length = 100)
private String username;
@Column(name = "firstname", nullable = false, length = 100)
private String firstname;
@Column(name = "lastname", nullable = false, length = 100)
private String lastname;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "nonexpired", nullable = false)
private boolean accountNonExpired;
@Column(name = "nonlocked", nullable = false)
private final boolean accountNonLocked;
@Column(name = "nonexpired_credentials", nullable = false)
private final boolean credentialsNonExpired;
@Column(name = "enabled", nullable = false)
private boolean enabled;
@Column(name = "firsttime_login_remaining", nullable = false)
private boolean firstTimeLoginRemaining;
@Column(name = "is_deleted", nullable = false)
private boolean deleted;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne
@JoinColumn(name = "staff_id", nullable = true)
private Staff staff;
@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 fromJson(final Office userOffice,
final Set<Role> allRoles, final JsonCommand command) {
final String username = command.stringValueOfParameterNamed("username");
String password = command.stringValueOfParameterNamed("password");
final Boolean sendPasswordToEmail = command
.booleanObjectValueOfParameterNamed("sendPasswordToEmail");
if (sendPasswordToEmail.booleanValue()) {
password = new RandomPasswordGenerator(13).generate();
}
final boolean userEnabled = true;
final boolean userAccountNonExpired = true;
final boolean userCredentialsNonExpired = true;
final boolean userAccountNonLocked = true;
final Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(
"DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION"));
final User user = new User(username, password, userEnabled,
userAccountNonExpired, userCredentialsNonExpired,
userAccountNonLocked, authorities);
final String email = command.stringValueOfParameterNamed("email");
final String firstname = command
.stringValueOfParameterNamed("firstname");
final String lastname = command.stringValueOfParameterNamed("lastname");
return new AppUser(userOffice, user, allRoles, email, firstname,
lastname);
}
protected AppUser() {
this.accountNonLocked = false;
this.credentialsNonExpired = false;
this.roles = new HashSet<Role>();
}
public AppUser(final Office office, final User user, final Set<Role> roles,
final String email, final String firstname, final String lastname) {
this.office = office;
this.email = email.trim();
this.username = user.getUsername().trim();
this.firstname = firstname.trim();
this.lastname = lastname.trim();
this.password = user.getPassword().trim();
this.accountNonExpired = user.isAccountNonExpired();
this.accountNonLocked = user.isAccountNonLocked();
this.credentialsNonExpired = user.isCredentialsNonExpired();
this.enabled = user.isEnabled();
this.roles = roles;
this.firstTimeLoginRemaining = true;
}
public EnumOptionData organisationalRoleData() {
EnumOptionData organisationalRole = null;
if (this.staff != null) {
organisationalRole = this.staff.organisationalRoleData();
}
return organisationalRole;
}
public void updatePassword(final String encodePassword) {
this.password = encodePassword;
this.firstTimeLoginRemaining = false;
}
public void changeOffice(final Office differentOffice) {
this.office = differentOffice;
}
public void updateRoles(final Set<Role> allRoles) {
if (!allRoles.isEmpty()) {
this.roles.clear();
this.roles = allRoles;
}
}
public Map<String, Object> update(final JsonCommand command,
final PlatformPasswordEncoder platformPasswordEncoder) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(
7);
// unencoded password provided
final String passwordParamName = "password";
final String passwordEncodedParamName = "passwordEncoded";
if (command.hasParameter(passwordParamName)) {
if (command.isChangeInPasswordParameterNamed(passwordParamName,
this.password, platformPasswordEncoder, getId())) {
final String passwordEncodedValue = command
.passwordValueOfParameterNamed(passwordParamName,
platformPasswordEncoder, getId());
actualChanges.put(passwordEncodedParamName,
passwordEncodedValue);
updatePassword(passwordEncodedValue);
}
}
if (command.hasParameter(passwordEncodedParamName)) {
if (command.isChangeInStringParameterNamed(
passwordEncodedParamName, this.password)) {
final String newValue = command
.stringValueOfParameterNamed(passwordEncodedParamName);
actualChanges.put(passwordEncodedParamName, newValue);
updatePassword(newValue);
}
}
final String officeIdParamName = "officeId";
if (command.isChangeInLongParameterNamed(officeIdParamName,
this.office.getId())) {
final Long newValue = command
.longValueOfParameterNamed(officeIdParamName);
actualChanges.put(officeIdParamName, newValue);
}
final String rolesParamName = "roles";
if (command.isChangeInArrayParameterNamed(rolesParamName,
getRolesAsIdStringArray())) {
final String[] newValue = command
.arrayValueOfParameterNamed(rolesParamName);
actualChanges.put(rolesParamName, newValue);
}
final String usernameParamName = "username";
if (command.isChangeInStringParameterNamed(usernameParamName,
this.username)) {
final String newValue = command
.stringValueOfParameterNamed(usernameParamName);
actualChanges.put(usernameParamName, newValue);
this.username = newValue;
}
final String firstnameParamName = "firstname";
if (command.isChangeInStringParameterNamed(firstnameParamName,
this.firstname)) {
final String newValue = command
.stringValueOfParameterNamed(firstnameParamName);
actualChanges.put(firstnameParamName, newValue);
this.firstname = newValue;
}
final String lastnameParamName = "lastname";
if (command.isChangeInStringParameterNamed(lastnameParamName,
this.lastname)) {
final String newValue = command
.stringValueOfParameterNamed(lastnameParamName);
actualChanges.put(lastnameParamName, newValue);
this.lastname = newValue;
}
final String emailParamName = "email";
if (command.isChangeInStringParameterNamed(emailParamName, this.email)) {
final String newValue = command
.stringValueOfParameterNamed(emailParamName);
actualChanges.put(emailParamName, newValue);
this.email = newValue;
}
return actualChanges;
}
private String[] getRolesAsIdStringArray() {
final List<String> roleIds = new ArrayList<String>();
for (final Role role : this.roles) {
roleIds.add(role.getId().toString());
}
return roleIds.toArray(new String[roleIds.size()]);
}
/**
* 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 = getId() + "_DELETED_" + this.username;
}
public boolean isDeleted() {
return this.deleted;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return populateGrantedAuthorities();
}
private List<GrantedAuthority> populateGrantedAuthorities() {
final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (final Role role : this.roles) {
final Collection<Permission> permissions = role.getPermissions();
for (final Permission permission : permissions) {
grantedAuthorities.add(new SimpleGrantedAuthority(permission
.getCode()));
}
}
return grantedAuthorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
public String getFirstname() {
return this.firstname;
}
public String getLastname() {
return this.lastname;
}
public String getEmail() {
return this.email;
}
public Set<Role> getRoles() {
return this.roles;
}
public Office getOffice() {
return this.office;
}
public boolean canNotApproveLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN");
}
public boolean canNotRejectLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN");
}
public boolean canNotWithdrawByClientLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN");
}
public boolean canNotDisburseLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN");
}
public boolean canNotMakeRepaymentOnLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN");
}
public boolean hasNotPermissionForReport(final String reportName) {
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ",
"REPORTING_SUPER_USER", "READ_" + reportName)) {
return true;
}
return false;
}
public boolean hasNotPermissionForDatatable(final String datatable,
final String accessType) {
final String matchPermission = accessType + "_" + datatable;
if (accessType.equalsIgnoreCase("READ")) {
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ",
matchPermission)) {
return true;
}
return false;
}
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) {
return true;
}
return false;
}
public boolean hasNotPermissionForAnyOf(final String... permissionCodes) {
boolean hasNotPermission = true;
for (final String permissionCode : permissionCodes) {
final boolean checkPermission = hasPermissionTo(permissionCode);
if (checkPermission) {
hasNotPermission = false;
break;
}
}
return hasNotPermission;
}
public void validateHasReadPermission(final String resourceType) {
final String authorizationMessage = "User has no authority to view "
+ resourceType.toLowerCase() + "s";
final String matchPermission = "READ_" + resourceType.toUpperCase();
if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ",
matchPermission)) {
return;
}
throw new NoAuthorizationException(authorizationMessage);
}
private boolean hasNotPermissionTo(final String permissionCode) {
return !hasPermissionTo(permissionCode);
}
private boolean hasPermissionTo(final String permissionCode) {
boolean hasPermission = hasAllFunctionsPermission();
if (!hasPermission) {
for (final Role role : this.roles) {
if (role.hasPermissionTo(permissionCode)) {
hasPermission = true;
break;
}
}
}
return hasPermission;
}
private boolean hasAllFunctionsPermission() {
boolean match = false;
for (final Role role : this.roles) {
if (role.hasPermissionTo("ALL_FUNCTIONS")) {
match = true;
break;
}
}
return match;
}
public boolean hasIdOf(final Long userId) {
return getId().equals(userId);
}
private boolean hasNotAnyPermission(final List<String> permissions) {
return !hasAnyPermission(permissions);
}
private boolean hasAnyPermission(final List<String> permissions) {
boolean hasAtLeastOneOf = false;
for (final String permissionCode : permissions) {
if (hasPermissionTo(permissionCode)) {
hasAtLeastOneOf = true;
break;
}
}
return hasAtLeastOneOf;
}
public void validateHasPermissionTo(final String function,
final List<String> allowedPermissions) {
if (hasNotAnyPermission(allowedPermissions)) {
final String authorizationMessage = "User has no authority to: "
+ function;
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasPermissionTo(final String function) {
if (hasNotPermissionTo(function)) {
final String authorizationMessage = "User has no authority to: "
+ function;
logger.info("Unauthorized access: userId: " + getId() + " action: "
+ function + " allowed: " + getAuthorities());
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasCheckerPermissionTo(final String function) {
final String checkerPermissionName = function.toUpperCase()
+ "_CHECKER";
if (hasNotPermissionTo("CHECKER_SUPER_USER")
&& hasNotPermissionTo(checkerPermissionName)) {
final String authorizationMessage = "User has no authority to be a checker for: "
+ function;
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasDatatableReadPermission(final String datatable) {
if (hasNotPermissionForDatatable(datatable, "READ")) {
throw new NoAuthorizationException(
"Not authorised to read datatable: " + datatable);
}
}
public Long getStaffId() {
Long staffId = null;
if (this.staff != null) {
staffId = this.staff.getId();
}
return staffId;
}
public String getStaffDisplayName() {
String staffDisplayName = null;
if (this.staff != null) {
staffDisplayName = this.staff.displayName();
}
return staffDisplayName;
}
private final static Logger logger = LoggerFactory.getLogger(AppUser.class);
@Column(name = "email", nullable = false, length = 100)
private String email;
@Column(name = "username", nullable = false, length = 100)
private String username;
@Column(name = "firstname", nullable = false, length = 100)
private String firstname;
@Column(name = "lastname", nullable = false, length = 100)
private String lastname;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "nonexpired", nullable = false)
private boolean accountNonExpired;
@Column(name = "nonlocked", nullable = false)
private final boolean accountNonLocked;
@Column(name = "nonexpired_credentials", nullable = false)
private final boolean credentialsNonExpired;
@Column(name = "enabled", nullable = false)
private boolean enabled;
@Column(name = "firsttime_login_remaining", nullable = false)
private boolean firstTimeLoginRemaining;
@Column(name = "is_deleted", nullable = false)
private boolean deleted;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne
@JoinColumn(name = "staff_id", nullable = true)
private Staff staff;
@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 fromJson(final Office userOffice, final Set<Role> allRoles, final JsonCommand command) {
final String username = command.stringValueOfParameterNamed("username");
String password = command.stringValueOfParameterNamed("password");
final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail");
if (sendPasswordToEmail.booleanValue()) {
password = new RandomPasswordGenerator(13).generate();
}
final boolean userEnabled = true;
final boolean userAccountNonExpired = true;
final boolean userCredentialsNonExpired = true;
final boolean userAccountNonLocked = true;
final Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION"));
final User user = new User(username, password, userEnabled, userAccountNonExpired, userCredentialsNonExpired, userAccountNonLocked,
authorities);
final String email = command.stringValueOfParameterNamed("email");
final String firstname = command.stringValueOfParameterNamed("firstname");
final String lastname = command.stringValueOfParameterNamed("lastname");
return new AppUser(userOffice, user, allRoles, email, firstname, lastname);
}
protected AppUser() {
this.accountNonLocked = false;
this.credentialsNonExpired = false;
this.roles = new HashSet<Role>();
}
public AppUser(final Office office, final User user, final Set<Role> roles, final String email, final String firstname,
final String lastname) {
this.office = office;
this.email = email.trim();
this.username = user.getUsername().trim();
this.firstname = firstname.trim();
this.lastname = lastname.trim();
this.password = user.getPassword().trim();
this.accountNonExpired = user.isAccountNonExpired();
this.accountNonLocked = user.isAccountNonLocked();
this.credentialsNonExpired = user.isCredentialsNonExpired();
this.enabled = user.isEnabled();
this.roles = roles;
this.firstTimeLoginRemaining = true;
}
public EnumOptionData organisationalRoleData() {
EnumOptionData organisationalRole = null;
if (this.staff != null) {
organisationalRole = this.staff.organisationalRoleData();
}
return organisationalRole;
}
public void updatePassword(final String encodePassword) {
this.password = encodePassword;
this.firstTimeLoginRemaining = false;
}
public void changeOffice(final Office differentOffice) {
this.office = differentOffice;
}
public void updateRoles(final Set<Role> allRoles) {
if (!allRoles.isEmpty()) {
this.roles.clear();
this.roles = allRoles;
}
}
public Map<String, Object> update(final JsonCommand command, final PlatformPasswordEncoder platformPasswordEncoder) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
// unencoded password provided
final String passwordParamName = "password";
final String passwordEncodedParamName = "passwordEncoded";
if (command.hasParameter(passwordParamName)) {
if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, getId())) {
final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder,
getId());
actualChanges.put(passwordEncodedParamName, passwordEncodedValue);
updatePassword(passwordEncodedValue);
}
}
if (command.hasParameter(passwordEncodedParamName)) {
if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) {
final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName);
actualChanges.put(passwordEncodedParamName, newValue);
updatePassword(newValue);
}
}
final String officeIdParamName = "officeId";
if (command.isChangeInLongParameterNamed(officeIdParamName, this.office.getId())) {
final Long newValue = command.longValueOfParameterNamed(officeIdParamName);
actualChanges.put(officeIdParamName, newValue);
}
final String rolesParamName = "roles";
if (command.isChangeInArrayParameterNamed(rolesParamName, getRolesAsIdStringArray())) {
final String[] newValue = command.arrayValueOfParameterNamed(rolesParamName);
actualChanges.put(rolesParamName, newValue);
}
final String usernameParamName = "username";
if (command.isChangeInStringParameterNamed(usernameParamName, this.username)) {
final String newValue = command.stringValueOfParameterNamed(usernameParamName);
actualChanges.put(usernameParamName, newValue);
this.username = newValue;
}
final String firstnameParamName = "firstname";
if (command.isChangeInStringParameterNamed(firstnameParamName, this.firstname)) {
final String newValue = command.stringValueOfParameterNamed(firstnameParamName);
actualChanges.put(firstnameParamName, newValue);
this.firstname = newValue;
}
final String lastnameParamName = "lastname";
if (command.isChangeInStringParameterNamed(lastnameParamName, this.lastname)) {
final String newValue = command.stringValueOfParameterNamed(lastnameParamName);
actualChanges.put(lastnameParamName, newValue);
this.lastname = newValue;
}
final String emailParamName = "email";
if (command.isChangeInStringParameterNamed(emailParamName, this.email)) {
final String newValue = command.stringValueOfParameterNamed(emailParamName);
actualChanges.put(emailParamName, newValue);
this.email = newValue;
}
return actualChanges;
}
private String[] getRolesAsIdStringArray() {
final List<String> roleIds = new ArrayList<String>();
for (final Role role : this.roles) {
roleIds.add(role.getId().toString());
}
return roleIds.toArray(new String[roleIds.size()]);
}
/**
* 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 = getId() + "_DELETED_" + this.username;
}
public boolean isDeleted() {
return this.deleted;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return populateGrantedAuthorities();
}
private List<GrantedAuthority> populateGrantedAuthorities() {
final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (final Role role : this.roles) {
final Collection<Permission> permissions = role.getPermissions();
for (final Permission permission : permissions) {
grantedAuthorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
}
return grantedAuthorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
public String getFirstname() {
return this.firstname;
}
public String getLastname() {
return this.lastname;
}
public String getEmail() {
return this.email;
}
public Set<Role> getRoles() {
return this.roles;
}
public Office getOffice() {
return this.office;
}
public boolean canNotApproveLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN");
}
public boolean canNotRejectLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN");
}
public boolean canNotWithdrawByClientLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN");
}
public boolean canNotDisburseLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN");
}
public boolean canNotMakeRepaymentOnLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN");
}
public boolean hasNotPermissionForReport(final String reportName) {
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "REPORTING_SUPER_USER", "READ_" + reportName)) { return true; }
return false;
}
public boolean hasNotPermissionForDatatable(final String datatable, final String accessType) {
final String matchPermission = accessType + "_" + datatable;
if (accessType.equalsIgnoreCase("READ")) {
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) { return true; }
return false;
}
if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) { return true; }
return false;
}
public boolean hasNotPermissionForAnyOf(final String... permissionCodes) {
boolean hasNotPermission = true;
for (final String permissionCode : permissionCodes) {
final boolean checkPermission = hasPermissionTo(permissionCode);
if (checkPermission) {
hasNotPermission = false;
break;
}
}
return hasNotPermission;
}
public void validateHasReadPermission(final String resourceType) {
final String authorizationMessage = "User has no authority to view " + resourceType.toLowerCase() + "s";
final String matchPermission = "READ_" + resourceType.toUpperCase();
if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) { return; }
throw new NoAuthorizationException(authorizationMessage);
}
private boolean hasNotPermissionTo(final String permissionCode) {
return !hasPermissionTo(permissionCode);
}
private boolean hasPermissionTo(final String permissionCode) {
boolean hasPermission = hasAllFunctionsPermission();
if (!hasPermission) {
for (final Role role : this.roles) {
if (role.hasPermissionTo(permissionCode)) {
hasPermission = true;
break;
}
}
}
return hasPermission;
}
private boolean hasAllFunctionsPermission() {
boolean match = false;
for (final Role role : this.roles) {
if (role.hasPermissionTo("ALL_FUNCTIONS")) {
match = true;
break;
}
}
return match;
}
public boolean hasIdOf(final Long userId) {
return getId().equals(userId);
}
private boolean hasNotAnyPermission(final List<String> permissions) {
return !hasAnyPermission(permissions);
}
private boolean hasAnyPermission(final List<String> permissions) {
boolean hasAtLeastOneOf = false;
for (final String permissionCode : permissions) {
if (hasPermissionTo(permissionCode)) {
hasAtLeastOneOf = true;
break;
}
}
return hasAtLeastOneOf;
}
public void validateHasPermissionTo(final String function, final List<String> allowedPermissions) {
if (hasNotAnyPermission(allowedPermissions)) {
final String authorizationMessage = "User has no authority to: " + function;
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasPermissionTo(final String function) {
if (hasNotPermissionTo(function)) {
final String authorizationMessage = "User has no authority to: " + function;
logger.info("Unauthorized access: userId: " + getId() + " action: " + function + " allowed: " + getAuthorities());
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasReadPermission(final String function, final Long userId) {
if ("USER".equalsIgnoreCase(function) && userId.equals(getId())) {
// abstain from validation as user allowed fetch their own data no matter what permissions they have.
} else {
validateHasPermissionTo(function);
}
}
public void validateHasCheckerPermissionTo(final String function) {
final String checkerPermissionName = function.toUpperCase() + "_CHECKER";
if (hasNotPermissionTo("CHECKER_SUPER_USER") && hasNotPermissionTo(checkerPermissionName)) {
final String authorizationMessage = "User has no authority to be a checker for: " + function;
throw new NoAuthorizationException(authorizationMessage);
}
}
public void validateHasDatatableReadPermission(final String datatable) {
if (hasNotPermissionForDatatable(datatable, "READ")) { throw new NoAuthorizationException("Not authorised to read datatable: "
+ datatable); }
}
public Long getStaffId() {
Long staffId = null;
if (this.staff != null) {
staffId = this.staff.getId();
}
return staffId;
}
public String getStaffDisplayName() {
String staffDisplayName = null;
if (this.staff != null) {
staffDisplayName = this.staff.displayName();
}
return staffDisplayName;
}
}