MIFOSX-97: initial integration of maker-checker capability on permissions with actual api services.

This commit is contained in:
Keith Woodlock
2012-11-26 18:17:53 +00:00
parent 092eabc0a0
commit 19fea3ed00
29 changed files with 636 additions and 417 deletions
@@ -33,66 +33,63 @@ import org.springframework.stereotype.Component;
@Scope("singleton")
public class CommandsApiResource {
@Autowired
private PortfolioCommandsReadPlatformService readPlatformService;
@Autowired
private PortfolioCommandSourceWritePlatformService writePlatformService;
@Autowired
private PortfolioCommandsReadPlatformService readPlatformService;
@Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService;
@Autowired
private PortfolioCommandSourceWritePlatformService writePlatformService;
private final String entityType = "MAKERCHECKER";
@Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService;
private final String entityType = "MAKERCHECKER";
@Autowired
private PlatformSecurityContext context;
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String retrieveCodes(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final Collection<CommandSourceData> entries = this.readPlatformService.retrieveAllEntriesToBeChecked();
return this.apiJsonSerializerService.serializeMakerCheckerDataToJson(prettyPrint, responseParameters, entries);
}
@POST
@Path("{makerCheckerId}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String approveMakerCheckerEntry(
@PathParam("makerCheckerId") final Long makerCheckerId,
@QueryParam("command") final String commandParam) {
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveCodes(@Context final UriInfo uriInfo) {
EntityIdentifier result = null;
if (is(commandParam, "approve")) {
result = this.writePlatformService.approveEntry(makerCheckerId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
context.authenticatedUser().validateHasReadPermission(entityType);
return this.apiJsonSerializerService.serializeEntityIdentifier(result);
}
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam)
&& commandParam.trim().equalsIgnoreCase(commandValue);
}
@DELETE
@Path("{makerCheckerId}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String deleteMakerCheckerEntry(
@PathParam("makerCheckerId") final Long makerCheckerId) {
final Collection<CommandSourceData> entries = this.readPlatformService.retrieveAllEntriesToBeChecked();
final Long id = this.writePlatformService.deleteEntry(makerCheckerId);
return this.apiJsonSerializerService.serializeMakerCheckerDataToJson(prettyPrint, responseParameters, entries);
}
return this.apiJsonSerializerService.serializeEntityIdentifier(EntityIdentifier.makerChecker(id));
}
@POST
@Path("{makerCheckerId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String approveMakerCheckerEntry(@PathParam("makerCheckerId") final Long makerCheckerId,
@QueryParam("command") final String commandParam) {
EntityIdentifier result = null;
if (is(commandParam, "approve")) {
result = this.writePlatformService.approveEntry(makerCheckerId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.apiJsonSerializerService.serializeEntityIdentifier(result);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
@DELETE
@Path("{makerCheckerId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteMakerCheckerEntry(@PathParam("makerCheckerId") final Long makerCheckerId) {
final Long id = this.writePlatformService.deleteEntry(makerCheckerId);
return this.apiJsonSerializerService.serializeEntityIdentifier(EntityIdentifier.makerChecker(id));
}
}
@@ -31,53 +31,52 @@ import org.springframework.stereotype.Component;
@Scope("singleton")
public class ConfigurationApiResource {
@Autowired
private ConfigurationReadPlatformService configurationReadPlatformService;
@Autowired
private ConfigurationReadPlatformService configurationReadPlatformService;
@Autowired
private ConfigurationWritePlatformService configurationWritePlatformService;
@Autowired
private ConfigurationWritePlatformService configurationWritePlatformService;
@Autowired
private PortfolioApiDataConversionService apiDataConversionService;
@Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService;
@Autowired
private PortfolioApiDataConversionService apiDataConversionService;
@Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService;
@Autowired
private PlatformSecurityContext context;
@GET
@Path("currency")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String retrieveCurrencyDataForConfiguration(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CURRENCY");
Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("selectedCurrencyOptions", "currencyOptions"));
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
@GET
@Path("currency")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveCurrencyDataForConfiguration(@Context final UriInfo uriInfo) {
ConfigurationData configurationData = this.configurationReadPlatformService.retrieveCurrencyConfiguration();
return this.apiJsonSerializerService.serializeConfigurationDataToJson(prettyPrint, responseParameters, configurationData);
}
context.authenticatedUser().validateHasReadPermission("CURRENCY");
@PUT
@Path("currency")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response updateAllowedCurrenciesForOrganisation(final String jsonRequestBody) {
Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("selectedCurrencyOptions", "currencyOptions"));
OrganisationCurrencyCommand command = this.apiDataConversionService.convertJsonToOrganisationCurrencyCommand(jsonRequestBody);
this.configurationWritePlatformService.updateOrganisationCurrencies(command);
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
return Response.ok().entity(command).build();
}
ConfigurationData configurationData = this.configurationReadPlatformService.retrieveCurrencyConfiguration();
return this.apiJsonSerializerService.serializeConfigurationDataToJson(prettyPrint, responseParameters, configurationData);
}
@PUT
@Path("currency")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response updateAllowedCurrenciesForOrganisation(final String jsonRequestBody) {
OrganisationCurrencyCommand command = this.apiDataConversionService.convertJsonToOrganisationCurrencyCommand(jsonRequestBody);
this.configurationWritePlatformService.updateOrganisationCurrencies(command);
return Response.ok().entity(command).build();
}
}
@@ -15,21 +15,17 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.api.data.AppUserData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.OfficeLookup;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService;
import org.mifosng.platform.infrastructure.api.ApiParameterHelper;
import org.mifosng.platform.makerchecker.service.PortfolioCommandSourceWritePlatformService;
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.service.AppUserReadPlatformService;
import org.mifosng.platform.user.service.AppUserWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -45,14 +41,11 @@ public class UsersApiResource {
@Autowired
private OfficeReadPlatformService officeReadPlatformService;
@Autowired
private AppUserWritePlatformService appUserWritePlatformService;
@Autowired
private PortfolioApiDataConversionService apiDataConversionService;
@Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService;
@Autowired
private PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
private PlatformSecurityContext context;
@@ -114,41 +107,32 @@ public class UsersApiResource {
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createUser(final String jsonRequestBody) {
public String createUser(final String apiRequestBodyAsJson) {
final UserCommand command = this.apiDataConversionService.convertJsonToUserCommand(null, jsonRequestBody);
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("CREATE", "users", null, apiRequestBodyAsJson);
final Long userId = this.appUserWritePlatformService.createUser(command);
return Response.ok().entity(new EntityIdentifier(userId)).build();
return this.apiJsonSerializerService.serializeEntityIdentifier(result);
}
@DELETE
@Path("{userId}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response deleteUser(@PathParam("userId") final Long userId) {
public String deleteUser(@PathParam("userId") final Long userId) {
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("DELETE", "users", userId, "{}");
this.appUserWritePlatformService.deleteUser(userId);
return Response.ok(new EntityIdentifier(userId)).build();
return this.apiJsonSerializerService.serializeEntityIdentifier(result);
}
@PUT
@Path("{userId}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response updateUser(@PathParam("userId") final Long userId, final String jsonRequestBody) {
public String updateUser(@PathParam("userId") final Long userId, final String apiRequestBodyAsJson) {
final UserCommand command = this.apiDataConversionService.convertJsonToUserCommand(userId, jsonRequestBody);
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", "users", userId, apiRequestBodyAsJson);
final AppUser loggedInUser = context.authenticatedUser();
if (loggedInUser.hasIdOf(userId)) {
this.appUserWritePlatformService.updateUsersOwnAccountDetails(command);
} else {
this.appUserWritePlatformService.updateUser(command);
}
return Response.ok().entity(new EntityIdentifier(userId)).build();
return this.apiJsonSerializerService.serializeEntityIdentifier(result);
}
}
@@ -1,113 +1,114 @@
package org.mifosng.platform.api.commands;
import java.io.Serializable;
import java.util.Set;
/**
* Immutable command for creating or updating details of a User.
*/
public class UserCommand implements Serializable {
public class UserCommand {
private final Long id;
private final String username;
private final String firstname;
private final String lastname;
private final String password;
private final String repeatPassword;
private final String email;
private final Long officeId;
private final String[] notSelectedRoles;
private final String[] roles;
private final Set<String> modifiedParameters;
private final String username;
private final String firstname;
private final String lastname;
private final String password;
private final String repeatPassword;
private final String email;
private final Long officeId;
public UserCommand(final Set<String> modifiedParameters, final Long id,
final String username, final String firstname, final String lastname,
final String password, final String repeatPassword, final String email,
final Long officeId, final String[] notSelectedRoles, final String[] roles) {
this.modifiedParameters = modifiedParameters;
this.id = id;
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
this.password = password;
this.repeatPassword = repeatPassword;
this.email = email;
this.officeId = officeId;
this.notSelectedRoles = notSelectedRoles;
this.roles = roles;
}
public String getDisplayName() {
return new StringBuilder(this.lastname).append(", ").append(this.firstname).toString();
}
private final String[] notSelectedRoles;
private final String[] roles;
public Long getId() {
return id;
}
private transient final boolean makerCheckerApproval;
private transient final Long id;
private transient final Set<String> modifiedParameters;
public UserCommand(final Set<String> modifiedParameters, final boolean makerCheckerApproval,
final Long id, final String username, final String firstname,
final String lastname, final String password, final String repeatPassword, final String email, final Long officeId,
final String[] notSelectedRoles, final String[] roles) {
this.modifiedParameters = modifiedParameters;
this.makerCheckerApproval = makerCheckerApproval;
this.id = id;
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
this.password = password;
this.repeatPassword = repeatPassword;
this.email = email;
this.officeId = officeId;
this.notSelectedRoles = notSelectedRoles;
this.roles = roles;
}
public String getUsername() {
return username;
}
public Long getId() {
return id;
}
public String getFirstname() {
return firstname;
}
public String getUsername() {
return username;
}
public String getLastname() {
return lastname;
}
public String getFirstname() {
return firstname;
}
public String getPassword() {
return password;
}
public String getLastname() {
return lastname;
}
public String getRepeatPassword() {
return repeatPassword;
}
public String getPassword() {
return password;
}
public String getEmail() {
return email;
}
public String getRepeatPassword() {
return repeatPassword;
}
public Long getOfficeId() {
return officeId;
}
public String getEmail() {
return email;
}
public String[] getNotSelectedRoles() {
return notSelectedRoles;
}
public Long getOfficeId() {
return officeId;
}
public String[] getRoles() {
return roles;
}
public String[] getNotSelectedRoles() {
return notSelectedRoles;
}
public boolean isUsernameChanged() {
return this.modifiedParameters.contains("username");
}
public boolean isFirstnameChanged() {
return this.modifiedParameters.contains("firstname");
}
public boolean isLastnameChanged() {
return this.modifiedParameters.contains("lastname");
}
public boolean isEmailChanged() {
return this.modifiedParameters.contains("email");
}
public boolean isPasswordChanged() {
return this.modifiedParameters.contains("password") && this.modifiedParameters.contains("repeatPassword");
}
public boolean isOfficeChanged() {
return this.modifiedParameters.contains("officeId");
}
public boolean isRolesChanged() {
return this.modifiedParameters.contains("roles");
}
public String[] getRoles() {
return roles;
}
public boolean isUsernameChanged() {
return this.modifiedParameters.contains("username");
}
public boolean isFirstnameChanged() {
return this.modifiedParameters.contains("firstname");
}
public boolean isLastnameChanged() {
return this.modifiedParameters.contains("lastname");
}
public boolean isEmailChanged() {
return this.modifiedParameters.contains("email");
}
public boolean isPasswordChanged() {
return this.modifiedParameters.contains("password") && this.modifiedParameters.contains("repeatPassword");
}
public boolean isOfficeChanged() {
return this.modifiedParameters.contains("officeId");
}
public boolean isRolesChanged() {
return this.modifiedParameters.contains("roles");
}
public boolean isApprovedByChecker() {
return this.makerCheckerApproval;
}
}
@@ -44,7 +44,7 @@ public interface PortfolioApiDataConversionService {
RolePermissionCommand convertApiRequestJsonToRolePermissionCommand(Long roleId, String jsonRequestBody);
UserCommand convertJsonToUserCommand(Long resourceIdentifier, String json);
UserCommand convertApiRequestJsonToUserCommand(Long resourceIdentifier, String json);
BranchMoneyTransferCommand convertJsonToBranchMoneyTransferCommand(String json);
@@ -269,58 +269,22 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
}
@Override
public UserCommand convertJsonToUserCommand(final Long resourceIdentifier, final String json) {
public UserCommand convertApiRequestJsonToUserCommand(final Long resourceIdentifier, final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>(Arrays.asList("username", "firstname", "lastname", "password", "repeatPassword",
final Set<String> supportedParams = new HashSet<String>(Arrays.asList("username", "firstname", "lastname", "password", "repeatPassword",
"email", "officeId", "notSelectedRoles", "roles"));
checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>();
String username = extractStringParameter("username", requestMap, modifiedParameters);
String firstname = extractStringParameter("firstname", requestMap, modifiedParameters);
String lastname = extractStringParameter("lastname", requestMap, modifiedParameters);
String password = extractStringParameter("password", requestMap, modifiedParameters);
String repeatPassword = extractStringParameter("repeatPassword", requestMap, modifiedParameters);
String email = extractStringParameter("email", requestMap, modifiedParameters);
Long officeId = extractLongParameter("officeId", requestMap, modifiedParameters);
// check array
JsonParser parser = new JsonParser();
String[] notSelectedRoles = null;
String[] roles = null;
JsonElement element = parser.parse(json);
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has("notSelectedRoles")) {
modifiedParameters.add("notSelectedRoles");
JsonArray array = object.get("notSelectedRoles").getAsJsonArray();
notSelectedRoles = new String[array.size()];
for (int i = 0; i < array.size(); i++) {
notSelectedRoles[i] = array.get(i).getAsString();
}
}
if (object.has("roles")) {
modifiedParameters.add("roles");
JsonArray array = object.get("roles").getAsJsonArray();
roles = new String[array.size()];
for (int i = 0; i < array.size(); i++) {
roles[i] = array.get(i).getAsString();
}
}
}
//
return new UserCommand(modifiedParameters, resourceIdentifier, username, firstname, lastname, password, repeatPassword, email,
officeId, notSelectedRoles, roles);
// no difference between the api json and internal command
// representation of json so reuse this code to de-serialize into
// command object
return this.portfolioCommandDeserializerService.deserializeUserCommand(resourceIdentifier, json, false);
}
@Override
@@ -576,8 +540,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
// FIXME - A - LoanProduct - KW - Do we add support for
// "overpaidTolerance" parameter?
final Set<String> supportedParams = new HashSet<String>(Arrays.asList("name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType",
@@ -621,10 +583,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
final Integer interestCalculationPeriodType = helper.extractIntegerNamed("interestCalculationPeriodType", element,
modifiedParameters);
// FIXME - A - LoanProduct - KW - product charges at present dont allow
// for adding 'charge' to loan product and setting its 'amount', might
// this be needed?
// loan product charges
String[] charges = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
@@ -739,7 +697,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
loanTermFrequency, loanTermFrequencyType, inArrearsToleranceValue, charges, loanOfficerId);
}
// FIXME - KW - charges
@Override
public LoanChargeCommand convertJsonToLoanChargeCommand(final Long loanChargeId, final Long loanId, final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -1594,7 +1551,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
@Override
public CodeCommand convertJsonToCodeCommand(Long resourceIdentifier, String json) {
// TODO Auto-generated method stub
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
@@ -6,12 +6,15 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.accounting.api.commands.RolePermissionCommand;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.infrastructure.api.JsonParserHelper;
import org.mifosng.platform.infrastructure.errorhandling.InvalidJsonException;
import org.springframework.stereotype.Service;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
@@ -53,4 +56,50 @@ public class PortfolioCommandDeerializerServiceGoogleGson implements PortfolioCo
return new RolePermissionCommand(roleId, command.getPermissions(), makerCheckerApproval);
}
@Override
public UserCommand deserializeUserCommand(final Long userId, final String commandAsJson, final boolean makerCheckerApproval) {
if (StringUtils.isBlank(commandAsJson)) { throw new InvalidJsonException(); }
final JsonParserHelper helper = new JsonParserHelper();
final JsonElement element = parser.parse(commandAsJson);
final Set<String> parametersPassedInRequest = new HashSet<String>();
final String username = helper.extractStringNamed("username", element, parametersPassedInRequest);
final String firstname = helper.extractStringNamed("firstname", element, parametersPassedInRequest);
final String lastname = helper.extractStringNamed("lastname", element, parametersPassedInRequest);
final String password = helper.extractStringNamed("password", element, parametersPassedInRequest);
final String repeatPassword = helper.extractStringNamed("repeatPassword", element, parametersPassedInRequest);
final String email = helper.extractStringNamed("email", element, parametersPassedInRequest);
final Long officeId = helper.extractLongNamed("officeId", element, parametersPassedInRequest);
// check array
String[] notSelectedRoles = null;
String[] roles = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has("notSelectedRoles")) {
parametersPassedInRequest.add("notSelectedRoles");
JsonArray array = object.get("notSelectedRoles").getAsJsonArray();
notSelectedRoles = new String[array.size()];
for (int i = 0; i < array.size(); i++) {
notSelectedRoles[i] = array.get(i).getAsString();
}
}
if (object.has("roles")) {
parametersPassedInRequest.add("roles");
JsonArray array = object.get("roles").getAsJsonArray();
roles = new String[array.size()];
for (int i = 0; i < array.size(); i++) {
roles[i] = array.get(i).getAsString();
}
}
}
return new UserCommand(parametersPassedInRequest, makerCheckerApproval, userId, username, firstname, lastname,
password, repeatPassword, email, officeId, notSelectedRoles, roles);
}
}
@@ -2,6 +2,7 @@ package org.mifosng.platform.api.infrastructure;
import org.mifosng.platform.accounting.api.commands.RolePermissionCommand;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.api.commands.UserCommand;
/**
* Service for de-serializing JSON for a command into the platforms internal
@@ -18,4 +19,6 @@ public interface PortfolioCommandDeserializerService {
RoleCommand deserializeRoleCommand(Long roleId, String commandAsJson, boolean makerCheckerApproval);
RolePermissionCommand deserializeRolePermissionCommand(Long roleId, String commandAsJson, boolean makerCheckerApproval);
UserCommand deserializeUserCommand(Long resourceId, String jsonOfChangesOnly, boolean makerCheckerApproval);
}
@@ -2,6 +2,7 @@ package org.mifosng.platform.api.infrastructure;
import org.mifosng.platform.accounting.api.commands.RolePermissionCommand;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.api.commands.UserCommand;
/**
* Service for serializing commands into another format.
@@ -14,4 +15,6 @@ public interface PortfolioCommandSerializerService {
String serializeRoleCommandToJson(RoleCommand command);
String serializeRolePermissionCommandToJson(RolePermissionCommand command);
String serializeUserCommandToJson(UserCommand command);
}
@@ -4,6 +4,7 @@ import java.util.HashSet;
import org.mifosng.platform.accounting.api.commands.RolePermissionCommand;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.infrastructure.api.GoogleGsonSerializerHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -36,4 +37,10 @@ public class PortfolioCommandSerializerServiceJson implements PortfolioCommandSe
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(new HashSet<String>(), false, new HashSet<String>());
return helper.serializedJsonFrom(gsonDeserializer, command);
}
@Override
public String serializeUserCommandToJson(final UserCommand command) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(new HashSet<String>(), false, new HashSet<String>());
return helper.serializedJsonFrom(gsonDeserializer, command);
}
}
@@ -30,6 +30,8 @@ import org.mifosng.platform.organisation.domain.CodeValueRepository;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.Permission;
import org.mifosng.platform.user.domain.PermissionRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,21 +50,19 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final OfficeRepository officeRepository;
private final NoteRepository noteRepository;
private final CodeValueRepository codeValueRepository;
// FIXME - kw - this value is hardcoded for now but will be retrieved from
// configuration
private boolean makerCheckerEnabledForTask = false;
private final PermissionRepository permissionRepository;
@Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository,
final ClientIdentifierRepository clientIdentifierRepository, final OfficeRepository officeRepository,
NoteRepository noteRepository, final CodeValueRepository codeValueRepository) {
NoteRepository noteRepository, final CodeValueRepository codeValueRepository, final PermissionRepository permissionRepository) {
this.context = context;
this.clientRepository = clientRepository;
this.clientIdentifierRepository = clientIdentifierRepository;
this.officeRepository = officeRepository;
this.noteRepository = noteRepository;
this.codeValueRepository = codeValueRepository;
this.permissionRepository = permissionRepository;
}
@Transactional
@@ -76,8 +76,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
client.delete();
this.clientRepository.save(client);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("DELETE_CLIENT");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return new EntityIdentifier(client.getId());
}
@@ -92,7 +93,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
"error.msg.client.duplicate.externalId", "Client with externalId `" + command.getExternalId() + "` already exists",
"externalId", command.getExternalId()); }
logger.error(dve.getMessage(), dve);
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.client.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
@@ -120,8 +121,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Client newClient = Client.newClient(clientOffice, firstname, lastname, command.getJoiningDate(), command.getExternalId());
this.clientRepository.save(newClient);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("CREATE_CLIENT");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return newClient.getId();
} catch (DataIntegrityViolationException dve) {
@@ -153,8 +155,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
this.clientRepository.saveAndFlush(clientForUpdate);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("UPDATE_CLIENT");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return new EntityIdentifier(clientForUpdate.getId());
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
@@ -285,12 +288,15 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
} else if (dve.getMostSpecificCause().getMessage().contains("unique_identifier_key")) { throw new DuplicateClientIdentifierException(
documentTypeId, documentTypeLabel, documentKey); }
// only log as error if unexpected data integrity violation.
logger.error(dve.getMessage(), dve);
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.clientIdentifier.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
}
@Transactional
@Override
public EntityIdentifier deleteClientIdentifier(final Long clientIdentifierId) {
@@ -4,6 +4,6 @@ import org.mifosng.platform.api.data.ConfigurationData;
public interface ConfigurationReadPlatformService {
ConfigurationData retrieveCurrencyConfiguration();
ConfigurationData retrieveCurrencyConfiguration();
}
@@ -12,24 +12,25 @@ import org.springframework.stereotype.Service;
@Service
public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPlatformService {
private final CurrencyReadPlatformService currencyReadPlatformService;
private final CurrencyReadPlatformService currencyReadPlatformService;
@Autowired
public ConfigurationReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService) {
this.currencyReadPlatformService = currencyReadPlatformService;
}
@Autowired
public ConfigurationReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService) {
this.currencyReadPlatformService = currencyReadPlatformService;
}
@Override
public ConfigurationData retrieveCurrencyConfiguration() {
List<CurrencyData> selectedCurrencyOptions = new ArrayList<CurrencyData>(this.currencyReadPlatformService.retrieveAllowedCurrencies());
List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>(this.currencyReadPlatformService.retrieveAllPlatformCurrencies());
@Override
public ConfigurationData retrieveCurrencyConfiguration() {
// remove selected currency options
currencyOptions.removeAll(selectedCurrencyOptions);
ConfigurationData configurationData = new ConfigurationData(currencyOptions, selectedCurrencyOptions);
return configurationData;
}
List<CurrencyData> selectedCurrencyOptions = new ArrayList<CurrencyData>(
this.currencyReadPlatformService.retrieveAllowedCurrencies());
List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>(this.currencyReadPlatformService.retrieveAllPlatformCurrencies());
// remove selected currency options
currencyOptions.removeAll(selectedCurrencyOptions);
ConfigurationData configurationData = new ConfigurationData(currencyOptions, selectedCurrencyOptions);
return configurationData;
}
}
@@ -5,7 +5,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface ConfigurationWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'ORGANISATION_ADMINISTRATION_SUPER_USER', 'UPDATE_CURRENCY')")
void updateOrganisationCurrencies(OrganisationCurrencyCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'ORGANISATION_ADMINISTRATION_SUPER_USER', 'UPDATE_CURRENCY')")
void updateOrganisationCurrencies(OrganisationCurrencyCommand command);
}
@@ -21,54 +21,53 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class ConfigurationWritePlatformServiceJpaRepositoryImpl implements ConfigurationWritePlatformService {
private final PlatformSecurityContext context;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final OrganisationCurrencyRepository organisationCurrencyRepository;
private final PlatformSecurityContext context;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final OrganisationCurrencyRepository organisationCurrencyRepository;
@Autowired
public ConfigurationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final ApplicationCurrencyRepository applicationCurrencyRepository, final OrganisationCurrencyRepository organisationCurrencyRepository) {
this.context = context;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.organisationCurrencyRepository = organisationCurrencyRepository;
}
@Transactional
@Override
public void updateOrganisationCurrencies(final OrganisationCurrencyCommand command) {
context.authenticatedUser();
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("configuration");
baseDataValidator.reset().parameter("currencies").value(command.getCurrencies()).arrayNotEmpty();
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
Set<OrganisationCurrency> allowedCurrencies = new HashSet<OrganisationCurrency>();
@Autowired
public ConfigurationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final ApplicationCurrencyRepository applicationCurrencyRepository,
final OrganisationCurrencyRepository organisationCurrencyRepository) {
this.context = context;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.organisationCurrencyRepository = organisationCurrencyRepository;
}
for (String currencyCode : command.getCurrencies()) {
@Transactional
@Override
public void updateOrganisationCurrencies(final OrganisationCurrencyCommand command) {
ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode);
if (currency == null) {
baseDataValidator.reset().parameter("currencies").value(command.getCurrencies()).inValidValue("currency.code", currencyCode);
} else {
OrganisationCurrency allowedCurrency = new OrganisationCurrency(
currency.getCode(), currency.getName(),
currency.getDecimalPlaces(), currency.getNameCode(), currency.getDisplaySymbol());
context.authenticatedUser();
allowedCurrencies.add(allowedCurrency);
}
}
this.organisationCurrencyRepository.deleteAll();
this.organisationCurrencyRepository.save(allowedCurrencies);
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("configuration");
baseDataValidator.reset().parameter("currencies").value(command.getCurrencies()).arrayNotEmpty();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
Set<OrganisationCurrency> allowedCurrencies = new HashSet<OrganisationCurrency>();
for (String currencyCode : command.getCurrencies()) {
ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode);
if (currency == null) {
baseDataValidator.reset().parameter("currencies").value(command.getCurrencies())
.inValidValue("currency.code", currencyCode);
} else {
OrganisationCurrency allowedCurrency = new OrganisationCurrency(currency.getCode(), currency.getName(),
currency.getDecimalPlaces(), currency.getNameCode(), currency.getDisplaySymbol());
allowedCurrencies.add(allowedCurrency);
}
}
this.organisationCurrencyRepository.deleteAll();
this.organisationCurrencyRepository.save(allowedCurrencies);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -131,4 +131,8 @@ public class CommandSource extends AbstractPersistable<Long> {
public boolean isRoleResource() {
return this.resource.equalsIgnoreCase("ROLES");
}
public boolean isUserResource() {
return this.resource.equalsIgnoreCase("USERS");
}
}
@@ -85,6 +85,8 @@ public class ClientCommandHandler implements CommandSourceHandler {
public CommandSource handle(final CommandSource commandSourceResult) {
final AppUser checker = context.authenticatedUser();
Long resourceId = null;
if (commandSourceResult.isClientResource()) {
final ClientCommand command = this.apiDataConversionService.convertInternalJsonFormatToClientCommand(
@@ -92,16 +94,15 @@ public class ClientCommandHandler implements CommandSourceHandler {
if (commandSourceResult.isCreate()) {
resourceId = this.clientWritePlatformService.createClient(command);
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isUpdate()) {
// TODO - is there a case for doing change detection at this
// point also?
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command);
resourceId = result.getEntityId();
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isDelete()) {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
resourceId = result.getEntityId();
commandSourceResult.markAsChecked(checker, new LocalDate());
}
}
@@ -15,14 +15,17 @@ public class CommandSourceHandlerDelegator {
private final PlatformSecurityContext context;
private final ClientCommandHandler clientCommandHandler;
private final RoleCommandHandler roleCommandHandler;
private final UserCommandHandler userCommandHandler;
@Autowired
public CommandSourceHandlerDelegator(final PlatformSecurityContext context,
final ClientCommandHandler clientCommandHandler,
final RoleCommandHandler roleCommandHandler) {
final RoleCommandHandler roleCommandHandler,
final UserCommandHandler userCommandHandler) {
this.context = context;
this.clientCommandHandler = clientCommandHandler;
this.roleCommandHandler = roleCommandHandler;
this.userCommandHandler = userCommandHandler;
}
public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) {
@@ -33,13 +36,15 @@ public class CommandSourceHandlerDelegator {
commandSourceResult = clientCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else if (commandSource.isRoleResource()) {
commandSourceResult = roleCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else if (commandSource.isUserResource()) {
commandSourceResult = userCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else {
throw new UnsupportedCommandException(commandSource.commandName());
}
return commandSourceResult;
}
public CommandSource handleExistingCommand(final CommandSource commandSource) {
CommandSource commandSourceResult = null;
@@ -47,6 +52,8 @@ public class CommandSourceHandlerDelegator {
commandSourceResult = clientCommandHandler.handle(commandSource);
} else if (commandSource.isRoleResource()) {
commandSourceResult = roleCommandHandler.handle(commandSource);
} else if (commandSource.isUserResource()) {
commandSourceResult = userCommandHandler.handle(commandSource);
} else {
throw new UnsupportedCommandException(commandSource.commandName());
}
@@ -44,13 +44,10 @@ public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandS
@Override
public EntityIdentifier approveEntry(final Long id) {
final AppUser checker = context.authenticatedUser();
context.authenticatedUser();
final CommandSource commandSourceInput = this.commandSourceRepository.findOne(id);
commandSourceInput.markAsChecked(checker, new LocalDate());
final CommandSource commandSourceResult = this.commandSourceHandlerDelegator.handleExistingCommand(commandSourceInput);
commandSourceRepository.save(commandSourceResult);
return EntityIdentifier.makerChecker(commandSourceResult.resourceId(), commandSourceResult.getId());
@@ -112,17 +112,22 @@ public class RoleCommandHandler implements CommandSourceHandler {
*/
public CommandSource handle(final CommandSource commandSourceResult) {
final AppUser checker = context.authenticatedUser();
Long resourceId = commandSourceResult.resourceId();
if (commandSourceResult.isRoleResource()) {
if (commandSourceResult.isCreate()) {
final RoleCommand command = this.commandDeserializerService.deserializeRoleCommand(resourceId, commandSourceResult.json(), true);
resourceId = this.writePlatformService.createRole(command);
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isUpdate()) {
final RoleCommand command = this.commandDeserializerService.deserializeRoleCommand(resourceId, commandSourceResult.json(), true);
resourceId = this.writePlatformService.updateRole(command);
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isUpdateRolePermissions()) {
final RolePermissionCommand command = this.commandDeserializerService.deserializeRolePermissionCommand(resourceId, commandSourceResult.json(), true);
resourceId =this.writePlatformService.updateRolePermissions(command);
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isDelete()) {
throw new UnsupportedCommandException(commandSourceResult.commandName());
}
@@ -0,0 +1,135 @@
package org.mifosng.platform.makerchecker.service;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioCommandDeserializerService;
import org.mifosng.platform.api.infrastructure.PortfolioCommandSerializerService;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.makerchecker.domain.CommandSource;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.service.AppUserWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserCommandHandler implements CommandSourceHandler {
private final PlatformSecurityContext context;
private final ChangeDetectionService changeDetectionService;
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioCommandSerializerService commandSerializerService;
private final PortfolioCommandDeserializerService commandDeserializerService;
private final AppUserWritePlatformService writePlatformService;
@Autowired
public UserCommandHandler(final PlatformSecurityContext context, final ChangeDetectionService changeDetectionService,
final PortfolioApiDataConversionService apiDataConversionService,
final PortfolioCommandSerializerService commandSerializerService,
final PortfolioCommandDeserializerService commandDeserializerService,
final AppUserWritePlatformService writePlatformService) {
this.context = context;
this.changeDetectionService = changeDetectionService;
this.apiDataConversionService = apiDataConversionService;
this.commandSerializerService = commandSerializerService;
this.commandDeserializerService = commandDeserializerService;
this.writePlatformService = writePlatformService;
}
/*
* Used when users with 'create' capability create a command. If 'maker-checker' is not
* enabled for this specific command then the 'creator' is also marked 'as the checker' and command
* automatically is processed and changes state of system.
*/
public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) {
final AppUser maker = context.authenticatedUser();
final LocalDate asToday = new LocalDate();
CommandSource commandSourceResult = commandSource.copy();
final Long resourceId = commandSource.resourceId();
if (commandSource.isCreate()) {
try {
final UserCommand command = this.apiDataConversionService.convertApiRequestJsonToUserCommand(null, apiRequestBodyInJson);
final String commandSerializedAsJson = this.commandSerializerService.serializeUserCommandToJson(command);
commandSourceResult.updateJsonTo(commandSerializedAsJson);
Long newResourceId = this.writePlatformService.createUser(command);
commandSourceResult.markAsChecked(maker, asToday);
commandSourceResult.updateResourceId(newResourceId);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isUpdate()) {
try {
final UserCommand command = this.apiDataConversionService.convertApiRequestJsonToUserCommand(resourceId, apiRequestBodyInJson);
final String commandSerializedAsJson = this.commandSerializerService.serializeUserCommandToJson(command);
commandSourceResult.updateJsonTo(commandSerializedAsJson);
final String jsonOfChangesOnly = this.changeDetectionService.detectChangesOnUpdate(commandSource.resourceName(), commandSource.resourceId(), commandSerializedAsJson);
commandSourceResult.updateJsonTo(jsonOfChangesOnly);
final UserCommand changesOnly = this.commandDeserializerService.deserializeUserCommand(resourceId, jsonOfChangesOnly, false);
if (maker.hasIdOf(command.getId())) {
this.writePlatformService.updateUsersOwnAccountDetails(changesOnly);
} else {
this.writePlatformService.updateUser(changesOnly);
}
commandSourceResult.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isDelete()) {
try {
final UserCommand command = this.apiDataConversionService.convertApiRequestJsonToUserCommand(resourceId, apiRequestBodyInJson);
final String commandSerializedAsJson = this.commandSerializerService.serializeUserCommandToJson(command);
commandSourceResult.updateJsonTo(commandSerializedAsJson);
this.writePlatformService.deleteUser(command);
commandSourceResult.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
}
return commandSourceResult;
}
/*
* Used when users with 'checker' capability approve a command.
*/
public CommandSource handle(final CommandSource commandSourceResult) {
final AppUser checker = context.authenticatedUser();
Long resourceId = commandSourceResult.resourceId();
if (commandSourceResult.isUserResource()) {
if (commandSourceResult.isCreate()) {
final UserCommand command = this.commandDeserializerService.deserializeUserCommand(resourceId, commandSourceResult.json(), true);
resourceId = this.writePlatformService.createUser(command);
commandSourceResult.updateResourceId(resourceId);
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isUpdate()) {
final UserCommand command = this.commandDeserializerService.deserializeUserCommand(resourceId, commandSourceResult.json(), true);
if (checker.hasIdOf(command.getId())) {
this.writePlatformService.updateUsersOwnAccountDetails(command);
} else {
this.writePlatformService.updateUser(command);
}
commandSourceResult.markAsChecked(checker, new LocalDate());
} else if (commandSourceResult.isDelete()) {
final UserCommand command = this.commandDeserializerService.deserializeUserCommand(resourceId, commandSourceResult.json(), true);
this.writePlatformService.deleteUser(command);
commandSourceResult.markAsChecked(checker, new LocalDate());
}
}
return commandSourceResult;
}
}
@@ -1,5 +1,6 @@
package org.mifosng.platform.user.domain;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.infrastructure.EmailDetail;
import org.mifosng.platform.infrastructure.PlatformEmailService;
import org.mifosng.platform.infrastructure.PlatformPasswordEncoder;
@@ -11,37 +12,46 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class JpaUserDomainService implements UserDomainService {
private final AppUserRepository userRepository;
private final PlatformPasswordEncoder applicationPasswordEncoder;
private final PlatformEmailService emailService;
private final PermissionRepository permissionRepository;
@Autowired
private AppUserRepository userRepository;
@Autowired
private PlatformPasswordEncoder applicationPasswordEncoder;
@Autowired
private PlatformEmailService emailService;
public JpaUserDomainService(final AppUserRepository userRepository, final PlatformPasswordEncoder applicationPasswordEncoder,
final PlatformEmailService emailService, final PermissionRepository permissionRepository) {
this.userRepository = userRepository;
this.applicationPasswordEncoder = applicationPasswordEncoder;
this.emailService = emailService;
this.permissionRepository = permissionRepository;
}
@Transactional
@Override
public void create(final AppUser appUser) {
public void create(final AppUser appUser, final boolean isApprovedByChecker) {
generateKeyUsedForPasswordSalting(appUser);
String unencodedPassword = appUser.getPassword();
if (org.apache.commons.lang.StringUtils.isBlank(unencodedPassword) || "autogenerate".equalsIgnoreCase(unencodedPassword)) {
unencodedPassword = new RandomPasswordGenerator(13).generate();
appUser.updatePassword(unencodedPassword);
unencodedPassword = new RandomPasswordGenerator(13).generate();
appUser.updatePassword(unencodedPassword);
}
String encodePassword = this.applicationPasswordEncoder.encode(appUser);
appUser.updatePassword(encodePassword);
this.userRepository.save(appUser);
final String encodePassword = this.applicationPasswordEncoder.encode(appUser);
appUser.updatePassword(encodePassword);
this.userRepository.save(appUser);
EmailDetail emailDetail = new EmailDetail(appUser.getFirstname(), appUser.getFirstname(), appUser.getEmail(), appUser.getUsername());
final Permission thisTask = this.permissionRepository.findOneByCode("CREATE_USER");
if (thisTask.hasMakerCheckerEnabled() && !isApprovedByChecker) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final EmailDetail emailDetail = new EmailDetail(appUser.getFirstname(), appUser.getFirstname(), appUser.getEmail(), appUser.getUsername());
this.emailService.sendToUserAccount(emailDetail, unencodedPassword);
}
private void generateKeyUsedForPasswordSalting(final AppUser appUser) {
this.userRepository.save(appUser);
}
private void generateKeyUsedForPasswordSalting(final AppUser appUser) {
this.userRepository.save(appUser);
}
}
@@ -25,15 +25,15 @@ public class Permission extends AbstractPersistable<Long> {
@Column(name = "action_name", nullable = true, length = 100)
private final String actionName;
@SuppressWarnings("unused")
@Column(name = "can_maker_checker", nullable = false)
private Boolean canMakerChecker;
private final boolean canMakerChecker;
protected Permission() {
this.grouping = null;
this.code = null;
this.entityName = null;
this.actionName = null;
this.canMakerChecker = false;
}
public boolean hasCode(final String checkCode) {
@@ -43,4 +43,8 @@ public class Permission extends AbstractPersistable<Long> {
public String code() {
return this.code;
}
public boolean hasMakerCheckerEnabled() {
return this.canMakerChecker;
}
}
@@ -3,5 +3,6 @@ package org.mifosng.platform.user.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PermissionRepository extends JpaRepository<Permission, Long> {
// no added behaviour
Permission findOneByCode(String code);
}
@@ -2,5 +2,5 @@ package org.mifosng.platform.user.domain;
public interface UserDomainService {
void create(AppUser appUser);
void create(AppUser appUser, boolean isApprovedByChecker);
}
@@ -6,15 +6,15 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface AppUserWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'USER_ADMINISTRATION_SUPER_USER', 'CREATE_USER')")
Long createUser(final UserCommand command);
Long createUser(UserCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'USER_ADMINISTRATION_SUPER_USER', 'UPDATE_USER')")
Long updateUser(final UserCommand command);
Long updateUser(UserCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'USER_ADMINISTRATION_SUPER_USER', 'DELETE_USER')")
void deleteUser(final Long userId);
void deleteUser(UserCommand command);
// we dont put any permissions on this e.g. a user with no admin rights can
// change their own details (email, username, password etc).
void updateUsersOwnAccountDetails(UserCommand command);
Long updateUsersOwnAccountDetails(UserCommand command);
}
@@ -8,6 +8,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.api.data.ApiParameterError;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.exceptions.OfficeNotFoundException;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
@@ -22,6 +23,8 @@ import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.mifosng.platform.user.domain.Permission;
import org.mifosng.platform.user.domain.PermissionRepository;
import org.mifosng.platform.user.domain.Role;
import org.mifosng.platform.user.domain.RoleRepository;
import org.mifosng.platform.user.domain.UserDomainService;
@@ -44,17 +47,19 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
private final AppUserRepository appUserRepository;
private final OfficeRepository officeRepository;
private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository;
@Autowired
public AppUserWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final AppUserRepository appUserRepository,
final UserDomainService userDomainService, final OfficeRepository officeRepository, final RoleRepository roleRepository,
final PlatformPasswordEncoder platformPasswordEncoder) {
final PlatformPasswordEncoder platformPasswordEncoder, final PermissionRepository permissionRepository) {
this.context = context;
this.appUserRepository = appUserRepository;
this.userDomainService = userDomainService;
this.officeRepository = officeRepository;
this.roleRepository = roleRepository;
this.platformPasswordEncoder = platformPasswordEncoder;
this.permissionRepository = permissionRepository;
}
@Transactional
@@ -64,12 +69,12 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
try {
context.authenticatedUser();
UserCommandValidator validator = new UserCommandValidator(command);
final UserCommandValidator validator = new UserCommandValidator(command);
validator.validateForCreate();
final Set<Role> allRoles = assembleSetOfRoles(command);
Office office = this.officeRepository.findOne(command.getOfficeId());
final Office office = this.officeRepository.findOne(command.getOfficeId());
if (office == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
String password = command.getPassword();
@@ -77,17 +82,17 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
password = "autogenerate";
}
AppUser appUser = AppUser.createNew(office, allRoles, command.getUsername(), command.getEmail(), command.getFirstname(),
final AppUser appUser = AppUser.createNew(office, allRoles, command.getUsername(), command.getEmail(), command.getFirstname(),
command.getLastname(), password);
this.userDomainService.create(appUser);
this.userDomainService.create(appUser, command.isApprovedByChecker());
return appUser.getId();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return Long.valueOf(-1);
} catch (PlatformEmailSendException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("error.msg.user.email.invalid", "The parameter email is invalid.",
"email", command.getEmail());
dataValidationErrors.add(error);
@@ -103,10 +108,10 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
try {
context.authenticatedUser();
UserCommandValidator validator = new UserCommandValidator(command);
validator.validateForUpdate();
final Set<Role> allRoles = assembleSetOfRoles(command);
Office office = null;
@@ -115,7 +120,7 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
if (office == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
}
AppUser userToUpdate = this.appUserRepository.findOne(command.getId());
final AppUser userToUpdate = this.appUserRepository.findOne(command.getId());
if (userToUpdate == null) { throw new UserNotFoundException(command.getId()); }
userToUpdate.update(allRoles, office, command);
@@ -131,6 +136,9 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
this.appUserRepository.saveAndFlush(userToUpdate);
}
final Permission thisTask = this.permissionRepository.findOneByCode("UPDATE_USER");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return userToUpdate.getId();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
@@ -138,10 +146,50 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
}
}
/**
* Different between this and <code>updateUser</code> is that we dont do
* maker-checker flow or require that user have particular permission to
* change their own details.
*/
@Transactional
@Override
public void updateUsersOwnAccountDetails(final UserCommand command) {
updateUser(command);
public Long updateUsersOwnAccountDetails(final UserCommand command) {
try {
context.authenticatedUser();
UserCommandValidator validator = new UserCommandValidator(command);
validator.validateForUpdate();
final Set<Role> allRoles = assembleSetOfRoles(command);
Office office = null;
if (command.isOfficeChanged()) {
office = this.officeRepository.findOne(command.getOfficeId());
if (office == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
}
final AppUser userToUpdate = this.appUserRepository.findOne(command.getId());
if (userToUpdate == null) { throw new UserNotFoundException(command.getId()); }
userToUpdate.update(allRoles, office, command);
this.appUserRepository.saveAndFlush(userToUpdate);
if (command.isPasswordChanged()) {
PlatformUser dummyPlatformUser = new BasicPasswordEncodablePlatformUser(userToUpdate.getId(), userToUpdate.getUsername(),
command.getPassword());
String newPasswordEncoded = this.platformPasswordEncoder.encode(dummyPlatformUser);
userToUpdate.updatePassword(newPasswordEncoded);
this.appUserRepository.saveAndFlush(userToUpdate);
}
return userToUpdate.getId();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return Long.valueOf(-1);
}
}
private Set<Role> assembleSetOfRoles(final UserCommand command) {
@@ -163,13 +211,18 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
@Transactional
@Override
public void deleteUser(final Long userId) {
public void deleteUser(final UserCommand command) {
final Permission permissionForThisTask = this.permissionRepository.findOneByCode("UPDATE_USER");
final Long userId = command.getId();
AppUser user = this.appUserRepository.findOne(userId);
if (user == null || user.isDeleted()) { throw new UserNotFoundException(userId); }
user.delete();
this.appUserRepository.save(user);
if (permissionForThisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
}
/*
@@ -24,10 +24,6 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository;
// FIXME - kw - this value is hardcoded for now but will be retrieved from
// configuration
private boolean makerCheckerEnabledForTask = true;
@Autowired
public RoleWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final RoleRepository roleRepository,
final PermissionRepository permissionRepository) {
@@ -42,15 +38,16 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
context.authenticatedUser();
RoleCommandValidator validator = new RoleCommandValidator(command);
final RoleCommandValidator validator = new RoleCommandValidator(command);
validator.validateForCreate();
Role entity = new Role(command.getName(), command.getDescription());
final Role entity = new Role(command.getName(), command.getDescription());
this.roleRepository.save(entity);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("CREATE_ROLE");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return entity.getId();
}
@@ -60,16 +57,17 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
context.authenticatedUser();
RoleCommandValidator validator = new RoleCommandValidator(command);
final RoleCommandValidator validator = new RoleCommandValidator(command);
validator.validateForUpdate();
Role role = this.roleRepository.findOne(command.getId());
final Role role = this.roleRepository.findOne(command.getId());
if (role == null) { throw new RoleNotFoundException(command.getId()); }
role.update(command);
this.roleRepository.save(role);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("UPDATE_ROLE");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return role.getId();
}
@@ -98,8 +96,9 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
this.roleRepository.save(role);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
final Permission thisTask = this.permissionRepository.findOneByCode("PERMISSIONS_ROLE");
if (thisTask.hasMakerCheckerEnabled() && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return role.getId();
}
@@ -18,10 +18,8 @@
<logger name="org.mifosng.platform" level="debug" />
<logger name="org.springframework.web" level="debug" />
<logger name="org.springframework.web" level="info" />
<logger name="org.springframework.beans" level="debug" />
<logger name="org.springframework.orm" level="debug" />
<logger name="org.springframework.web" level="debug" />
<logger name="net.sf.ehcache" level="error" />
<logger name="org.hibernate.cache" level="error" />