diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiErrorMessageArg.java b/mifosng-data/src/main/java/org/mifosng/data/ApiErrorMessageArg.java new file mode 100644 index 000000000..22843ba22 --- /dev/null +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiErrorMessageArg.java @@ -0,0 +1,32 @@ +package org.mifosng.data; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name="messageArg") +public class ApiErrorMessageArg { + + /** + * The actual value of the parameter (if any) as passed to API. + */ + private Object value; + + public static ApiErrorMessageArg from(Object object) { + return new ApiErrorMessageArg(object); + } + + protected ApiErrorMessageArg() { + // + } + + public ApiErrorMessageArg(Object object) { + this.value = object; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} \ No newline at end of file diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java new file mode 100644 index 000000000..7a093a53b --- /dev/null +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java @@ -0,0 +1,174 @@ +package org.mifosng.data; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + */ +@XmlRootElement +public class ApiGlobalErrorResponse { + + /** + * A developer friendly plain English description of why the HTTP error response was returned from the API. + */ + private String developerMessage; + + /** + * A link to a URL which provides more information on the details of this type of error. + */ + private String developerDocLink; + + /** + * The HTTP status code of the response. + */ + private String httpStatusCode; + + /** + * A user friendly plain English description of why the HTTP error response was returned from the API that can be presented to end users. + */ + private String defaultUserMessage; + + /** + * A code that can be used for globalisation support by client applications of the API. + */ + private String userMessageGlobalisationCode; + + /** + * A list of zero or more of the actual reasons for the HTTP error should they be needed. Typically used to express data validation errors with parameters passed to API. + */ + private List errors = new ArrayList(); + + public static ApiGlobalErrorResponse unAuthenticated() { + + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("401"); + globalErrorResponse.setDeveloperMessage("No authentication details were passed in api request."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated"); + globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login."); + + return globalErrorResponse; + } + + public static ApiGlobalErrorResponse unAuthorized() { + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("403"); + globalErrorResponse.setDeveloperMessage("The user associated with credentials passed on this request does not have sufficient privileges to perform this action."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authorized"); + globalErrorResponse.setDefaultUserMessage("Insufficient priviledges to perform this action."); + + return globalErrorResponse; + } + + + public static ApiGlobalErrorResponse notFound(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("404"); + globalErrorResponse.setDeveloperMessage("The requested resource is not available."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode("error.msg.resource.not.found"); + globalErrorResponse.setDefaultUserMessage("The requested resource is not available."); + + List errors = new ArrayList(); + errors.add(ApiParameterError.resourceIdentifierNotFound(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs)); + globalErrorResponse.setErrors(errors); + + return globalErrorResponse; + } + + public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage, final String parameterName, final Object... defaultUserMessageArgs) { + + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("400"); + globalErrorResponse.setDeveloperMessage("The request caused a data integrity issue to be fired by the database."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode); + globalErrorResponse.setDefaultUserMessage(defaultUserMessage); + + List errors = new ArrayList(); + errors.add(ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, parameterName, defaultUserMessageArgs)); + globalErrorResponse.setErrors(errors); + + return globalErrorResponse; + } + + public static ApiGlobalErrorResponse badClientRequest(final String globalisationMessageCode, final String defaultUserMessage, + List errors) { + + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("400"); + globalErrorResponse.setDeveloperMessage("The request was invalid. This typically will happen due to validation errors which are provided."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode); + globalErrorResponse.setDefaultUserMessage(defaultUserMessage); + + globalErrorResponse.setErrors(errors); + + return globalErrorResponse; + } + + protected ApiGlobalErrorResponse() { + // + } + + public ApiGlobalErrorResponse(final List errors) { + this.errors = errors; + } + + @XmlElementWrapper(name="errors") + @XmlElement(name="errorResponse") + public List getErrors() { + return errors; + } + + public void setErrors(List errors) { + this.errors = errors; + } + + public String getDeveloperMessage() { + return developerMessage; + } + + public void setDeveloperMessage(String developerMessage) { + this.developerMessage = developerMessage; + } + + public String getHttpStatusCode() { + return httpStatusCode; + } + + public void setHttpStatusCode(String httpStatusCode) { + this.httpStatusCode = httpStatusCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public void setDefaultUserMessage(String defaultUserMessage) { + this.defaultUserMessage = defaultUserMessage; + } + + public String getUserMessageGlobalisationCode() { + return userMessageGlobalisationCode; + } + + public void setUserMessageGlobalisationCode(String userMessageGlobalisationCode) { + this.userMessageGlobalisationCode = userMessageGlobalisationCode; + } + + public String getDeveloperDocLink() { + return developerDocLink; + } + + public void setDeveloperDocLink(String developerDocLink) { + this.developerDocLink = developerDocLink; + } +} \ No newline at end of file diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java b/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java new file mode 100644 index 000000000..603e484ea --- /dev/null +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java @@ -0,0 +1,120 @@ +package org.mifosng.data; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name="apiParameterError") +public class ApiParameterError { + + /** + * A developer friendly plain English description of why the HTTP error response was returned from the API. + */ + private String developerMessage; + + /** + * A user friendly plain English description of why the HTTP error response was returned from the API that can be presented to end users. + */ + private String defaultUserMessage; + + /** + * A code that can be used for globalisation support by client applications of the API. + */ + private String userMessageGlobalisationCode; + + /** + * The name of the field or parameter passed to the API that this error relates to. + */ + private String parameterName; + + /** + * The actual value of the parameter (if any) as passed to API. + */ + private Object value; + + /** + * Arguments related to the user error message. + */ + private List args = new ArrayList(); + + + public static ApiParameterError resourceIdentifierNotFound(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + return new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); + } + + public static ApiParameterError parameterError(final String globalisationMessageCode, final String defaultUserMessage, + final String parameterName, final Object... defaultUserMessageArgs) { + ApiParameterError error = new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); + error.setParameterName(parameterName); + return error; + } + + protected ApiParameterError() { + // + } + + private ApiParameterError(final String globalisationMessageCode, final String defaultUserMessage, final Object[] defaultUserMessageArgs) { + this.userMessageGlobalisationCode = globalisationMessageCode; + this.developerMessage = defaultUserMessage; + this.defaultUserMessage = defaultUserMessage; + + List messageArgs = new ArrayList(); + if (defaultUserMessageArgs != null) { + for (Object object : defaultUserMessageArgs) { + messageArgs.add(ApiErrorMessageArg.from(object)); + } + } + this.args = messageArgs; + + this.parameterName="identifier"; + } + + public String getDeveloperMessage() { + return developerMessage; + } + + public void setDeveloperMessage(String developerMessage) { + this.developerMessage = developerMessage; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public void setDefaultUserMessage(String defaultUserMessage) { + this.defaultUserMessage = defaultUserMessage; + } + + public String getUserMessageGlobalisationCode() { + return userMessageGlobalisationCode; + } + + public void setUserMessageGlobalisationCode(String userMessageGlobalisationCode) { + this.userMessageGlobalisationCode = userMessageGlobalisationCode; + } + + public String getParameterName() { + return parameterName; + } + + public void setParameterName(String parameterName) { + this.parameterName = parameterName; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public List getArgs() { + return args; + } + + public void setArgs(List args) { + this.args = args; + } +} \ No newline at end of file diff --git a/mifosng-data/src/main/java/org/mifosng/data/command/UpdateLoanProductCommand.java b/mifosng-data/src/main/java/org/mifosng/data/command/UpdateLoanProductCommand.java index 07da0703f..b5a1a469a 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/command/UpdateLoanProductCommand.java +++ b/mifosng-data/src/main/java/org/mifosng/data/command/UpdateLoanProductCommand.java @@ -33,6 +33,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed); } + @Override public String getName() { return this.name; } @@ -41,6 +42,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.name = name; } + @Override public String getDescription() { return this.description; } @@ -49,6 +51,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.description = description; } + @Override public String getCurrencyCode() { return this.commonLoanProperties.getCurrencyCode(); } @@ -57,6 +60,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setCurrencyCode(currencyCode); } + @Override public Integer getDigitsAfterDecimal() { return this.commonLoanProperties.getDigitsAfterDecimal(); } @@ -65,6 +69,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setDigitsAfterDecimal(digitsAfterDecimal); } + @Override public Integer getRepaymentEvery() { return this.commonLoanProperties.getRepaymentEvery(); } @@ -73,6 +78,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setRepaymentEvery(repaymentEvery); } + @Override public Integer getRepaymentFrequency() { return this.commonLoanProperties.getRepaymentFrequency(); } @@ -81,6 +87,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setRepaymentFrequency(repaymentFrequency); } + @Override public Boolean isFlexibleRepaymentSchedule() { return this.commonLoanProperties.isFlexibleRepaymentSchedule(); } @@ -90,6 +97,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setFlexibleRepaymentSchedule(flexibleRepaymentSchedule); } + @Override public Boolean isInterestRebateAllowed() { return this.commonLoanProperties.isInterestRebateAllowed(); } @@ -98,6 +106,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setInterestRebateAllowed(interestRebateAllowed); } + @Override public BigDecimal getPrincipal() { return this.commonLoanProperties.getPrincipal(); } @@ -114,6 +123,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { // return this.commonLoanProperties.getInterestRebateAllowed(); // } + @Override public Integer getInterestRateFrequencyMethod() { return commonLoanProperties.getInterestRateFrequencyMethod(); } @@ -122,6 +132,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setInterestRateFrequencyMethod(interestRateFrequencyMethod); } + @Override public Integer getInterestMethod() { return commonLoanProperties.getInterestMethod(); } @@ -130,6 +141,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setInterestMethod(interestMethod); } + @Override public Integer getAmortizationMethod() { return commonLoanProperties.getAmortizationMethod(); } @@ -138,6 +150,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setAmortizationMethod(amortizationMethod); } + @Override public Integer getNumberOfRepayments() { return commonLoanProperties.getNumberOfRepayments(); } @@ -146,6 +159,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setNumberOfRepayments(numberOfRepayments); } + @Override public BigDecimal getInterestRatePerPeriod() { return commonLoanProperties.getInterestRatePerPeriod(); } @@ -154,6 +168,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties.setInterestRatePerPeriod(interestRatePerPeriod); } + @Override public CommonLoanProperties getCommonLoanProperties() { return commonLoanProperties; } @@ -162,6 +177,7 @@ public class UpdateLoanProductCommand implements LoanProductCommandData { this.commonLoanProperties = commonLoanProperties; } + @Override public BigDecimal getInArrearsToleranceAmount() { return this.commonLoanProperties.getInArrearsToleranceAmount(); } diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/oauth/CustomOAuthConsumerContextFilter.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/oauth/CustomOAuthConsumerContextFilter.java index 49b49083d..25bd10b6e 100644 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/oauth/CustomOAuthConsumerContextFilter.java +++ b/mifosng-individual-lending-app/src/main/java/org/mifosng/oauth/CustomOAuthConsumerContextFilter.java @@ -56,6 +56,7 @@ public class CustomOAuthConsumerContextFilter extends OAuthConsumerContextFilter } + @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties index 241cbac9b..b3a2e6a55 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties @@ -40,7 +40,7 @@ error.msg.client.id.invalid=Client does not exist. error.msg.office.id.invalid=Office does not exist. error.msg.office.duplicate.externalId=An office with 'External Id' already exists. error.msg.office.duplicate.name=An office with 'name' already exists. -error.msg.office.unknown.data.integretity.issue=An unknown data integrity issue occured. +error.msg.office.unknown.data.integrity.issue=An unknown data integrity issue occured. dialog.title.confirmation.required=Confirmation required text.confirmation.required=Are you sure? diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties index bd9376af0..37a8b21d0 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties @@ -40,7 +40,7 @@ error.msg.client.id.invalid=Client does not exist.(fr) error.msg.office.id.invalid=Office does not exist.(fr) error.msg.office.duplicate.externalId=An office with 'External Id' already exists.(fr) error.msg.office.duplicate.name=An office with 'name' already exists.(fr) -error.msg.office.unknown.data.integretity.issue=An unknown data integrity issue occured.(fr) +error.msg.office.unknown.data.integrity.issue=An unknown data integrity issue occured.(fr) dialog.title.confirmation.required=Confirmation required(fr) text.confirmation.required=Are you sure?(fr) diff --git a/mifosng-provider/src/main/java/org/mifosng/oauth/CustomProtectedResourceProcessingFilter.java b/mifosng-provider/src/main/java/org/mifosng/oauth/CustomProtectedResourceProcessingFilter.java index 823ef2038..8640d4ee1 100644 --- a/mifosng-provider/src/main/java/org/mifosng/oauth/CustomProtectedResourceProcessingFilter.java +++ b/mifosng-provider/src/main/java/org/mifosng/oauth/CustomProtectedResourceProcessingFilter.java @@ -28,6 +28,7 @@ import org.springframework.security.oauth.provider.token.OAuthProviderToken; public class CustomProtectedResourceProcessingFilter extends ProtectedResourceProcessingFilter { + @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/OfficeValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/OfficeValidator.java deleted file mode 100644 index a42f69bec..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/OfficeValidator.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.mifosng.platform; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang.StringUtils; -import org.joda.time.LocalDate; -import org.mifosng.data.ErrorResponse; -import org.mifosng.platform.exceptions.NewDataValidationException; - -public class OfficeValidator { - - private final String name; - private final Long parentId; - private final LocalDate openingDate; - private final String externalId; - - public OfficeValidator(String name, Long parentId, LocalDate openingDate, String externalId) { - this.name = name; - this.parentId = parentId; - this.openingDate = openingDate; - this.externalId = externalId; - } - - public void validate() { - List dataValidationErrors = new ArrayList(); - - if (StringUtils.isBlank(this.name)) { - ErrorResponse error = new ErrorResponse("validation.msg.office.name.cannot.be.blank", "name"); - dataValidationErrors.add(error); - } - - if (this.parentId == null || parentId.equals(Long.valueOf(-1))) { - ErrorResponse error = new ErrorResponse("validation.msg.office.parent.cannot.be.blank", "parentId"); - dataValidationErrors.add(error); - } - - if (this.openingDate == null) { - ErrorResponse error = new ErrorResponse("validation.msg.office.opening.date.cannot.be.blank", "openingDate"); - dataValidationErrors.add(error); - } - - if (StringUtils.isNotBlank(this.externalId)) { - - if (this.externalId.trim().length() > 100) { - ErrorResponse error = new ErrorResponse("validation.msg.office.externalId.exceeds.max.length", "externalId"); - dataValidationErrors.add(error); - } - } - - if (!dataValidationErrors.isEmpty()) { - throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist."); - } - } - -} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java index 2b8a94374..854ed4fa8 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java @@ -33,7 +33,6 @@ import org.mifosng.data.ClientDataWithAccountsData; import org.mifosng.data.CurrencyData; import org.mifosng.data.DerivedLoanData; import org.mifosng.data.EnumOptionReadModel; -import org.mifosng.data.ErrorResponse; import org.mifosng.data.ExtraDatasets; import org.mifosng.data.LoanAccountData; import org.mifosng.data.LoanProductData; @@ -53,7 +52,7 @@ import org.mifosng.platform.client.domain.ClientRepository; import org.mifosng.platform.currency.domain.ApplicationCurrency; import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository; import org.mifosng.platform.currency.domain.Money; -import org.mifosng.platform.exceptions.ApplicationDomainRuleException; +import org.mifosng.platform.exceptions.PlatformResourceNotFoundException; import org.mifosng.platform.exceptions.UnAuthenticatedUserException; import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.Loan; @@ -200,11 +199,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService { new Object[] { currentUser.getOrganisation().getId(), clientId, noteId }); } catch (EmptyResultDataAccessException e) { - ErrorResponse errorResponse = new ErrorResponse( - "error.msg.client.id.invalid", "id"); - throw new ApplicationDomainRuleException( - Arrays.asList(errorResponse), "Client with identifier " - + clientId.toString() + " does not exist."); + throw new PlatformResourceNotFoundException("error.msg.client.id.invalid", "Client with identifier {0} does not exist", clientId); } } @@ -403,11 +398,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService { return selectedOffice; } catch (EmptyResultDataAccessException e) { - ErrorResponse errorResponse = new ErrorResponse( - "error.msg.office.id.invalid", "id"); - throw new ApplicationDomainRuleException( - Arrays.asList(errorResponse), "Office with identifier " - + officeId.toString() + " does not exist."); + throw new PlatformResourceNotFoundException("error.msg.office.id.invalid", "Office with identifier {0} does not exist", officeId); } } @@ -426,11 +417,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService { return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { clientId, currentUser.getOrganisation().getId() }); } catch (EmptyResultDataAccessException e) { - ErrorResponse errorResponse = new ErrorResponse( - "error.msg.client.id.invalid", "id"); - throw new ApplicationDomainRuleException( - Arrays.asList(errorResponse), "Client with identifier " - + clientId.toString() + " does not exist."); + throw new PlatformResourceNotFoundException("error.msg.client.id.invalid", "Client with identifier {0} does not exist", clientId); } } @@ -575,6 +562,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService { return loanRepaymentData; } + @Override public NewLoanWorkflowStepOneData retrieveClientAndProductDetails(final Long clientId, final Long productId) { AppUser currentUser = extractAuthenticatedUser(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java index be114e646..c6597c902 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java @@ -1,6 +1,10 @@ package org.mifosng.platform; -import static org.mifosng.platform.Specifications.*; +import static org.mifosng.platform.Specifications.loansThatMatch; +import static org.mifosng.platform.Specifications.officesThatMatch; +import static org.mifosng.platform.Specifications.productThatMatches; +import static org.mifosng.platform.Specifications.rolesThatMatch; +import static org.mifosng.platform.Specifications.usersThatMatch; import java.math.BigDecimal; import java.util.ArrayList; @@ -44,10 +48,11 @@ import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository; import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.exceptions.ApplicationDomainRuleException; -import org.mifosng.platform.exceptions.UnAuthenticatedUserException; import org.mifosng.platform.exceptions.InvalidSignupException; import org.mifosng.platform.exceptions.NewDataValidationException; import org.mifosng.platform.exceptions.NoAuthorizationException; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; +import org.mifosng.platform.exceptions.UnAuthenticatedUserException; import org.mifosng.platform.infrastructure.BasicPasswordEncodablePlatformUser; import org.mifosng.platform.infrastructure.PlatformPasswordEncoder; import org.mifosng.platform.infrastructure.PlatformUser; @@ -74,6 +79,7 @@ import org.mifosng.platform.organisation.domain.OfficeRepository; import org.mifosng.platform.organisation.domain.Organisation; import org.mifosng.platform.organisation.domain.OrganisationCurrency; import org.mifosng.platform.organisation.domain.OrganisationRepository; +import org.mifosng.platform.organisation.service.OfficeCommandValidator; import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUserRepository; import org.mifosng.platform.user.domain.Permission; @@ -331,7 +337,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements try { AppUser currentUser = extractAuthenticatedUser(); - OfficeValidator validator = new OfficeValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); + OfficeCommandValidator validator = new OfficeCommandValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); validator.validate(); Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getParentId()); @@ -347,8 +353,8 @@ public class WritePlatformServiceJpaRepositoryImpl implements return office.getId(); } catch (DataIntegrityViolationException dve) { - List dataValidationErrors = handleOfficeDataIntegrityIssues(command, dve); - throw new ApplicationDomainRuleException(dataValidationErrors, "Errors exist."); + handleOfficeDataIntegrityIssues(command, dve); + return Long.valueOf(-1); } } @@ -359,7 +365,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements try { AppUser currentUser = extractAuthenticatedUser(); - OfficeValidator validator = new OfficeValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); + OfficeCommandValidator validator = new OfficeCommandValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); validator.validate(); Office office = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getId()); @@ -370,27 +376,25 @@ public class WritePlatformServiceJpaRepositoryImpl implements return office.getId(); } catch (DataIntegrityViolationException dve) { - List dataValidationErrors = handleOfficeDataIntegrityIssues(command, dve); - throw new ApplicationDomainRuleException(dataValidationErrors, "Errors exist."); + handleOfficeDataIntegrityIssues(command, dve); + return Long.valueOf(-1); } } - private List handleOfficeDataIntegrityIssues(final OfficeCommand command, DataIntegrityViolationException dve) { + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleOfficeDataIntegrityIssues(final OfficeCommand command, DataIntegrityViolationException dve) { - List dataValidationErrors = new ArrayList(); Throwable realCause = dve.getMostSpecificCause(); if (realCause.getMessage().contains("externalid_org")) { - ErrorResponse error = new ErrorResponse("error.msg.office.duplicate.externalId", "externalId", command.getExternalId()); - dataValidationErrors.add(error); + throw new PlatformDataIntegrityException("error.msg.office.duplicate.externalId", "Office with externalId {0} already exists", "externalId", command.getExternalId()); } else if (realCause.getMessage().contains("name_org")) { - ErrorResponse error = new ErrorResponse("error.msg.office.duplicate.name", "name", command.getName()); - dataValidationErrors.add(error); - } else { - logger.error(dve.getMessage(), dve); - ErrorResponse error = new ErrorResponse("error.msg.office.unknown.data.integretity.issue", "id"); - dataValidationErrors.add(error); - } - return dataValidationErrors; + throw new PlatformDataIntegrityException("error.msg.office.duplicate.name", "Office with name {0} already exists", "name", command.getName()); + } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.office.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } /* diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/ExtraDataApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/ExtraDataApiResource.java index eb8fab5ec..568114341 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/ExtraDataApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/ExtraDataApiResource.java @@ -1,31 +1,16 @@ package org.mifosng.platform.api; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import javax.ws.rs.Consumes; import javax.ws.rs.GET; -import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.mifosng.data.EntityIdentifier; -import org.mifosng.data.ErrorResponse; -import org.mifosng.data.ErrorResponseList; import org.mifosng.data.ExtraDatasets; -import org.mifosng.data.reports.GenericResultset; -import org.mifosng.platform.InvalidSqlException; import org.mifosng.platform.ReadPlatformService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,8 +22,7 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class ExtraDataApiResource { - private final static Logger logger = LoggerFactory - .getLogger(ExtraDataApiResource.class); + private final static Logger logger = LoggerFactory.getLogger(ExtraDataApiResource.class); @Autowired private ReadPlatformService readPlatformService; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java index 64735aed7..efb3689bf 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java @@ -1,6 +1,5 @@ package org.mifosng.platform.api; -import java.util.Arrays; import java.util.Collection; import javax.ws.rs.Consumes; @@ -16,7 +15,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.mifosng.data.EntityIdentifier; -import org.mifosng.data.ErrorResponse; import org.mifosng.data.ErrorResponseList; import org.mifosng.data.LoanProductData; import org.mifosng.data.LoanProductList; @@ -30,7 +28,6 @@ import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; -import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; @@ -38,7 +35,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; /* - * * @see http://viewfromthefringe.blogspot.com/2010/06/versioning-rest-services.html * @see http://jersey.576304.n2.nabble.com/Restful-Service-Versioning-td6332833.html * @@ -57,7 +53,6 @@ import org.springframework.stereotype.Component; * 8. @PUT /loanproducts/{productId} updates loan product of that identifier * 9. Using /resource/template pattern for UI specific function of get 'data with defaults' for new resource. */ - @Path("/v1/loanproducts") @Component @Scope("singleton") @@ -113,10 +108,6 @@ public class LoanProductApiResource { EntityIdentifier entityIdentifier = this.writePlatformService.createLoanProduct(command); return Response.ok().entity(entityIdentifier).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); } catch (ApplicationDomainRuleException e) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); } catch (NewDataValidationException e) { @@ -135,10 +126,6 @@ public class LoanProductApiResource { Collection products = this.readPlatformService.retrieveAllLoanProducts(); return Response.ok().entity(new LoanProductList(products)).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); } catch (ApplicationDomainRuleException e) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); } catch (NewDataValidationException e) { @@ -161,10 +148,6 @@ public class LoanProductApiResource { LoanProductData loanProduct = this.readPlatformService.retrieveNewLoanProductDetails(); return Response.ok().entity(loanProduct).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); } catch (ApplicationDomainRuleException e) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); } catch (NewDataValidationException e) { @@ -184,10 +167,6 @@ public class LoanProductApiResource { LoanProductData loanProduct = this.readPlatformService.retrieveLoanProduct(productId); return Response.ok().entity(loanProduct).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); } catch (ApplicationDomainRuleException e) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); } catch (NewDataValidationException e) { @@ -230,10 +209,6 @@ public class LoanProductApiResource { EntityIdentifier entityIdentifier = this.writePlatformService.updateLoanProduct(command); return Response.ok().entity(entityIdentifier).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); } catch (ApplicationDomainRuleException e) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); } catch (NewDataValidationException e) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java deleted file mode 100644 index 0c0c6985e..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.mifosng.platform.api; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.mifosng.data.EntityIdentifier; -import org.mifosng.data.ErrorResponse; -import org.mifosng.data.ErrorResponseList; -import org.mifosng.data.OfficeData; -import org.mifosng.data.OfficeList; -import org.mifosng.data.command.OfficeCommand; -import org.mifosng.platform.ReadPlatformService; -import org.mifosng.platform.WritePlatformService; -import org.mifosng.platform.exceptions.ApplicationDomainRuleException; -import org.mifosng.platform.exceptions.NewDataValidationException; -import org.mifosng.platform.user.domain.AppUser; -import org.mifosng.platform.user.domain.AppUserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Scope; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; - -@Path("/v1/offices") -@Component -@Scope("singleton") -public class OfficeApiResource { - - @Autowired - private ReadPlatformService readPlatformService; - - @Autowired - private WritePlatformService writePlatformService; - - @Autowired - private AppUserRepository appUserRepository; - - private void hardcodeUserIntoSecurityContext() { - AppUser currentUser = this.appUserRepository.findOne(Long.valueOf(1)); - - Authentication auth = new UsernamePasswordAuthenticationToken(currentUser, currentUser, currentUser.getAuthorities()); - SecurityContext context = SecurityContextHolder.getContext(); - context.setAuthentication(auth); - } - - @GET - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({MediaType.APPLICATION_JSON}) - public Response retrieveOffices() { - - try { - hardcodeUserIntoSecurityContext(); - - Collection offices = this.readPlatformService.retrieveAllOffices(); - - return Response.ok().entity(new OfficeList(offices)).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } - - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON}) - public Response createOffice(final OfficeCommand command) { - - try { - hardcodeUserIntoSecurityContext(); - - Long officeId = this.writePlatformService.createOffice(command); - - return Response.ok().entity(new EntityIdentifier(officeId)).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } - - @GET - @Path("{officeId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON}) - public Response retreiveOffice(@PathParam("officeId") final Long officeId) { - - try { - hardcodeUserIntoSecurityContext(); - - OfficeData office = this.readPlatformService.retrieveOffice(officeId); - - return Response.ok().entity(office).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } - - @PUT - @Path("{officeId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON}) - public Response updateOffice(@PathParam("officeId") final Long officeId, final OfficeCommand command) { - - try { - hardcodeUserIntoSecurityContext(); - - command.setId(officeId); - Long entityId = this.writePlatformService.updateOffice(command); - - return Response.ok().entity(new EntityIdentifier(entityId)).build(); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/AccessDeniedExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/AccessDeniedExceptionMapper.java new file mode 100644 index 000000000..2e823f383 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/AccessDeniedExceptionMapper.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link AccessDeniedException} thrown by platform into a HTTP API friendly format. + * + * The {@link AccessDeniedException} is thrown by spring security on platform when an attempt is made to use functionality + * for which the user does have sufficient privileges. + */ +@Provider +@Component +@Scope("singleton") +public class AccessDeniedExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(AccessDeniedException exception) { + // Status code 403 really reads as: "Authenticated - but not authorized": + return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized()).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/NoAuthorizationExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/NoAuthorizationExceptionMapper.java new file mode 100644 index 000000000..9081bdcc5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/NoAuthorizationExceptionMapper.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.mifosng.platform.exceptions.NoAuthorizationException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link NoAuthorizationException} thrown by platform into a HTTP API friendly format. + * + * The {@link NoAuthorizationException} is thrown on platform when an attempt is made to use functionality + * for which the user does have sufficient privileges. + */ +@Provider +@Component +@Scope("singleton") +public class NoAuthorizationExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(NoAuthorizationException exception) { + // Status code 403 really reads as: "Authenticated - but not authorized": + return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized()).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformApiDataValidationExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformApiDataValidationExceptionMapper.java new file mode 100644 index 000000000..8fdd13777 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformApiDataValidationExceptionMapper.java @@ -0,0 +1,31 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link PlatformApiDataValidationException} thrown by platform into a HTTP API friendly format. + * + * The {@link PlatformApiDataValidationException} is typically thrown in data validation of the parameters passed in with an api request. + */ +@Provider +@Component +@Scope("singleton") +public class PlatformApiDataValidationExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(PlatformApiDataValidationException exception) { + + ApiGlobalErrorResponse dataIntegrityError = ApiGlobalErrorResponse.badClientRequest(exception.getGlobalisationMessageCode(), + exception.getDefaultUserMessage(), exception.getErrors()); + + return Response.status(Status.BAD_REQUEST).entity(dataIntegrityError).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformDataIntegrityExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformDataIntegrityExceptionMapper.java new file mode 100644 index 000000000..12edc343a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformDataIntegrityExceptionMapper.java @@ -0,0 +1,31 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link PlatformDataIntegrityException} thrown by platform into a HTTP API friendly format. + * + * The {@link PlatformDataIntegrityException} is thrown when modifying api call result in data integrity checks to be fired. + */ +@Provider +@Component +@Scope("singleton") +public class PlatformDataIntegrityExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(PlatformDataIntegrityException exception) { + + ApiGlobalErrorResponse dataIntegrityError = ApiGlobalErrorResponse.dataIntegrityError(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage(), + exception.getParameterName(), exception.getDefaultUserMessageArgs()); + + return Response.status(Status.BAD_REQUEST).entity(dataIntegrityError).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformResourceNotFoundExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformResourceNotFoundExceptionMapper.java new file mode 100644 index 000000000..fd24765ad --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformResourceNotFoundExceptionMapper.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.mifosng.platform.exceptions.PlatformResourceNotFoundException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link PlatformResourceNotFoundException} thrown by platform into a HTTP API friendly format. + * + * The {@link PlatformResourceNotFoundException} is thrown when an api call for a resource that is expected to exist does not. + */ +@Provider +@Component +@Scope("singleton") +public class PlatformResourceNotFoundExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(PlatformResourceNotFoundException exception) { + + ApiGlobalErrorResponse notFoundErrorResponse = ApiGlobalErrorResponse.notFound(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage(), exception.getDefaultUserMessageArgs()); + return Response.status(Status.NOT_FOUND).entity(notFoundErrorResponse).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnAuthenticatedUserExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnAuthenticatedUserExceptionMapper.java index 2ae2a2645..8d04556ef 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnAuthenticatedUserExceptionMapper.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnAuthenticatedUserExceptionMapper.java @@ -1,14 +1,11 @@ package org.mifosng.platform.api.errorhandling; -import java.util.Arrays; - import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; -import org.mifosng.data.ErrorResponse; -import org.mifosng.data.ErrorResponseList; +import org.mifosng.data.ApiGlobalErrorResponse; import org.mifosng.platform.exceptions.UnAuthenticatedUserException; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -23,9 +20,7 @@ public class UnAuthenticatedUserExceptionMapper implements ExceptionMapper offices = this.readPlatformService.retrieveAllOffices(); + + return Response.ok().entity(new OfficeList(offices)).build(); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON}) + public Response createOffice(final OfficeCommand command) { + hardcodeUserIntoSecurityContext(); + + Long officeId = this.writePlatformService.createOffice(command); + + return Response.ok().entity(new EntityIdentifier(officeId)).build(); + } + + @GET + @Path("{officeId}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON}) + public Response retreiveOffice(@PathParam("officeId") final Long officeId) { + hardcodeUserIntoSecurityContext(); + + OfficeData office = this.readPlatformService.retrieveOffice(officeId); + + return Response.ok().entity(office).build(); + } + + @PUT + @Path("{officeId}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON}) + public Response updateOffice(@PathParam("officeId") final Long officeId, final OfficeCommand command) { + hardcodeUserIntoSecurityContext(); + + command.setId(officeId); + Long entityId = this.writePlatformService.updateOffice(command); + + return Response.ok().entity(new EntityIdentifier(entityId)).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformApiDataValidationException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformApiDataValidationException.java new file mode 100644 index 000000000..a4edcb675 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformApiDataValidationException.java @@ -0,0 +1,30 @@ +package org.mifosng.platform.exceptions; + +import java.util.List; + +import org.mifosng.data.ApiParameterError; + +public class PlatformApiDataValidationException extends RuntimeException { + + private final String globalisationMessageCode; + private final String defaultUserMessage; + private final List errors; + + public PlatformApiDataValidationException(final String globalisationMessageCode, final String defaultUserMessage, List errors) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.errors = errors; + } + + public String getGlobalisationMessageCode() { + return globalisationMessageCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public List getErrors() { + return errors; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDataIntegrityException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDataIntegrityException.java new file mode 100644 index 000000000..17f6fd954 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDataIntegrityException.java @@ -0,0 +1,42 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when data integrity problems happen due to state modifying actions. + */ +public class PlatformDataIntegrityException extends RuntimeException { + + private final String globalisationMessageCode; + private final String defaultUserMessage; + private final String parameterName; + private final Object[] defaultUserMessageArgs; + + public PlatformDataIntegrityException(final String globalisationMessageCode, final String defaultUserMessage, final Object... defaultUserMessageArgs) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.parameterName = ""; + this.defaultUserMessageArgs = defaultUserMessageArgs; + } + + public PlatformDataIntegrityException(final String globalisationMessageCode, final String defaultUserMessage, final String parameterName, final Object... defaultUserMessageArgs) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.parameterName = parameterName; + this.defaultUserMessageArgs = defaultUserMessageArgs; + } + + public String getGlobalisationMessageCode() { + return globalisationMessageCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public Object[] getDefaultUserMessageArgs() { + return defaultUserMessageArgs; + } + + public String getParameterName() { + return parameterName; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformResourceNotFoundException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformResourceNotFoundException.java new file mode 100644 index 000000000..3b55eb9ab --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformResourceNotFoundException.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when resources that are queried for are not found. + */ +public class PlatformResourceNotFoundException extends RuntimeException { + + private final String globalisationMessageCode; + private final String defaultUserMessage; + private final Object[] defaultUserMessageArgs; + + public PlatformResourceNotFoundException(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.defaultUserMessageArgs = defaultUserMessageArgs; + } + + public String getGlobalisationMessageCode() { + return globalisationMessageCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public Object[] getDefaultUserMessageArgs() { + return defaultUserMessageArgs; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/CustomAuthenticationFailureHandler.java b/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/CustomAuthenticationFailureHandler.java index 9c3092d49..75bff314a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/CustomAuthenticationFailureHandler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/CustomAuthenticationFailureHandler.java @@ -35,7 +35,8 @@ public class CustomAuthenticationFailureHandler implements AuthenticationFailure * If redirecting or forwarding, {@code saveException} will be called to cache the exception for use in * the target view. */ - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { if (defaultFailureUrl == null) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeCommandValidator.java new file mode 100644 index 000000000..f905828e2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeCommandValidator.java @@ -0,0 +1,55 @@ +package org.mifosng.platform.organisation.service; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosng.data.ApiParameterError; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; + +public class OfficeCommandValidator { + + private final String name; + private final Long parentId; + private final LocalDate openingDate; + private final String externalId; + + public OfficeCommandValidator(String name, Long parentId, LocalDate openingDate, String externalId) { + this.name = name; + this.parentId = parentId; + this.openingDate = openingDate; + this.externalId = externalId; + } + + public void validate() { + + List dataValidationErrors = new ArrayList(); + + if (StringUtils.isBlank(this.name)) { + ApiParameterError error = ApiParameterError.parameterError("validation.msg.office.name.cannot.be.blank", "The parameter name cannot be blank.", "name"); + dataValidationErrors.add(error); + } + + if (this.parentId == null || parentId.intValue() <= 0) { + ApiParameterError error = ApiParameterError.parameterError("validation.msg.office.parent.cannot.be.blank", "A valid parentId must be provided.", "parentId"); + dataValidationErrors.add(error); + } + + if (this.openingDate == null) { + ApiParameterError error = ApiParameterError.parameterError("validation.msg.office.opening.date.cannot.be.blank", "The parameter openingDate cannot be blank", "openingDate"); + dataValidationErrors.add(error); + } + + if (StringUtils.isNotBlank(this.externalId)) { + if (this.externalId.trim().length() > 100) { + ApiParameterError error = ApiParameterError.parameterError("validation.msg.office.externalId.exceeds.max.length", "The parameter externalId exceeds max length of {0}", "externalId", 100); + dataValidationErrors.add(error); + } + } + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..20c940952 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,117 @@ +package org.mifosng.platform.organisation.service; + +import static org.mifosng.platform.Specifications.officesThatMatch; + +import org.mifosng.data.command.OfficeCommand; +import org.mifosng.platform.exceptions.NoAuthorizationException; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; +import org.mifosng.platform.organisation.domain.Office; +import org.mifosng.platform.organisation.domain.OfficeRepository; +import org.mifosng.platform.user.domain.AppUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class OfficePlatformServiceJpaRepositoryImpl implements OfficeWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(OfficePlatformServiceJpaRepositoryImpl.class); + + private final PlatformSecurityContext context; + private final OfficeRepository officeRepository; + + @Autowired + public OfficePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final OfficeRepository officeRepository) { + this.context = context; + this.officeRepository = officeRepository; + } + + @Transactional + @Override + public Long createOffice(final OfficeCommand command) { + + try { + AppUser currentUser = context.authenticatedUser(); + + OfficeCommandValidator validator = new OfficeCommandValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); + validator.validate(); + + Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getParentId()); + + Office office = Office.createNew(currentUser.getOrganisation(), parent, command.getName(), command.getOpeningDate(), command.getExternalId()); + + // pre save to generate id for use in office hierarchy + this.officeRepository.save(office); + + office.generateHierarchy(); + + this.officeRepository.save(office); + + return office.getId(); + } catch (DataIntegrityViolationException dve) { + handleOfficeDataIntegrityIssues(command, dve); + return Long.valueOf(-1); + } + } + + @Transactional + @Override + public Long updateOffice(final OfficeCommand command) { + + try { + AppUser currentUser = context.authenticatedUser(); + + OfficeCommandValidator validator = new OfficeCommandValidator(command.getName(), command.getParentId(), command.getOpeningDate(), command.getExternalId()); + validator.validate(); + + Office office = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, command.getId()); + + office.update(command.getName(), command.getExternalId(), command.getOpeningDate()); + + this.officeRepository.save(office); + + return office.getId(); + } catch (DataIntegrityViolationException dve) { + handleOfficeDataIntegrityIssues(command, dve); + return Long.valueOf(-1); + } + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleOfficeDataIntegrityIssues(final OfficeCommand command, DataIntegrityViolationException dve) { + + Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("externalid_org")) { + throw new PlatformDataIntegrityException("error.msg.office.duplicate.externalId", "Office with externalId {0} already exists", "externalId", command.getExternalId()); + } else if (realCause.getMessage().contains("name_org")) { + throw new PlatformDataIntegrityException("error.msg.office.duplicate.name", "Office with name {0} already exists", "name", command.getName()); + } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.office.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); + } + + /* + * used to restrict modifying operations to office that are either the users office or lower (child) in the office hierarchy + */ + private Office validateUserPriviledgeOnOfficeAndRetrieve(AppUser currentUser, Long officeId) { + + Office userOffice = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), currentUser.getOffice().getId())); + + if (userOffice.doesNotHaveAnOfficeInHierarchyWithId(officeId)) { + throw new NoAuthorizationException("User does not have sufficient priviledges to act on the provided office."); + } + + Office officeToReturn = userOffice; + if (!userOffice.identifiedBy(officeId)) { + officeToReturn = this.officeRepository.findOne(officesThatMatch(currentUser.getOrganisation(), officeId)); + } + + return officeToReturn; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformService.java new file mode 100644 index 000000000..32d5d556a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformService.java @@ -0,0 +1,12 @@ +package org.mifosng.platform.organisation.service; + +import java.util.Collection; + +import org.mifosng.data.OfficeData; + +public interface OfficeReadPlatformService { + + Collection retrieveAllOffices(); + + OfficeData retrieveOffice(Long officeId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformServiceImpl.java new file mode 100644 index 000000000..dc09391a9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeReadPlatformServiceImpl.java @@ -0,0 +1,105 @@ +package org.mifosng.platform.organisation.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.sql.DataSource; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosng.data.OfficeData; +import org.mifosng.platform.exceptions.PlatformResourceNotFoundException; +import org.mifosng.platform.user.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.stereotype.Service; + +@Service +public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService { + + private final SimpleJdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + + @Autowired + public OfficeReadPlatformServiceImpl(final PlatformSecurityContext context, final DataSource dataSource) { + this.context = context; + this.jdbcTemplate = new SimpleJdbcTemplate(dataSource); + } + + private static final class OfficeMapper implements RowMapper { + + public String officeSchema() { + return " o.id as id, o.name as name, o.external_id as externalId, o.opening_date as openingDate, o.hierarchy as hierarchy, parent.id as parentId, parent.name as parentName " + + "from org_office o LEFT JOIN org_office AS parent ON parent.id = o.parent_id "; + } + + @Override + public OfficeData mapRow(final ResultSet rs, final int rowNum) + throws SQLException { + + Long id = rs.getLong("id"); + String name = rs.getString("name"); + String externalId = rs.getString("externalId"); + LocalDate openingDate = new LocalDate(rs.getDate("openingDate")); + String hierarchy = rs.getString("hierarchy"); + Long parentId = rs.getLong("parentId"); + String parentName = rs.getString("parentName"); + + return new OfficeData(id, name, externalId, openingDate, hierarchy, parentId, parentName); + } + } + + @Override + public Collection retrieveAllOffices() { + + AppUser currentUser = context.authenticatedUser(); + + String hierarchy = currentUser.getOffice().getHierarchy(); + String hierarchySearchString = hierarchy + "%"; + + OfficeMapper rm = new OfficeMapper(); + String sql = "select " + rm.officeSchema() + "where o.org_id = ? and o.hierarchy like ? order by o.hierarchy"; + + return this.jdbcTemplate.query(sql, rm, new Object[] {currentUser.getOrganisation().getId(), hierarchySearchString}); + } + + @Override + public OfficeData retrieveOffice(final Long officeId) { + + try { + AppUser currentUser = context.authenticatedUser(); + + OfficeMapper rm = new OfficeMapper(); + String sql = "select " + rm.officeSchema() + + " where o.org_id = ? and o.id = ?"; + + OfficeData selectedOffice = this.jdbcTemplate.queryForObject(sql, + rm, new Object[] { currentUser.getOrganisation().getId(), + officeId }); + + List allowedParents = new ArrayList(); + + if (StringUtils.isNotBlank(selectedOffice.getParentName())) { + Collection allOffices = retrieveAllOffices(); + + for (OfficeData office : allOffices) { + + if (!office.getId().equals(selectedOffice.getId())) { + allowedParents.add(office); + } + } + } + + selectedOffice.setAllowedParents(allowedParents); + + return selectedOffice; + } catch (EmptyResultDataAccessException e) { + throw new PlatformResourceNotFoundException("error.msg.office.id.invalid", "Office with identifier {0} does not exist", officeId); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java new file mode 100644 index 000000000..8c41deb9b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/OfficeWritePlatformService.java @@ -0,0 +1,10 @@ +package org.mifosng.platform.organisation.service; + +import org.mifosng.data.command.OfficeCommand; + +public interface OfficeWritePlatformService { + + Long createOffice(OfficeCommand command); + + Long updateOffice(OfficeCommand command); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContext.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContext.java new file mode 100644 index 000000000..e70d2f620 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContext.java @@ -0,0 +1,9 @@ +package org.mifosng.platform.organisation.service; + +import org.mifosng.platform.user.domain.AppUser; + +public interface PlatformSecurityContext { + + AppUser authenticatedUser(); + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContextThroughSpringSecurity.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContextThroughSpringSecurity.java new file mode 100644 index 000000000..7ea5d4f1c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/service/PlatformSecurityContextThroughSpringSecurity.java @@ -0,0 +1,33 @@ +package org.mifosng.platform.organisation.service; + +import org.mifosng.platform.exceptions.UnAuthenticatedUserException; +import org.mifosng.platform.user.domain.AppUser; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +/** + * Responsible for returning the current authenticated user that is currently set in the {@link SecurityContext}. + */ +@Service +public class PlatformSecurityContextThroughSpringSecurity implements PlatformSecurityContext { + + @Override + public AppUser authenticatedUser() { + AppUser currentUser = null; + SecurityContext context = SecurityContextHolder.getContext(); + if (context != null) { + Authentication auth = context.getAuthentication(); + if (auth != null) { + currentUser = (AppUser) auth.getPrincipal(); + } + } + + if (currentUser == null) { + throw new UnAuthenticatedUserException(); + } + + return currentUser; + } +}