add source for individual lending app.

This commit is contained in:
Keith Woodlock
2012-04-20 11:49:03 +01:00
parent 54189a8d6c
commit ceb30e8012
833 changed files with 186628 additions and 0 deletions
@@ -0,0 +1,9 @@
package org.mifosng.configuration;
public interface ApplicationConfigurationService {
OAuthProviderDetails retrieveOAuthProviderDetails();
void update(OAuthProviderDetails newDetails);
}
@@ -0,0 +1,51 @@
package org.mifosng.configuration;
import org.springframework.stereotype.Service;
/**
* When application goes down or rebooted, configuration values return to defaults provided here.
*/
@Service
public class InMemoryApplicationConfigurationService implements
ApplicationConfigurationService {
private String oauthProviderUrl = "http://localhost:8085/mifosng-provider/";
private String requestTokenURL = "oauth/request_token";
private String userAuthorizationURL = "oauth/confirm_access";
private String accessTokenURL = "oauth/access_token";
private String individualLendingResourceConsumerkey = "mifosng-ui-consumer-key";
private String individualLendingConsumerSharedSecret = "testmifosng";
public InMemoryApplicationConfigurationService() {
//
}
@Override
public OAuthProviderDetails retrieveOAuthProviderDetails() {
String requestTokenFullUrl = this.oauthProviderUrl
.concat(this.requestTokenURL);
String userAuthorizationFullURL = this.oauthProviderUrl
.concat(this.userAuthorizationURL);
String accessTokenFullURL = this.oauthProviderUrl
.concat(this.accessTokenURL);
return new OAuthProviderDetails(this.oauthProviderUrl, requestTokenFullUrl,
userAuthorizationFullURL, accessTokenFullURL,
this.individualLendingResourceConsumerkey,
this.individualLendingConsumerSharedSecret);
}
@Override
public void update(OAuthProviderDetails newDetails) {
this.oauthProviderUrl = newDetails.getProviderBaseUrl();
if (!this.oauthProviderUrl.endsWith("/")) {
this.oauthProviderUrl = this.oauthProviderUrl.concat("/");
}
this.individualLendingResourceConsumerkey = newDetails.getConsumerkey();
this.individualLendingConsumerSharedSecret = newDetails.getSharedSecret();
}
}
@@ -0,0 +1,56 @@
package org.mifosng.configuration;
public class OAuthProviderDetails {
private final String requestTokenUrl;
private final String userAuthorizationUrl;
private final String accessTokenUrl;
private final String sharedSecret;
private final String consumerkey;
private final String providerBaseUrl;
public OAuthProviderDetails(String providerBaseUrl, String requestTokenUrl,
String userAuthorizationUrl, String accessTokenUrl,
String consumerkey, String sharedSecret) {
this.providerBaseUrl = providerBaseUrl;
this.requestTokenUrl = requestTokenUrl;
this.userAuthorizationUrl = userAuthorizationUrl;
this.accessTokenUrl = accessTokenUrl;
this.consumerkey = consumerkey;
this.sharedSecret = sharedSecret;
}
public OAuthProviderDetails(String oauthProviderUrl, String consumerkey,
String sharedSecret) {
this.providerBaseUrl = oauthProviderUrl;
this.consumerkey = consumerkey;
this.sharedSecret = sharedSecret;
this.accessTokenUrl = "";
this.userAuthorizationUrl = "";
this.requestTokenUrl = "";
}
public String getRequestTokenUrl() {
return requestTokenUrl;
}
public String getUserAuthorizationUrl() {
return userAuthorizationUrl;
}
public String getAccessTokenUrl() {
return accessTokenUrl;
}
public String getSharedSecret() {
return sharedSecret;
}
public String getConsumerkey() {
return consumerkey;
}
public String getProviderBaseUrl() {
return providerBaseUrl;
}
}
@@ -0,0 +1,93 @@
package org.mifosng.oauth;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class ConsumerUserDetails implements UserDetails {
private final String username;
private final Collection<GrantedAuthority> authorities;
public ConsumerUserDetails(String username, Collection<GrantedAuthority> authorities) {
this.username = username;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return "";
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public boolean hasNoReportingAuthority() {
return !hasReportingAuthority();
}
private boolean hasReportingAuthority() {
SimpleGrantedAuthority reportingAuthority = new SimpleGrantedAuthority("REPORTING_SUPER_USER_ROLE");
return this.authorities.contains(reportingAuthority);
}
public boolean hasNoAuthorityToSumitLoanApplication() {
return !hasAuthorityToSumitLoanApplication();
}
private boolean hasAuthorityToSumitLoanApplication() {
return containsAnyOf(portfolioAllAuthority(), sumbitLoanApplicationAuthority(), sumbitHistoricLoanApplicationAuthority());
}
private boolean containsAnyOf(SimpleGrantedAuthority... authorities) {
boolean match = false;
for (SimpleGrantedAuthority authority : authorities) {
match = this.authorities.contains(authority);
if (match) {
break;
}
}
return match;
}
private SimpleGrantedAuthority portfolioAllAuthority() {
return new SimpleGrantedAuthority("PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE");
}
private SimpleGrantedAuthority sumbitLoanApplicationAuthority() {
return new SimpleGrantedAuthority("CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE");
}
private SimpleGrantedAuthority sumbitHistoricLoanApplicationAuthority() {
return new SimpleGrantedAuthority("CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE");
}
}
@@ -0,0 +1,270 @@
package org.mifosng.oauth;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.data.AuthenticatedUserData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth.common.OAuthProviderParameter;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthRequestFailedException;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.security.oauth.consumer.OAuthSecurityContextImpl;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
import org.springframework.security.oauth.consumer.client.CoreOAuthConsumerSupport;
import org.springframework.security.oauth.consumer.filter.OAuthConsumerContextFilter;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.web.client.HttpStatusCodeException;
public class CustomOAuthConsumerContextFilter extends OAuthConsumerContextFilter {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public CustomOAuthConsumerContextFilter(final ApplicationConfigurationService applicationConfigurationService) {
this.applicationConfigurationService = applicationConfigurationService;
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
OAuthSecurityContextImpl context = new OAuthSecurityContextImpl();
context.setDetails(request);
Map<String, OAuthConsumerToken> rememberedTokens = getRememberMeServices().loadRememberedTokens(request, response);
Map<String, OAuthConsumerToken> accessTokens = new TreeMap<String, OAuthConsumerToken>();
Map<String, OAuthConsumerToken> requestTokens = new TreeMap<String, OAuthConsumerToken>();
if (rememberedTokens != null) {
for (Map.Entry<String, OAuthConsumerToken> tokenEntry : rememberedTokens.entrySet()) {
OAuthConsumerToken token = tokenEntry.getValue();
if (token != null) {
if (token.isAccessToken()) {
accessTokens.put(tokenEntry.getKey(), token);
}
else {
requestTokens.put(tokenEntry.getKey(), token);
}
}
}
}
context.setAccessTokens(accessTokens);
OAuthSecurityContextHolder.setContext(context);
// if (LOG.isDebugEnabled()) {
// LOG.debug("Storing access tokens in request attribute '" + getAccessTokensRequestAttribute() + "'.");
// }
try {
try {
request.setAttribute(getAccessTokensRequestAttribute(), new ArrayList<OAuthConsumerToken>(accessTokens.values()));
chain.doFilter(request, response);
}
catch (Exception e) {
try {
ProtectedResourceDetails resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e);
String neededResourceId = resourceThatNeedsAuthorization.getId();
while (!accessTokens.containsKey(neededResourceId)) {
OAuthConsumerToken token = requestTokens.remove(neededResourceId);
if (token == null) {
token = getTokenServices().getToken(neededResourceId);
}
String verifier = request.getParameter(OAuthProviderParameter.oauth_verifier.toString());
// if the token is null OR
// if there is NO access token and (we're not using 1.0a or the verifier is not null)
if (token == null || (!token.isAccessToken() && (!resourceThatNeedsAuthorization.isUse10a() || verifier == null))) {
//no token associated with the resource, start the oauth flow.
//if there's a request token, but no verifier, we'll assume that a previous oauth request failed and we need to get a new request token.
// if (LOG.isDebugEnabled()) {
// LOG.debug("Obtaining request token for resource: " + neededResourceId);
// }
//obtain authorization.
String callbackURL = response.encodeRedirectURL(getCallbackURL(request));
token = getConsumerSupport().getUnauthorizedRequestToken(neededResourceId, callbackURL);
// if (LOG.isDebugEnabled()) {
// LOG.debug("Request token obtained for resource " + neededResourceId + ": " + token);
// }
//okay, we've got a request token, now we need to authorize it.
requestTokens.put(neededResourceId, token);
getTokenServices().storeToken(neededResourceId, token);
try {
String baseURL = this.applicationConfigurationService.retrieveOAuthProviderDetails().getUserAuthorizationUrl();
StringBuilder builder = new StringBuilder(baseURL);
char appendChar = baseURL.indexOf('?') < 0 ? '?' : '&';
builder.append(appendChar).append("oauth_token=");
builder.append(URLEncoder.encode(token.getValue(), "UTF-8"));
builder.append('&').append("oauth_callback=");
builder.append(URLEncoder.encode(callbackURL, "UTF-8"));
String redirect = builder.toString();
request.setAttribute("org.springframework.security.oauth.consumer.AccessTokenRequiredException", e);
this.redirectStrategy.sendRedirect(request, response, redirect);
return;
}
catch (UnsupportedEncodingException use) {
throw new IllegalStateException(use);
}
}
else if (!token.isAccessToken()) {
//we have a presumably authorized request token, let's try to get an access token with it.
// if (LOG.isDebugEnabled()) {
// LOG.debug("Obtaining access token for resource: " + neededResourceId);
// }
//authorize the request token and store it.
try {
token = getConsumerSupport().getAccessToken(token, verifier);
}
finally {
getTokenServices().removeToken(neededResourceId);
}
// if (LOG.isDebugEnabled()) {
// LOG.debug("Access token " + token + " obtained for resource " + neededResourceId + ". Now storing and using.");
// }
getTokenServices().storeToken(neededResourceId, token);
}
accessTokens.put(neededResourceId, token);
// hack here to detect this situation which happens once user is authenticated successfully on provider
// and redirected back to this client application
String oauthAccessToken = request.getParameter("oauth_token");
String oauthVerifier = request.getParameter("oauth_verifier");
if (StringUtils.isNotBlank(oauthAccessToken) && StringUtils.isNotBlank(oauthVerifier)) {
CoreOAuthConsumerSupport support = (CoreOAuthConsumerSupport)getConsumerSupport();
MifosNgOauthRestTemplate oauthRestTemplate = new MifosNgOauthRestTemplate(support.getProtectedResourceDetailsService());
try {
URI restUri = URI
.create(getBaseServerUrl().concat("api/protected/user/"
+ oauthAccessToken));
ResponseEntity<AuthenticatedUserData> s = oauthRestTemplate.exchange(restUri, HttpMethod.GET, emptyRequest(), AuthenticatedUserData.class);
AuthenticatedUserData userData = s.getBody();
SecurityContextHolder.clearContext();
SecurityContext springSecurityContext = new SecurityContextImpl();
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
for (String permissionCode : userData.getPermissions()) {
authorities.add(new SimpleGrantedAuthority(permissionCode));
}
UserDetails principal = new ConsumerUserDetails(userData.getUsername(), authorities);
Authentication auth = new UsernamePasswordAuthenticationToken(principal, principal, principal.getAuthorities());
springSecurityContext.setAuthentication(auth);
SecurityContextHolder.setContext(springSecurityContext);
} catch (HttpStatusCodeException ex) {
System.err.println(ex.getResponseBodyAsString());
throw ex;
}
}
try {
//try again
if (!response.isCommitted()) {
request.setAttribute(getAccessTokensRequestAttribute(), new ArrayList<OAuthConsumerToken>(accessTokens.values()));
chain.doFilter(request, response);
}
else {
//dang. what do we do now?
throw new IllegalStateException("Unable to reprocess filter chain with needed OAuth2 resources because the response is already committed.");
}
}
catch (Exception e1) {
resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e1);
neededResourceId = resourceThatNeedsAuthorization.getId();
}
}
}
catch (OAuthRequestFailedException eo) {
fail(request, response, eo);
}
catch (Exception ex) {
Throwable[] causeChain = getThrowableAnalyzer().determineCauseChain(ex);
OAuthRequestFailedException rfe = (OAuthRequestFailedException) getThrowableAnalyzer().getFirstThrowableOfType(OAuthRequestFailedException.class, causeChain);
if (rfe != null) {
fail(request, response, rfe);
}
else {
// Rethrow ServletExceptions and RuntimeExceptions as-is
if (ex instanceof ServletException) {
throw (ServletException) ex;
}
else if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
// Wrap other Exceptions. These are not expected to happen
throw new RuntimeException(ex);
}
}
}
}
finally {
OAuthSecurityContextHolder.setContext(null);
HashMap<String, OAuthConsumerToken> tokensToRemember = new HashMap<String, OAuthConsumerToken>();
tokensToRemember.putAll(requestTokens);
tokensToRemember.putAll(accessTokens);
getRememberMeServices().rememberTokens(tokensToRemember, request, response);
}
}
private HttpEntity<Object> emptyRequest() {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(
requestHeaders);
return requestEntity;
}
private String getBaseServerUrl() {
return this.applicationConfigurationService.retrieveOAuthProviderDetails().getProviderBaseUrl();
}
}
@@ -0,0 +1,67 @@
package org.mifosng.oauth;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth.consumer.AccessTokenRequiredException;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthSecurityContext;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.security.oauth.consumer.filter.OAuthConsumerProcessingFilter;
public class CustomOAuthConsumerProcessingFilter extends
OAuthConsumerProcessingFilter {
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Set<String> accessTokenDeps = getAccessTokenDependencies(request,
response, chain);
if (!accessTokenDeps.isEmpty()) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (isRequireAuthenticated() && !authentication.isAuthenticated()) {
throw new InsufficientAuthenticationException(
"An authenticated principal must be present.");
}
OAuthSecurityContext context = OAuthSecurityContextHolder
.getContext();
if (context == null) {
throw new IllegalStateException(
"No OAuth security context has been established. Unable to access resources.");
}
Map<String, OAuthConsumerToken> accessTokens = context
.getAccessTokens();
for (String dependency : accessTokenDeps) {
if (!accessTokens.containsKey(dependency)) {
throw new AccessTokenRequiredException(
getProtectedResourceDetailsService()
.loadProtectedResourceDetailsById(
dependency));
}
}
chain.doFilter(request, response);
} else {
chain.doFilter(servletRequest, servletResponse);
}
}
}
@@ -0,0 +1,233 @@
package org.mifosng.oauth;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mifosng.ui.JaxRs311ErrorResponseHandler;
import org.springframework.security.oauth.common.OAuthProviderParameter;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthRequestFailedException;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.security.oauth.consumer.OAuthSecurityContextImpl;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
import org.springframework.security.oauth.consumer.filter.OAuthConsumerContextFilter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
public class MifosNgOauthConsumerContextFilter extends
OAuthConsumerContextFilter {
private static final Log LOG = LogFactory
.getLog(MifosNgOauthConsumerContextFilter.class);
private RestTemplate noAuthRestTemplate;
@Override
public void doFilter(final ServletRequest servletRequest,
final ServletResponse servletResponse, final FilterChain chain)
throws IOException, ServletException {
ResponseErrorHandler defaultResponseErrorHandler = new DefaultResponseErrorHandler();
ResponseErrorHandler jaxrs311ErrorResponseHandler = new JaxRs311ErrorResponseHandler(
defaultResponseErrorHandler);
this.noAuthRestTemplate = new RestTemplate();
this.noAuthRestTemplate.setErrorHandler(jaxrs311ErrorResponseHandler);
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
OAuthSecurityContextImpl context = new OAuthSecurityContextImpl();
context.setDetails(request);
Map<String, OAuthConsumerToken> rememberedTokens = getRememberMeServices()
.loadRememberedTokens(request, response);
Map<String, OAuthConsumerToken> accessTokens = new TreeMap<String, OAuthConsumerToken>();
Map<String, OAuthConsumerToken> requestTokens = new TreeMap<String, OAuthConsumerToken>();
if (rememberedTokens != null) {
for (Map.Entry<String, OAuthConsumerToken> tokenEntry : rememberedTokens
.entrySet()) {
OAuthConsumerToken token = tokenEntry.getValue();
if (token != null) {
if (token.isAccessToken()) {
accessTokens.put(tokenEntry.getKey(), token);
} else {
requestTokens.put(tokenEntry.getKey(), token);
}
}
}
}
context.setAccessTokens(accessTokens);
OAuthSecurityContextHolder.setContext(context);
if (LOG.isDebugEnabled()) {
LOG.debug("Storing access tokens in request attribute '"
+ getAccessTokensRequestAttribute() + "'.");
}
try {
try {
request.setAttribute(
getAccessTokensRequestAttribute(),
new ArrayList<OAuthConsumerToken>(accessTokens.values()));
chain.doFilter(request, response);
} catch (Exception e) {
try {
ProtectedResourceDetails resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e);
String neededResourceId = resourceThatNeedsAuthorization
.getId();
while (!accessTokens.containsKey(neededResourceId)) {
OAuthConsumerToken token = requestTokens
.remove(neededResourceId);
if (token == null) {
token = getTokenServices().getToken(
neededResourceId);
}
String verifier = request
.getParameter(OAuthProviderParameter.oauth_verifier
.toString());
// if the token is null OR
// if there is NO access token and (we're not using 1.0a
// or the verifier is not null)
if (token == null
|| !token.isAccessToken()
&& (!resourceThatNeedsAuthorization.isUse10a() || verifier == null)) {
// no token associated with the resource, start the
// oauth flow.
// if there's a request token, but no verifier,
// we'll assume that a previous oauth request failed
// and we need to get a new request token.
if (LOG.isDebugEnabled()) {
LOG.debug("Obtaining request token for resource: "
+ neededResourceId);
}
// obtain authorization.
String callbackURL = response
.encodeRedirectURL(getCallbackURL(request));
token = getConsumerSupport()
.getUnauthorizedRequestToken(
neededResourceId, callbackURL);
if (LOG.isDebugEnabled()) {
LOG.debug("Request token obtained for resource "
+ neededResourceId + ": " + token);
}
// okay, we've got a request token, now we need to
// authorize it.
requestTokens.put(neededResourceId, token);
getTokenServices().storeToken(neededResourceId,
token);
String redirect = getUserAuthorizationRedirectURL(
resourceThatNeedsAuthorization, token,
callbackURL);
if (LOG.isDebugEnabled()) {
LOG.debug("Redirecting request to "
+ redirect
+ " for user authorization of the request token for resource "
+ neededResourceId + ".");
}
request.setAttribute(
"org.springframework.security.oauth.consumer.AccessTokenRequiredException",
e);
getRedirectStrategy().sendRedirect(request,
response, redirect);
return;
} else if (!token.isAccessToken()) {
// we have a presumably authorized request token,
// let's try to get an access token with it.
if (LOG.isDebugEnabled()) {
LOG.debug("Obtaining access token for resource: "
+ neededResourceId);
}
// authorize the request token and store it.
try {
token = getConsumerSupport().getAccessToken(
token, verifier);
} finally {
getTokenServices()
.removeToken(neededResourceId);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Access token " + token
+ " obtained for resource "
+ neededResourceId
+ ". Now storing and using.");
}
getTokenServices().storeToken(neededResourceId,
token);
}
accessTokens.put(neededResourceId, token);
try {
// try again
if (!response.isCommitted()) {
request.setAttribute(
getAccessTokensRequestAttribute(),
new ArrayList<OAuthConsumerToken>(
accessTokens.values()));
chain.doFilter(request, response);
} else {
// dang. what do we do now?
throw new IllegalStateException(
"Unable to reprocess filter chain with needed OAuth2 resources because the response is already committed.");
}
} catch (Exception e1) {
resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e1);
neededResourceId = resourceThatNeedsAuthorization
.getId();
}
}
} catch (OAuthRequestFailedException eo) {
fail(request, response, eo);
} catch (Exception ex) {
Throwable[] causeChain = getThrowableAnalyzer()
.determineCauseChain(ex);
OAuthRequestFailedException rfe = (OAuthRequestFailedException) getThrowableAnalyzer()
.getFirstThrowableOfType(
OAuthRequestFailedException.class,
causeChain);
if (rfe != null) {
fail(request, response, rfe);
} else {
// Rethrow ServletExceptions and RuntimeExceptions as-is
if (ex instanceof ServletException) {
throw (ServletException) ex;
} else if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
// Wrap other Exceptions. These are not expected to
// happen
throw new RuntimeException(ex);
}
}
}
} finally {
OAuthSecurityContextHolder.setContext(null);
HashMap<String, OAuthConsumerToken> tokensToRemember = new HashMap<String, OAuthConsumerToken>();
tokensToRemember.putAll(requestTokens);
tokensToRemember.putAll(accessTokens);
getRememberMeServices().rememberTokens(tokensToRemember, request,
response);
}
}
}
@@ -0,0 +1,16 @@
package org.mifosng.oauth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
import org.springframework.security.oauth.consumer.client.OAuthRestTemplate;
import org.springframework.stereotype.Service;
@Service(value="oauthRestServiceTemplate")
public class MifosNgOauthRestTemplate extends OAuthRestTemplate {
@Autowired
public MifosNgOauthRestTemplate(ProtectedResourceDetailsService protectedResourceDetailsService) {
super(protectedResourceDetailsService.loadProtectedResourceDetailsById("protectedMifosNgServices"));
}
}
@@ -0,0 +1,69 @@
package org.mifosng.oauth;
import java.util.HashMap;
import java.util.Map;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.configuration.OAuthProviderDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
import org.springframework.security.oauth.consumer.BaseProtectedResourceDetails;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
import org.springframework.stereotype.Service;
@Service(value="mifosNgInMemoryResourceDetailsService")
public class MifosNgProtectedResourceDetailsService implements
ProtectedResourceDetailsService, RefreshableProtectedResourceDetailsService {
private Map<String, ProtectedResourceDetails> resourceDetailsStore = new HashMap<String, ProtectedResourceDetails>();
private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public MifosNgProtectedResourceDetailsService(ApplicationConfigurationService applicationConfigurationService) {
this.applicationConfigurationService = applicationConfigurationService;
addHardcodedMifosNgResourceIdToStore();
}
private void addHardcodedMifosNgResourceIdToStore() {
BaseProtectedResourceDetails individualLendingResourceDetails = new BaseProtectedResourceDetails();
individualLendingResourceDetails.setId("protectedMifosNgServices");
OAuthProviderDetails oauthProviderDetails = applicationConfigurationService
.retrieveOAuthProviderDetails();
individualLendingResourceDetails
.setRequestTokenURL(oauthProviderDetails.getRequestTokenUrl());
individualLendingResourceDetails
.setUserAuthorizationURL(oauthProviderDetails
.getUserAuthorizationUrl());
individualLendingResourceDetails.setAccessTokenURL(oauthProviderDetails
.getAccessTokenUrl());
individualLendingResourceDetails.setConsumerKey(oauthProviderDetails
.getConsumerkey());
individualLendingResourceDetails
.setSharedSecret(new SharedConsumerSecret(oauthProviderDetails
.getSharedSecret()));
resourceDetailsStore.put(
individualLendingResourceDetails.getId(),
individualLendingResourceDetails);
}
@Override
public ProtectedResourceDetails loadProtectedResourceDetailsById(
final String id) throws IllegalArgumentException {
return resourceDetailsStore.get(id);
}
@Override
public void refresh() {
resourceDetailsStore.clear();
addHardcodedMifosNgResourceIdToStore();
}
public ApplicationConfigurationService getApplicationConfigurationService() {
return applicationConfigurationService;
}
}
@@ -0,0 +1,45 @@
package org.mifosng.oauth;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
public class OAuthConsumerFilterFilterInvocationSecurityMetadataSource
implements FilterInvocationSecurityMetadataSource {
private final FilterInvocationSecurityMetadataSource metadataSource;
public OAuthConsumerFilterFilterInvocationSecurityMetadataSource() {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
RequestMatcher key = new AntPathRequestMatcher("/sparklr/**");
Collection<ConfigAttribute> value = new ArrayList<ConfigAttribute>();
value.add(new SecurityConfig("sparklrPhotos"));
requestMap.put(key, value);
this.metadataSource = new DefaultFilterInvocationSecurityMetadataSource(
requestMap);
}
@Override
public Collection<ConfigAttribute> getAttributes(final Object object)
throws IllegalArgumentException {
return this.metadataSource.getAttributes(object);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return this.metadataSource.getAllConfigAttributes();
}
@Override
public boolean supports(final Class<?> clazz) {
return this.metadataSource.supports(clazz);
}
}
@@ -0,0 +1,91 @@
package org.mifosng.oauth;
import java.net.URI;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.configuration.OAuthProviderDetails;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.security.oauth.consumer.OAuthSecurityContextImpl;
import org.springframework.security.oauth.consumer.rememberme.OAuthRememberMeServices;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
/**
* Logout Filter process before oauth filters are applied so need create OAuthSercurityContext and populate with 'remembered tokens' first.
*/
public class OAuthLogoutHandler implements LogoutHandler {
private final SecurityContextLogoutHandler delegate;
private final MifosNgProtectedResourceDetailsService protectedResourceDetailsService;
private final OAuthRememberMeServices oAuthRememberMeServices;
public OAuthLogoutHandler(final SecurityContextLogoutHandler delegate, final MifosNgProtectedResourceDetailsService protectedResourceDetailsService,
final OAuthRememberMeServices oAuthRememberMeServices) {
this.delegate = delegate;
this.protectedResourceDetailsService = protectedResourceDetailsService;
this.oAuthRememberMeServices = oAuthRememberMeServices;
}
@Override
public void logout(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) {
this.delegate.logout(request, response, authentication);
OAuthSecurityContextImpl context = new OAuthSecurityContextImpl();
context.setDetails(request);
Map<String, OAuthConsumerToken> rememberedTokens = this.oAuthRememberMeServices.loadRememberedTokens(request, response);
Map<String, OAuthConsumerToken> accessTokens = new TreeMap<String, OAuthConsumerToken>();
if (rememberedTokens != null) {
for (Map.Entry<String, OAuthConsumerToken> tokenEntry : rememberedTokens.entrySet()) {
OAuthConsumerToken token = tokenEntry.getValue();
if (token != null) {
if (token.isAccessToken()) {
accessTokens.put(tokenEntry.getKey(), token);
}
}
}
}
context.setAccessTokens(accessTokens);
OAuthSecurityContextHolder.setContext(context);
if (!accessTokens.isEmpty()) {
MifosNgOauthRestTemplate oauthRestTemplate = new MifosNgOauthRestTemplate(
protectedResourceDetailsService);
OAuthProviderDetails providerDetails = this.protectedResourceDetailsService
.getApplicationConfigurationService()
.retrieveOAuthProviderDetails();
URI restUri = URI.create(providerDetails.getProviderBaseUrl()
.concat("api/protected/user/signout"));
oauthRestTemplate.exchange(restUri, HttpMethod.GET, emptyRequest(),
null);
}
OAuthSecurityContextHolder.setContext(null);
}
private HttpEntity<Object> emptyRequest() {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(
requestHeaders);
return requestEntity;
}
}
@@ -0,0 +1,7 @@
package org.mifosng.oauth;
public interface RefreshableProtectedResourceDetailsService {
void refresh();
}
@@ -0,0 +1,177 @@
package org.mifosng.ui;
import java.util.Collection;
import java.util.Map;
import org.mifosng.data.AppUserData;
import org.mifosng.data.ClientData;
import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.data.ExtraDatasets;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.NoteData;
import org.mifosng.data.OfficeData;
import org.mifosng.data.PermissionData;
import org.mifosng.data.RoleData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.ChangePasswordCommand;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.NoteCommand;
import org.mifosng.data.command.OfficeCommand;
import org.mifosng.data.command.RoleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.UpdateOrganisationCurrencyCommand;
import org.mifosng.data.command.UserCommand;
import org.mifosng.data.reports.GenericResultset;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
public interface CommonRestOperations {
void logout(String accessToken);
void updateProtectedResource(
ProtectedResourceDetails loadProtectedResourceDetailsById);
void updateOrganisationCurrencies(UpdateOrganisationCurrencyCommand command);
Collection<OfficeData> retrieveAllOffices();
OfficeData retrieveOffice(Long officeId);
Collection<AppUserData> retrieveAllUsers();
AppUserData retrieveNewUserDetails();
AppUserData retrieveUser(Long userId);
AppUserData retrieveCurrentUser();
EntityIdentifier createUser(UserCommand command);
EntityIdentifier updateUser(UserCommand command);
EntityIdentifier updateCurrentUserPassword(ChangePasswordCommand command);
void deleteUser(Long userId);
Collection<RoleData> retrieveAllRoles();
RoleData retrieveRole(Long roleId);
RoleData retrieveNewRoleDetails();
EntityIdentifier updateRole(RoleCommand command);
Collection<PermissionData> retrieveAllPermissions();
Collection<EnumOptionReadModel> retrieveAllPermissionGroups();
EntityIdentifier createRole(RoleCommand command);
EntityIdentifier createOffice(OfficeCommand command);
EntityIdentifier updateOffice(OfficeCommand command);
Collection<ClientData> retrieveAllIndividualClients();
Collection<LoanProductData> retrieveAllLoanProducts();
Collection<CurrencyData> retrieveAllowedCurrencies();
Collection<CurrencyData> retrieveAllPlatformCurrencies();
Collection<EnumOptionReadModel> retrieveAllowedLoanAmortizationMethodOptions();
Collection<EnumOptionReadModel> retrieveAllowedLoanInterestMethodOptions();
Collection<EnumOptionReadModel> retrieveAllowedRepaymentFrequencyOptions();
Collection<EnumOptionReadModel> retrieveAllowedNominalInterestFrequencyOptions();
EntityIdentifier createLoanProduct(CreateLoanProductCommand command);
EntityIdentifier updateLoanProduct(UpdateLoanProductCommand command);
LoanProductData retrieveLoanProductDetails(Long selectedLoanProductOption);
LoanProductData retrieveNewLoanProductDetails();
LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationStepOneDetails(
Long clientId);
Long submitLoanApplication(SubmitLoanApplicationCommand command);
EntityIdentifier deleteLoan(Long loanId);
EntityIdentifier approveLoan(LoanStateTransitionCommand command);
EntityIdentifier undoLoanApproval(UndoLoanApprovalCommand command);
EntityIdentifier rejectLoan(LoanStateTransitionCommand command);
EntityIdentifier withdrawLoan(LoanStateTransitionCommand command);
EntityIdentifier disburseLoan(LoanStateTransitionCommand command);
EntityIdentifier undloLoanDisbursal(UndoLoanDisbursalCommand command);
LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId);
LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId, Long repaymentId);
EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
EntityIdentifier adjustLoanRepayment(AdjustLoanTransactionCommand command);
LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId);
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
EntityIdentifier updateCurrentUserDetails(UserCommand command);
ClientData retrieveNewIndividualClient();
ClientData retrieveClientDetails(Long clientId);
EntityIdentifier enrollClient(EnrollClientCommand command);
EntityIdentifier addNote(NoteCommand command);
EntityIdentifier updateNote(NoteCommand command);
NoteData retrieveClientNote(Long clientId, Long noteId);
Collection<NoteData> retrieveClientNotes(Long clientId);
ClientDataWithAccountsData retrieveClientAccount(Long clientId);
LoanAccountData retrieveLoanAccount(Long loanId);
ExtraDatasets retrieveExtraDatasets(String datasetType);
GenericResultset retrieveExtraData(String datasetType, String datasetName,
String datasetPKValue);
@SuppressWarnings("rawtypes")
EntityIdentifier saveExtraData(String datasetType, String datasetName,
String datasetPKValue, Map map);
GenericResultset retrieveReportingData(String rptDB, String name, String type, Map<String, String> extractedQueryParams);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationDetails(Long clientId, Long productId);
}
@@ -0,0 +1,34 @@
package org.mifosng.ui;
import org.mifosng.data.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
public class Jax2bRestClientException extends RestClientException {
private final HttpStatus statusCode;
private final MediaType contentType;
private final ErrorResponse errorResponse;
public Jax2bRestClientException(final HttpStatus statusCode,
final MediaType contentType, final ErrorResponse errorResponse) {
super(statusCode.name() + ": on client http rest request.");
this.statusCode = statusCode;
this.contentType = contentType;
this.errorResponse = errorResponse;
}
public HttpStatus getStatusCode() {
return this.statusCode;
}
public MediaType getContentType() {
return this.contentType;
}
public ErrorResponse getErrorResponse() {
return this.errorResponse;
}
}
@@ -0,0 +1,34 @@
package org.mifosng.ui;
import org.mifosng.data.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
public class Jax2bRestServerException extends RestClientException {
private final HttpStatus statusCode;
private final MediaType contentType;
private final ErrorResponse errorResponse;
public Jax2bRestServerException(final HttpStatus statusCode,
final MediaType contentType, final ErrorResponse errorResponse) {
super(statusCode.name() + ": on client http rest request.");
this.statusCode = statusCode;
this.contentType = contentType;
this.errorResponse = errorResponse;
}
public HttpStatus getStatusCode() {
return this.statusCode;
}
public MediaType getContentType() {
return this.contentType;
}
public ErrorResponse getErrorResponse() {
return this.errorResponse;
}
}
@@ -0,0 +1,49 @@
package org.mifosng.ui;
import java.io.IOException;
import org.mifosng.data.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
public class JaxRs311ErrorResponseHandler implements ResponseErrorHandler {
private final ResponseErrorHandler delegateResponseErrorHandler;
public JaxRs311ErrorResponseHandler(
final ResponseErrorHandler delegateResponseErrorHandler) {
this.delegateResponseErrorHandler = delegateResponseErrorHandler;
}
@Override
public boolean hasError(final ClientHttpResponse response) throws IOException {
return this.delegateResponseErrorHandler.hasError(response);
}
@Override
public void handleError(final ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
MediaType contentType = response.getHeaders().getContentType();
Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
ErrorResponse errorResponse = (ErrorResponse) converter.read(
ErrorResponse.class, response);
switch (statusCode.series()) {
case CLIENT_ERROR:
throw new Jax2bRestClientException(statusCode, contentType, errorResponse);
case SERVER_ERROR:
throw new Jax2bRestServerException(statusCode, contentType,
errorResponse);
default:
throw new RestClientException("Unknown status code [" + statusCode
+ "]");
}
}
}
@@ -0,0 +1,106 @@
package org.mifosng.ui.admin;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.data.AppUserData;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.ChangePasswordCommand;
import org.mifosng.data.command.UserCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* Controller for user and organisation administration functions.
*/
@Controller
public class AdministrationController {
private final CommonRestOperations commonRestOperations;
@Autowired
public AdministrationController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
@RequestMapping(value = "/org/admin", method = RequestMethod.GET)
public String organisationAdminScreen() {
return "admin/orghome";
}
@RequestMapping(value = "/org/admin/user", method = RequestMethod.GET)
public String userAdminScreen() {
return "admin/userhome";
}
@RequestMapping(value = "/org/admin/settings", method = RequestMethod.GET)
public String userSettingsScreen(Model model) {
return "admin/accountsettings";
}
@RequestMapping(consumes="application/json", produces="application/json", value = "org/admin/settings/details", method = RequestMethod.GET)
public @ResponseBody AppUserData viewUserDetails() {
return this.commonRestOperations.retrieveCurrentUser();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "org/admin/settings/details", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateUserDetails(HttpServletRequest request,
@RequestParam(value="username", required=false) String username,
@RequestParam(value="firstname", required=false) String firstname,
@RequestParam(value="lastname", required=false) String lastname,
@RequestParam(value="email", required=false) String email) {
UserCommand command = new UserCommand(username, firstname, lastname, email);
return this.commonRestOperations.updateCurrentUserDetails(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "org/admin/settings/password", method = RequestMethod.GET)
public @ResponseBody ChangePasswordCommand viewChangePasswordDetails() {
return new ChangePasswordCommand();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "org/admin/settings/password", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateUserDetails(HttpServletRequest request,
@RequestParam(value="password", required=false) String password,
@RequestParam(value="passwordrepeat", required=false) String passwordrepeat) {
ChangePasswordCommand command = new ChangePasswordCommand();
command.setPassword(password);
command.setPasswordrepeat(passwordrepeat);
return this.commonRestOperations.updateCurrentUserPassword(command);
}
}
@@ -0,0 +1,180 @@
package org.mifosng.ui.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.data.ClientData;
import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.ClientList;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.NoteData;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.NoteCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class ClientController {
private final CommonRestOperations commonRestOperations;
@Autowired
public ClientController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) {
LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(eventDate)) {
try {
Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale));
} catch (IllegalArgumentException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate));
throw new ClientValidationException(validationErrors);
}
}
return eventLocalDate;
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/all", method = RequestMethod.GET)
public @ResponseBody List<NoteData> retrieveClientNotes(@PathVariable("clientId") Long clientId) {
Collection<NoteData> clientNotes = this.commonRestOperations.retrieveClientNotes(clientId);
return new ArrayList<NoteData>(clientNotes);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/new", method = RequestMethod.GET)
public @ResponseBody NoteData retrieveNewNoteDetails(@PathVariable("clientId") Long clientId) {
NoteData noteData = new NoteData();
noteData.setClientId(clientId);
return noteData;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/note/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier addClientNote(@PathVariable("clientId") Long clientId,
@RequestParam(value="note", required=false) String note) {
NoteCommand command = new NoteCommand();
command.setClientId(clientId);
command.setNote(note);
return this.commonRestOperations.addNote(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/{noteId}", method = RequestMethod.GET)
public @ResponseBody NoteData retrieveExistingNoteDetails(@PathVariable("clientId") Long clientId, @PathVariable("noteId") Long noteId) {
return this.commonRestOperations.retrieveClientNote(clientId, noteId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/note/{noteId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateClientNote(@PathVariable("clientId") Long clientId, @PathVariable("noteId") Long noteId,
@RequestParam(value="note", required=false) String note) {
NoteCommand command = new NoteCommand();
command.setId(noteId);
command.setClientId(clientId);
command.setNote(note);
return this.commonRestOperations.updateNote(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/all", method = RequestMethod.GET)
public @ResponseBody ClientList viewAllClients() {
return new ClientList(this.commonRestOperations.retrieveAllIndividualClients());
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/new", method = RequestMethod.GET)
public @ResponseBody ClientData newIndividualClientDetails() {
ClientData clientData = this.commonRestOperations.retrieveNewIndividualClient();
return clientData;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createClient(HttpServletRequest request,
@RequestParam(value="office", required=false) Long officeId,
@RequestParam(value="firstname", required=false) String firstname,
@RequestParam(value="lastname", required=false) String lastname,
@RequestParam(value="fullname", required=false) String fullname,
@RequestParam(value="joiningDate", required=false) String joiningDate,
@RequestParam(value="externalId", required=false) String externalId) {
LocalDate joiningLocalDate = parseStringToLocalDate(joiningDate, "joiningDate");
EnrollClientCommand command = new EnrollClientCommand(firstname, lastname, fullname, officeId, joiningLocalDate);
return this.commonRestOperations.enrollClient(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}", method = RequestMethod.GET)
public @ResponseBody ClientDataWithAccountsData viewClientAccountData(@PathVariable("clientId") Long clientId) {
return this.commonRestOperations.retrieveClientAccount(clientId);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/loan/{loanId}", method = RequestMethod.GET)
public @ResponseBody LoanAccountData viewLoanAccountData(@PathVariable("loanId") Long loanId) {
return this.commonRestOperations.retrieveLoanAccount(loanId);
}
@RequestMapping(value = "/portfolio/client/{clientId}", method = RequestMethod.GET)
public String viewClientAccount(final Model model, @PathVariable("clientId") final Long clientId) {
ClientData clientData = this.commonRestOperations.retrieveClientDetails(clientId);
model.addAttribute("clientId", clientId);
model.addAttribute("clientDisplayName", clientData.getDisplayName());
return "client/viewClientAccount";
}
}
@@ -0,0 +1,30 @@
package org.mifosng.ui.client;
public class DropdownOption {
private String label;
private String value;
public static DropdownOption of(String label, String value) {
DropdownOption option = new DropdownOption();
option.setLabel(label);
option.setValue(value);
return option;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@@ -0,0 +1,18 @@
package org.mifosng.ui.client;
import org.joda.time.DateTime;
import org.springframework.format.annotation.DateTimeFormat;
public class PayoffFormBean {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private DateTime payoffDate = new DateTime();
public DateTime getPayoffDate() {
return this.payoffDate;
}
public void setPayoffDate(final DateTime payoffDate) {
this.payoffDate = payoffDate;
}
}
@@ -0,0 +1,133 @@
package org.mifosng.ui.configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.configuration.OAuthProviderDetails;
import org.mifosng.data.ConfigurationData;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.UpdateOrganisationCurrencyCommand;
import org.mifosng.oauth.RefreshableProtectedResourceDetailsService;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
@SessionAttributes({"oauthSettingsFormBean"})
@Controller
public class ConfigurationController {
private final ApplicationConfigurationService applicationConfigurationService;
private final RefreshableProtectedResourceDetailsService refreshableProtectedResourceDetailsService;
private final CommonRestOperations commonRestOperations;
@Autowired
public ConfigurationController(
final CommonRestOperations commonRestOperations, final ApplicationConfigurationService applicationConfigurationService,
final RefreshableProtectedResourceDetailsService refreshableProtectedResourceDetailsService) {
this.commonRestOperations = commonRestOperations;
this.applicationConfigurationService = applicationConfigurationService;
this.refreshableProtectedResourceDetailsService = refreshableProtectedResourceDetailsService;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody
Collection<ErrorResponse> validationException(ClientValidationException ex,
HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "org/configuration/edit", method = RequestMethod.GET)
public @ResponseBody ConfigurationData viewConfigurationDetails() {
List<CurrencyData> selectedCurrencyOptions = new ArrayList<CurrencyData>(this.commonRestOperations.retrieveAllowedCurrencies());
List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>(this.commonRestOperations.retrieveAllPlatformCurrencies());
// remove selected currency options
currencyOptions.removeAll(selectedCurrencyOptions);
ConfigurationData configurationData = new ConfigurationData();
configurationData.setCurrencyOptions(currencyOptions);
configurationData.setSelectedCurrencyOptions(selectedCurrencyOptions);
return configurationData;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "org/configuration/edit", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateOrganisationCurrencies(@RequestParam(value="selectedItems", required=false) String[] selectedCurrencies) {
List<String> codes = new ArrayList<String>();
if (selectedCurrencies != null) {
codes = Arrays.asList(selectedCurrencies);
}
UpdateOrganisationCurrencyCommand command = new UpdateOrganisationCurrencyCommand(codes);
this.commonRestOperations.updateOrganisationCurrencies(command);
return new EntityIdentifier(Long.valueOf(1));
}
@ModelAttribute("oauthSettingsFormBean")
@RequestMapping(value = "oauth/configuration/edit", method = RequestMethod.GET)
public ModelAndView showOAuthConfiguration() {
OAuthProviderDetails details = this.applicationConfigurationService.retrieveOAuthProviderDetails();
OauthSettingsFormBean oauthSettingsFormBean = new OauthSettingsFormBean();
oauthSettingsFormBean.setConsumerkey(details.getConsumerkey());
oauthSettingsFormBean.setSharedSecret(details.getSharedSecret());
oauthSettingsFormBean.setOauthProviderUrl(details.getProviderBaseUrl());
return new ModelAndView("configuration/oauthconfig",
"oauthSettingsFormBean", oauthSettingsFormBean);
}
@RequestMapping(value = "oauth/configuration/edit", method = RequestMethod.POST)
public ModelAndView updateOAuthConfiguration(
@ModelAttribute final OauthSettingsFormBean oauthSettingsFormBean,
final BindingResult result) {
ModelAndView mav = new ModelAndView("redirect:/");
OAuthProviderDetails newDetails = new OAuthProviderDetails(oauthSettingsFormBean.getOauthProviderUrl(), oauthSettingsFormBean.getConsumerkey(), oauthSettingsFormBean.getSharedSecret());
this.applicationConfigurationService.update(newDetails);
this.refreshableProtectedResourceDetailsService.refresh();
this.commonRestOperations.updateProtectedResource(((ProtectedResourceDetailsService)refreshableProtectedResourceDetailsService).loadProtectedResourceDetailsById("protectedMifosNgServices"));
return mav;
}
}
@@ -0,0 +1,32 @@
package org.mifosng.ui.configuration;
public class OauthSettingsFormBean {
private String oauthProviderUrl;
private String sharedSecret;
private String consumerkey;
public String getOauthProviderUrl() {
return oauthProviderUrl;
}
public void setOauthProviderUrl(String oauthProviderUrl) {
this.oauthProviderUrl = oauthProviderUrl;
}
public String getSharedSecret() {
return sharedSecret;
}
public void setSharedSecret(String sharedSecret) {
this.sharedSecret = sharedSecret;
}
public String getConsumerkey() {
return consumerkey;
}
public void setConsumerkey(String consumerkey) {
this.consumerkey = consumerkey;
}
}
@@ -0,0 +1,67 @@
package org.mifosng.ui.extradata;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ExtraDatasets;
import org.mifosng.data.reports.GenericResultset;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class ExtraDataController {
private final CommonRestOperations commonRestOperations;
@Autowired
public ExtraDataController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/extradata/datasets/{datasetType}", method = RequestMethod.GET)
public @ResponseBody ExtraDatasets retrieveExtraDatasets(@PathVariable("datasetType") String datasetType) {
return this.commonRestOperations.retrieveExtraDatasets(datasetType);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/extradata/{datasetType}/{datasetName}/{datasetPKValue}", method = RequestMethod.GET)
public @ResponseBody GenericResultset viewExtraData(@PathVariable("datasetType") String datasetType, @PathVariable("datasetName") String datasetName, @PathVariable("datasetPKValue") String datasetPKValue) {
return this.commonRestOperations.retrieveExtraData(datasetType, datasetName, datasetPKValue);
}
@RequestMapping(produces="application/json", value = "/extradata/{datasetType}/{datasetName}/{datasetPKValue}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier saveExtraData(HttpServletRequest request, @PathVariable("datasetType") String datasetType,@PathVariable("datasetName") String datasetName, @PathVariable("datasetPKValue") String datasetPKValue) {
return this.commonRestOperations.saveExtraData(datasetType, datasetName, datasetPKValue, request.getParameterMap());
}
}
@@ -0,0 +1,43 @@
package org.mifosng.ui.home;
import org.mifosng.ui.reporting.ReportingRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HomeController {
private final ReportingRestOperations reportingRestOperations;
@Autowired
public HomeController(final ReportingRestOperations reportingRestOperations) {
this.reportingRestOperations = reportingRestOperations;
}
@RequestMapping(value="/forceOAuth", method = RequestMethod.GET)
public String forceOAuth() {
this.reportingRestOperations.hackToForceAuthentication();
return "redirect:/home";
}
@RequestMapping(value="/", method = RequestMethod.GET)
public String redirectToIndexPage() {
return "redirect:/home";
}
@RequestMapping(value = "/home", method = RequestMethod.GET)
public ModelAndView showIndividualClientsOnHomePage() {
return new ModelAndView("/home");
}
@RequestMapping(value="/switchToClient", method = RequestMethod.POST)
public String switchToClientView(@RequestParam("client") final Long client) {
return "redirect:/portfolio/client/" + client;
}
}
@@ -0,0 +1,64 @@
package org.mifosng.ui.home;
import java.util.Map;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.ui.CommonRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthSecurityContext;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LogoutController {
private final CommonRestOperations commonRestOperations;
// private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public LogoutController(final CommonRestOperations commonRestOperations,
ApplicationConfigurationService applicationConfigurationService) {
this.commonRestOperations = commonRestOperations;
// this.applicationConfigurationService = applicationConfigurationService;
}
@RequestMapping(value="/logoutcontroller", method = RequestMethod.GET)
public String forceOAuth() {
// OAuthProviderDetails oauthDetails = this.applicationConfigurationService
// .retrieveOAuthProviderDetails();
String resourceId = "protectedMifosNgServices";
OAuthSecurityContext context = OAuthSecurityContextHolder.getContext();
if (context == null) {
throw new IllegalStateException(
"No OAuth security context has been established. Unable to access resource '"
+ resourceId + "'.");
}
Map<String, OAuthConsumerToken> accessTokens = context
.getAccessTokens();
OAuthConsumerToken accessToken = accessTokens == null ? null
: accessTokens.get(resourceId);
// model.addAttribute("baseUrl", oauthDetails.getProviderBaseUrl());
// model.addAttribute("consumerKey", oauthDetails.getConsumerkey());
// model.addAttribute("consumerSecret", oauthDetails.getSharedSecret());
// if (accessToken != null) {
// model.addAttribute("accessToken", accessToken.getValue());
// model.addAttribute("tokenSecret", accessToken.getSecret());
// }
String oauthToken = "";
if (accessToken != null) {
oauthToken = accessToken.getValue();
}
this.commonRestOperations.logout(oauthToken);
return "redirect:/j_spring_security_logout";
}
}
@@ -0,0 +1,78 @@
package org.mifosng.ui.loan;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.MoneyData;
import org.mifosng.ui.CommonRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class CashflowController {
// private final CommonRestOperations commonRestOperations;
@Autowired
public CashflowController(final CommonRestOperations commonRestOperations) {
// this.commonRestOperations = commonRestOperations;
}
@RequestMapping(value = "/portfolio/client/{clientId}/cashflow/new", method = RequestMethod.GET)
public String submitNewCashflowAnalysis(@PathVariable("clientId") final Long clientId) {
return "redirect:/createCashflowAnalysis?loanId=0&clientId={clientId}";
}
// called from webflow
public void populateLoanDetailsForCashflow(final CashflowFormBean formBean, Long loanId, Long clientId) {
formBean.setBusinessName("Fixed Example Business (Sole Trader)");
formBean.setClientName("Fixed Example Client");
CurrencyData currency = new CurrencyData("XOF", "", 0, "CFA", "currency.XOF");
MoneyData amount = MoneyData.of(currency, BigDecimal.valueOf(0));
formBean.setExpectedLoanTerm(12);
List<MultiTermExpense> investments = new ArrayList<MultiTermExpense>();
investments.add(new MultiTermExpense("equipment purchase", Integer.valueOf(0), amount.getAmount(), formBean.getExpectedLoanTerm()));
investments.add(new MultiTermExpense("Option two", Integer.valueOf(0), amount.getAmount(), formBean.getExpectedLoanTerm()));
investments.add(new MultiTermExpense("Option three", Integer.valueOf(0), amount.getAmount(), formBean.getExpectedLoanTerm()));
formBean.setInvestments(investments);
List<OneOffExpense> fixedExpenses = new ArrayList<OneOffExpense>();
fixedExpenses.add(new OneOffExpense("Fixed one", amount.getAmount()));
fixedExpenses.add(new OneOffExpense("Fixed two", amount.getAmount()));
fixedExpenses.add(new OneOffExpense("Fixed three", amount.getAmount()));
formBean.setFixedExpenses(fixedExpenses);
List<UnitInformationOfProduct> products = new ArrayList<UnitInformationOfProduct>();
products.add(new UnitInformationOfProduct("First Product", Integer.valueOf(10), BigDecimal.valueOf(Double.valueOf("12.00")), BigDecimal.valueOf(Double.valueOf("23.00"))));
products.add(new UnitInformationOfProduct("Second Product", Integer.valueOf(500), BigDecimal.valueOf(Double.valueOf("10.00")), BigDecimal.valueOf(Double.valueOf("15.00"))));
formBean.setProducts(products);
// List<ClientReadModel> individualClients = new ArrayList<ClientReadModel>(this.commonRestOperations.retrieveLoan);
// List<LoanProductReadModel> loanProducts = new ArrayList<LoanProductReadModel>(
// this.commonRestOperations.retrieveAllLoanProducts());
//
// loanFormBean.setApplicantOptions(individualClients);
// if (individualClients.size() == 1) {
// loanFormBean.setSelectedApplicantOption(individualClients.get(0)
// .getId());
// }
//
// loanFormBean.setLoanProductOptions(loanProducts);
// if (loanProducts.size() == 1) {
// loanFormBean.setSelectedLoanProductOption(loanProducts.get(0)
// .getId());
// }
}
}
@@ -0,0 +1,76 @@
package org.mifosng.ui.loan;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class CashflowFormBean implements Serializable {
private String clientName;
private String businessName;
private Integer expectedLoanTerm = Integer.valueOf(12);
private List<MultiTermExpense> investments = new ArrayList<MultiTermExpense>();
private List<OneOffExpense> fixedExpenses = new ArrayList<OneOffExpense>();
private List<UnitInformationOfProduct> products = new ArrayList<UnitInformationOfProduct>();
private Number entrepreneurInput = Double.valueOf("0");
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getBusinessName() {
return businessName;
}
public void setBusinessName(String businessName) {
this.businessName = businessName;
}
public List<OneOffExpense> getFixedExpenses() {
return fixedExpenses;
}
public void setFixedExpenses(List<OneOffExpense> fixedExpenses) {
this.fixedExpenses = fixedExpenses;
}
public List<UnitInformationOfProduct> getProducts() {
return products;
}
public void setProducts(List<UnitInformationOfProduct> products) {
this.products = products;
}
public Integer getExpectedLoanTerm() {
return expectedLoanTerm;
}
public void setExpectedLoanTerm(Integer expectedLoanTerm) {
this.expectedLoanTerm = expectedLoanTerm;
}
public List<MultiTermExpense> getInvestments() {
return investments;
}
public void setInvestments(List<MultiTermExpense> investments) {
this.investments = investments;
}
public Number getEntrepreneurInput() {
return entrepreneurInput;
}
public void setEntrepreneurInput(Number entrepreneurInput) {
this.entrepreneurInput = entrepreneurInput;
}
}
@@ -0,0 +1,209 @@
package org.mifosng.ui.loan;
import java.math.BigDecimal;
import java.security.Principal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.oauth.ConsumerUserDetails;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class LoanController {
private final CommonRestOperations commonRestOperations;
@Autowired
public LoanController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) {
LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(eventDate)) {
try {
Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale));
} catch (IllegalArgumentException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate));
throw new ClientValidationException(validationErrors);
}
}
return eventLocalDate;
}
private BigDecimal parseStringToBigDecimal(String source) {
try {
BigDecimal number = null;
if (StringUtils.isNotBlank(source)) {
String sourceWithoutSpaces = source.replaceAll(" ", "");
Locale locale = LocaleContextHolder.getLocale();
NumberFormat format = NumberFormat.getNumberInstance(locale);
Number parsedNumber = format.parse(sourceWithoutSpaces);
number = BigDecimal.valueOf(Double.valueOf(parsedNumber
.doubleValue()));
}
return number;
} catch (ParseException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.number.format", "amount", source));
throw new ClientValidationException(validationErrors);
}
}
@RequestMapping(value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.GET)
public String loadLoanCreationWorkflow(final Model model, @PathVariable("clientId") final Long clientId, final Principal principal) {
UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) principal;
ConsumerUserDetails user = (ConsumerUserDetails) authenticationToken.getPrincipal();
if (user.hasNoAuthorityToSumitLoanApplication()) {
throw new AccessDeniedException("");
}
model.addAttribute("clientId", clientId);
return "newloanapplication";
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.POST)
public @ResponseBody EntityIdentifier submitNewLoanApplication(
@RequestParam(value="clientId", required=false) Long applicantId,
@RequestParam(value="productId", required=false) Long productId,
@RequestParam(value="submittedOnDate", required=false) String submittedOnDateStr,
@RequestParam(value="submittedOnNote", required=false) String submittedOnNote,
@RequestParam(value="currencyCode", required=false) String currencyCode,
@RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal,
@RequestParam(value="principalMoney", required=false) String principalMoney,
@RequestParam(value="repaidEvery", required=false) Integer repaymentEvery,
@RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency,
@RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments,
@RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod,
@RequestParam(value="inArrearsTolerance", required=false) String inArrearsTolerance,
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
) {
final LocalDate submittedOnDate = parseStringToLocalDate(submittedOnDateStr, "submittedOnDate");
final Number toleranceAmount = parseStringToBigDecimal(inArrearsTolerance);
final Number principal = parseStringToBigDecimal(principalMoney);
final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate);
final boolean flexibleRepaymentSchedule = false;
final boolean interestRebateAllowed = false;
final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate");
final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate");
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.commonRestOperations.calculateLoanSchedule(calculateLoanScheduleCommand);
SubmitLoanApplicationCommand command = new SubmitLoanApplicationCommand(applicantId, productId,
submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate,
loanSchedule, currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
Long identifier = this.commonRestOperations.submitLoanApplication(command);
return new EntityIdentifier(identifier);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loanschedule/calculate", method = RequestMethod.POST)
public @ResponseBody LoanSchedule calculateLoanSchedule(
@RequestParam(value="currencyCode", required=false) String currencyCode,
@RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal,
@RequestParam(value="principalMoney", required=false) String principalMoney,
@RequestParam(value="repaidEvery", required=false) Integer repaymentEvery,
@RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency,
@RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments,
@RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod,
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
) {
final Number principal = parseStringToBigDecimal(principalMoney);
final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate);
final boolean flexibleRepaymentSchedule = false;
final boolean interestRebateAllowed = false;
final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate");
final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate");
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand command = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
return this.commonRestOperations.calculateLoanSchedule(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/product/{productId}/new", method = RequestMethod.GET)
public @ResponseBody NewLoanWorkflowStepOneData retrieveNewLoan(@PathVariable("clientId") Long clientId, @PathVariable("productId") Long productId) {
return this.commonRestOperations.retrieveNewLoanApplicationDetails(clientId, productId);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/loan/new/workflow/one", method = RequestMethod.GET)
public @ResponseBody NewLoanWorkflowStepOneData retrieveNewLoan(@PathVariable("clientId") Long clientId) {
return this.commonRestOperations.retrieveNewLoanApplicationStepOneDetails(clientId);
}
}
@@ -0,0 +1,251 @@
package org.mifosng.ui.loan;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class LoanTransactionController {
private final CommonRestOperations commonRestOperations;
@Autowired
public LoanTransactionController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) {
LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(eventDate)) {
try {
Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale));
} catch (IllegalArgumentException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate));
throw new ClientValidationException(validationErrors);
}
}
return eventLocalDate;
}
private BigDecimal parseStringToBigDecimal(String source) {
try {
BigDecimal number = null;
if (StringUtils.isNotBlank(source)) {
String sourceWithoutSpaces = source.replaceAll(" ", "");
Locale locale = LocaleContextHolder.getLocale();
NumberFormat format = NumberFormat.getNumberInstance(locale);
Number parsedNumber = format.parse(sourceWithoutSpaces);
number = BigDecimal.valueOf(Double.valueOf(parsedNumber
.doubleValue()));
}
return number;
} catch (ParseException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.number.format", "amount", source));
throw new ClientValidationException(validationErrors);
}
}
@RequestMapping(value = "/portfolio/loan/{loanId}/delete", method = RequestMethod.POST)
public @ResponseBody EntityIdentifier deleteLoan(@PathVariable("loanId") final Long loanId) {
return this.commonRestOperations.deleteLoan(loanId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/approve", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier approveLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate approvalDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, approvalDate, comment);
return this.commonRestOperations.approveLoan(command);
}
@RequestMapping(value = "/portfolio/loan/{loanId}/undoapproval", method = RequestMethod.POST)
public String undoLoanApproval(@PathVariable("loanId") final Long loanId) {
UndoLoanApprovalCommand command = new UndoLoanApprovalCommand(loanId);
EntityIdentifier clientAccountId = this.commonRestOperations
.undoLoanApproval(command);
return "redirect:/portfolio/client/" + clientAccountId.getEntityId();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/reject", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier rejectLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate rejectionDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, rejectionDate, comment);
return this.commonRestOperations.rejectLoan(command);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/withdraw", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier loanWithdrawnByClient(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate withdrawnOnDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, withdrawnOnDate, comment);
return this.commonRestOperations.withdrawLoan(command);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/disburse", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier disburseLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate disbursalDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, disbursalDate, comment);
return this.commonRestOperations.disburseLoan(command);
}
@RequestMapping(value = "/portfolio/loan/{loanId}/undodisbursal", method = RequestMethod.POST)
public String undoLoanDisbursal(@PathVariable final Long loanId) {
UndoLoanDisbursalCommand command = new UndoLoanDisbursalCommand(loanId);
EntityIdentifier clientAccountId = this.commonRestOperations
.undloLoanDisbursal(command);
return "redirect:/portfolio/client/" + clientAccountId.getEntityId();
}
@RequestMapping(consumes="application/json", produces="application/json", value ="/portfolio/loan/{loanId}/repayment", method = RequestMethod.GET)
public @ResponseBody LoanRepaymentData retrieveNewLoanRepaymentDetail(@PathVariable("loanId") final Long loanId) {
return this.commonRestOperations.retrieveNewLoanRepaymentDetails(loanId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/repayment", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier enterRepaymentOnLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="transactionDate", required=false) String transactionDate,
@RequestParam(value="transactionComment", required=false) String transactionComment,
@RequestParam(value="transactionAmount", required=false) String transactionAmount) {
LocalDate repaymentDate = parseStringToLocalDate(transactionDate, "transactionDate");
BigDecimal repaymentAmount = parseStringToBigDecimal(transactionAmount);
LoanTransactionCommand command = new LoanTransactionCommand(loanId,
repaymentDate, transactionComment, repaymentAmount);
return this.commonRestOperations.makeLoanRepayment(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/loan/{loanId}/repayment/{repaymentId}/adjust", method = RequestMethod.GET)
public @ResponseBody LoanRepaymentData retrieveLoanRepayment(@PathVariable("loanId") final Long loanId, @PathVariable("repaymentId") final Long repaymentId) {
return this.commonRestOperations.retrieveLoanRepaymentDetails(loanId, repaymentId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/repayment/{repaymentId}/adjust", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier adjustPreviousRepaymentOnLoan(
@PathVariable("loanId") final Long loanId, @PathVariable("repaymentId") final Long repaymentId,
@RequestParam(value="transactionDate", required=false) String transactionDate,
@RequestParam(value="transactionComment", required=false) String transactionComment,
@RequestParam(value="transactionAmount", required=false) String transactionAmount) {
LocalDate repaymentDate = parseStringToLocalDate(transactionDate, "transactionDate");
BigDecimal repaymentAmount = parseStringToBigDecimal(transactionAmount);
AdjustLoanTransactionCommand command = new AdjustLoanTransactionCommand(loanId, repaymentId, repaymentDate, transactionComment, repaymentAmount);
return this.commonRestOperations.adjustLoanRepayment(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/loan/{loanId}/waive", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody LoanRepaymentData retrievePossibleWaiveDetails(@PathVariable("loanId") final Long loanId) {
return this.commonRestOperations.retrieveNewLoanWaiverDetails(loanId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/waive", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier waiveLoanAmount(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="transactionDate", required=false) String transactionDate,
@RequestParam(value="transactionComment", required=false) String transactionComment,
@RequestParam(value="transactionAmount", required=false) String transactionAmount) {
LocalDate waiveDate = parseStringToLocalDate(transactionDate, "transactionDate");
BigDecimal waiveAmount = parseStringToBigDecimal(transactionAmount);
LoanTransactionCommand command = new LoanTransactionCommand(loanId,
waiveDate, transactionComment, waiveAmount);
return this.commonRestOperations.waiveLoanAmount(command);
}
}
@@ -0,0 +1,79 @@
package org.mifosng.ui.loan;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class MultiTermExpense implements Serializable {
private String name;
private String description;
private Integer fullTermInMonths;
private Number fullAmount;
private Integer applicableTermInMonths;
private Number applicableAmount;
public MultiTermExpense() {
//
}
public MultiTermExpense(String name, Integer fullTermInMonths, BigDecimal fullAmount, Integer applicableTermInMonths) {
this.name = name;
this.fullTermInMonths = fullTermInMonths;
this.fullAmount = fullAmount;
this.applicableTermInMonths = applicableTermInMonths;
this.applicableAmount = fullAmount;
BigDecimal fullAmountAsDecimal = BigDecimal.valueOf(this.fullAmount.longValue());
if (!fullAmountAsDecimal.equals(BigDecimal.ZERO)) {
this.applicableAmount = fullAmountAsDecimal.divide(BigDecimal.valueOf(applicableTermInMonths.longValue()), RoundingMode.HALF_EVEN);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getFullTermInMonths() {
return fullTermInMonths;
}
public void setFullTermInMonths(Integer fullTermInMonths) {
this.fullTermInMonths = fullTermInMonths;
}
public Number getFullAmount() {
return fullAmount;
}
public void setFullAmount(Number fullAmount) {
this.fullAmount = fullAmount;
}
public Integer getApplicableTermInMonths() {
return applicableTermInMonths;
}
public void setApplicableTermInMonths(Integer applicableTermInMonths) {
this.applicableTermInMonths = applicableTermInMonths;
}
public Number getApplicableAmount() {
return applicableAmount;
}
public void setApplicableAmount(Number applicableAmount) {
this.applicableAmount = applicableAmount;
}
}
@@ -0,0 +1,45 @@
package org.mifosng.ui.loan;
import java.io.Serializable;
import java.math.BigDecimal;
public class OneOffExpense implements Serializable {
private String name;
private String description;
private Number amount;
public OneOffExpense() {
//
}
public OneOffExpense(String name, BigDecimal amount) {
this.name = name;
this.amount = amount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Number getAmount() {
return amount;
}
public void setAmount(Number amount) {
this.amount = amount;
}
}
@@ -0,0 +1,57 @@
package org.mifosng.ui.loan;
import java.io.Serializable;
import java.math.BigDecimal;
public class UnitInformationOfProduct implements Serializable {
private String name;
private Number unitsSold = Integer.valueOf(0);
private Number costPerUnit = BigDecimal.ZERO;
private Number pricePerUnit = BigDecimal.ZERO;
public UnitInformationOfProduct() {
//
}
public UnitInformationOfProduct(String name, Integer unitsSold,
BigDecimal costPerUnit, BigDecimal pricePerUnit) {
this.name = name;
this.unitsSold = unitsSold;
this.costPerUnit = costPerUnit;
this.pricePerUnit = pricePerUnit;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Number getUnitsSold() {
return unitsSold;
}
public void setUnitsSold(Number unitsSold) {
this.unitsSold = unitsSold;
}
public Number getCostPerUnit() {
return costPerUnit;
}
public void setCostPerUnit(Number costPerUnit) {
this.costPerUnit = costPerUnit;
}
public Number getPricePerUnit() {
return pricePerUnit;
}
public void setPricePerUnit(Number pricePerUnit) {
this.pricePerUnit = pricePerUnit;
}
}
@@ -0,0 +1,19 @@
package org.mifosng.ui.loanproduct;
import java.util.List;
import org.mifosng.data.ErrorResponse;
public class ClientValidationException extends RuntimeException {
private final List<ErrorResponse> validationErrors;
public ClientValidationException(List<ErrorResponse> validationErrors) {
super("Validation errors exist");
this.validationErrors = validationErrors;
}
public List<ErrorResponse> getValidationErrors() {
return validationErrors;
}
}
@@ -0,0 +1,193 @@
package org.mifosng.ui.loanproduct;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.ui.CommonRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class LoanProductController {
private final CommonRestOperations commonRestOperations;
@Autowired
public LoanProductController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private BigDecimal parseStringToBigDecimal(String source) {
try {
BigDecimal number = null;
if (StringUtils.isNotBlank(source)) {
String sourceWithoutSpaces = source.replaceAll(" ", "");
Locale locale = LocaleContextHolder.getLocale();
NumberFormat format = NumberFormat.getNumberInstance(locale);
Number parsedNumber = format.parse(sourceWithoutSpaces);
number = BigDecimal.valueOf(Double.valueOf(parsedNumber
.doubleValue()));
}
return number;
} catch (ParseException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.number.format", "amount", source));
throw new ClientValidationException(validationErrors);
}
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/product/loan/all", method = RequestMethod.GET)
public @ResponseBody Collection<LoanProductData> viewAllLoanProducts() {
return this.commonRestOperations.retrieveAllLoanProducts();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/product/loan/new", method = RequestMethod.GET)
public @ResponseBody LoanProductData viewNewLoanProductForm() {
return this.commonRestOperations.retrieveNewLoanProductDetails();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/product/loan/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createLoanProduct(HttpServletRequest request,
@RequestParam("name") String name,
@RequestParam("description") String description,
@RequestParam("selectedCurrency") String currencyCode, @RequestParam("selectedDigitsAfterDecimal") Integer digitsAfterDecimal,
@RequestParam("amount") String principalAsNumber,
@RequestParam("repaidEvery") Integer repaymentEvery,
@RequestParam("selectedRepaymentFrequencyOption") Integer repaymentFrequency,
@RequestParam("numberOfRepayments") Integer numberOfRepayments,
@RequestParam("selectedAmortizationMethodOption") Integer amortizationMethod,
@RequestParam("inArrearsTolerance") String inArrearsToleranceAsNumber,
@RequestParam("nominalInterestRate") String nominalInterestRatePerPeriodAsNumber,
@RequestParam("selectedInterestFrequencyOption") Integer nominalInterestRatePerPeriodFrequency,
@RequestParam("selectedInterestMethodOption") Integer interestMethod
) {
CreateLoanProductCommand command = new CreateLoanProductCommand();
command.setName(name);
command.setDescription(description);
command.setCurrencyCode(currencyCode);
command.setDigitsAfterDecimal(digitsAfterDecimal);
if (StringUtils.isNotBlank(principalAsNumber)) {
BigDecimal principal = parseStringToBigDecimal(principalAsNumber);
command.setPrincipal(principal);
}
command.setRepaymentEvery(repaymentEvery);
command.setRepaymentFrequency(repaymentFrequency);
command.setNumberOfRepayments(numberOfRepayments);
command.setAmortizationMethod(amortizationMethod);
if (StringUtils.isNotBlank(inArrearsToleranceAsNumber)) {
BigDecimal inArrearsToleranceAmount = parseStringToBigDecimal(inArrearsToleranceAsNumber);
command.setInArrearsToleranceAmount(inArrearsToleranceAmount);
}
if (StringUtils.isNotBlank(nominalInterestRatePerPeriodAsNumber)) {
BigDecimal interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRatePerPeriodAsNumber);
command.setInterestRatePerPeriod(interestRatePerPeriod);
}
command.setInterestRateFrequencyMethod(nominalInterestRatePerPeriodFrequency);
command.setInterestMethod(interestMethod);
return this.commonRestOperations.createLoanProduct(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/product/loan/{productId}", method = RequestMethod.GET)
public @ResponseBody LoanProductData viewLoanProduct(@PathVariable("productId") final Long productId) {
return this.commonRestOperations.retrieveLoanProductDetails(productId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/product/loan/{productId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateLoanProduct(@PathVariable("productId") final Long productId, HttpServletRequest request,
@RequestParam("name") String name, @RequestParam("description") String description,
@RequestParam("selectedCurrency") String currencyCode, @RequestParam("selectedDigitsAfterDecimal") Integer digitsAfterDecimal,
@RequestParam("amount") String principalAsNumber,
@RequestParam("repaidEvery") Integer repaymentEvery,
@RequestParam("selectedRepaymentFrequencyOption") Integer repaymentFrequency,
@RequestParam("numberOfRepayments") Integer numberOfRepayments,
@RequestParam("selectedAmortizationMethodOption") Integer amortizationMethod,
@RequestParam("inArrearsTolerance") String inArrearsToleranceAsNumber,
@RequestParam("nominalInterestRate") String nominalInterestRatePerPeriodAsNumber,
@RequestParam("selectedInterestFrequencyOption") Integer nominalInterestRatePerPeriodFrequency,
@RequestParam("selectedInterestMethodOption") Integer interestMethod
) {
UpdateLoanProductCommand command = new UpdateLoanProductCommand();
command.setId(productId);
command.setName(name);
command.setDescription(description);
command.setExternalId(null);
command.setCurrencyCode(currencyCode);
command.setDigitsAfterDecimal(digitsAfterDecimal);
if (StringUtils.isNotBlank(principalAsNumber)) {
BigDecimal principal = parseStringToBigDecimal(principalAsNumber);
command.setPrincipal(principal);
}
command.setRepaymentEvery(repaymentEvery);
command.setRepaymentFrequency(repaymentFrequency);
command.setNumberOfRepayments(numberOfRepayments);
command.setAmortizationMethod(amortizationMethod);
if (StringUtils.isNotBlank(inArrearsToleranceAsNumber)) {
BigDecimal inArrearsToleranceAmount = parseStringToBigDecimal(inArrearsToleranceAsNumber);
command.setInArrearsToleranceAmount(inArrearsToleranceAmount);
}
if (StringUtils.isNotBlank(nominalInterestRatePerPeriodAsNumber)) {
BigDecimal interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRatePerPeriodAsNumber);
command.setInterestRatePerPeriod(interestRatePerPeriod);
}
command.setInterestRateFrequencyMethod(nominalInterestRatePerPeriodFrequency);
command.setInterestMethod(interestMethod);
return this.commonRestOperations.updateLoanProduct(command);
}
}
@@ -0,0 +1,57 @@
package org.mifosng.ui.migration;
import java.net.URI;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth.consumer.client.OAuthRestTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MigrationTriggerController {
private final OAuthRestTemplate oauthRestServiceTemplate;
private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public MigrationTriggerController(final OAuthRestTemplate oauthRestServiceTemplate, ApplicationConfigurationService applicationConfigurationService) {
this.oauthRestServiceTemplate = oauthRestServiceTemplate;
this.applicationConfigurationService = applicationConfigurationService;
}
@RequestMapping(value = "/migration/trigger/clients/creocore", method = RequestMethod.GET)
public String migrateClientInformation() {
URI restUri = URI
.create(getBaseServerUrl().concat("api/protected/import/trigger/clients/creocore"));
this.oauthRestServiceTemplate.getForEntity(restUri, null);
return "redirect:/home";
}
@RequestMapping(value = "/migration/trigger/loans/creocore", method = RequestMethod.GET)
public String migrateLoanInformation() {
URI restUri = URI
.create(getBaseServerUrl().concat("api/protected/import/trigger/loans/creocore"));
this.oauthRestServiceTemplate.getForEntity(restUri, null);
return "redirect:/home";
}
@RequestMapping(value = "/migration/trigger/repayments/creocore", method = RequestMethod.GET)
public String migrateLoanRepaymentsInformation() {
URI restUri = URI
.create(getBaseServerUrl().concat("api/protected/import/trigger/repayments/creocore"));
this.oauthRestServiceTemplate.getForEntity(restUri, null);
return "redirect:/home";
}
private String getBaseServerUrl() {
return this.applicationConfigurationService.retrieveOAuthProviderDetails().getProviderBaseUrl();
}
}
@@ -0,0 +1,128 @@
package org.mifosng.ui.office;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.OfficeData;
import org.mifosng.data.command.OfficeCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class OfficeController {
private final CommonRestOperations commonRestOperations;
@Autowired
public OfficeController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody
Collection<ErrorResponse> validationException(ClientValidationException ex,
HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) {
LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(eventDate)) {
try {
Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale));
} catch (IllegalArgumentException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate));
throw new ClientValidationException(validationErrors);
}
}
return eventLocalDate;
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/org/office/all", method = RequestMethod.GET)
public @ResponseBody Collection<OfficeData> viewAllOffices() {
return this.commonRestOperations.retrieveAllOffices();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/org/office/new", method = RequestMethod.GET)
public @ResponseBody OfficeData viewNewOfficeForm() {
OfficeData newOffice = new OfficeData();
newOffice.setOpeningDate(new LocalDate());
Collection<OfficeData> allOffices = this.commonRestOperations.retrieveAllOffices();
List<OfficeData> allowedParents = new ArrayList<OfficeData>(allOffices);
newOffice.setAllowedParents(allowedParents);
return newOffice;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/org/office/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createOffice(HttpServletRequest request,
@RequestParam("name") String name,
@RequestParam(value="parentId", required=false) Long parentId,
@RequestParam("openingDate") String openingDate,
@RequestParam(value="externalId", required=false) String externalId) {
LocalDate openingLocalDate = parseStringToLocalDate(openingDate, "openingDate");
OfficeCommand command = new OfficeCommand(name, externalId, parentId, openingLocalDate);
return this.commonRestOperations.createOffice(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/org/office/{officeId}", method = RequestMethod.GET)
public @ResponseBody OfficeData viewOffice(@PathVariable("officeId") final Long officeId) {
return this.commonRestOperations.retrieveOffice(officeId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/org/office/{officeId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateOffice(HttpServletRequest request, @PathVariable("officeId") final Long officeId,
@RequestParam("name") String name,
@RequestParam(value="parentId", required=false) Long parentId,
@RequestParam("openingDate") String openingDate,
@RequestParam(value="externalId", required=false) String externalId) {
LocalDate openingLocalDate = parseStringToLocalDate(openingDate, "openingDate");
OfficeCommand command = new OfficeCommand(officeId, name, externalId, parentId, openingLocalDate);
return this.commonRestOperations.updateOffice(command);
}
}
@@ -0,0 +1,77 @@
package org.mifosng.ui.reporting;
import java.security.Principal;
import java.util.Map;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.configuration.OAuthProviderDetails;
import org.mifosng.oauth.ConsumerUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthSecurityContext;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/reporting")
public class ReportingController {
private final ReportingRestOperations reportingRestOperations;
private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public ReportingController(final ReportingRestOperations reportingRestOperations,
ApplicationConfigurationService applicationConfigurationService) {
this.reportingRestOperations = reportingRestOperations;
this.applicationConfigurationService = applicationConfigurationService;
}
@RequestMapping(value = "/flexireport", method = RequestMethod.GET)
public String viewFlexibleReportingPage(final Model model, final Principal principal) {
UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) principal;
ConsumerUserDetails user = (ConsumerUserDetails) authenticationToken.getPrincipal();
if (user.hasNoReportingAuthority()) {
throw new AccessDeniedException("");
}
this.reportingRestOperations.hackToForceAuthentication();
OAuthProviderDetails oauthDetails = this.applicationConfigurationService
.retrieveOAuthProviderDetails();
String resourceId = "protectedMifosNgServices";
OAuthSecurityContext context = OAuthSecurityContextHolder.getContext();
if (context == null) {
throw new IllegalStateException(
"No OAuth security context has been established. Unable to access resource '"
+ resourceId + "'.");
}
Map<String, OAuthConsumerToken> accessTokens = context
.getAccessTokens();
OAuthConsumerToken accessToken = accessTokens == null ? null
: accessTokens.get(resourceId);
model.addAttribute("baseUrl", oauthDetails.getProviderBaseUrl());
model.addAttribute("consumerKey", oauthDetails.getConsumerkey());
model.addAttribute("consumerSecret", oauthDetails.getSharedSecret());
if (accessToken != null) {
model.addAttribute("accessToken", accessToken.getValue());
model.addAttribute("tokenSecret", accessToken.getSecret());
}
return "reports/flexireport";
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
}
@@ -0,0 +1,7 @@
package org.mifosng.ui.reporting;
public interface ReportingRestOperations {
void hackToForceAuthentication();
}
@@ -0,0 +1,53 @@
package org.mifosng.ui.reporting;
import java.net.URI;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.oauth.consumer.client.OAuthRestTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
@Service
public class ReportingRestOperationsImpl implements ReportingRestOperations {
private final OAuthRestTemplate oauthRestServiceTemplate;
private final ApplicationConfigurationService applicationConfigurationService;
@Autowired
public ReportingRestOperationsImpl(
final OAuthRestTemplate oauthRestServiceTemplate, final ApplicationConfigurationService applicationConfigurationService) {
this.oauthRestServiceTemplate = oauthRestServiceTemplate;
this.applicationConfigurationService = applicationConfigurationService;
}
private HttpEntity<Object> emptyRequest() {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(
requestHeaders);
return requestEntity;
}
private String getBaseServerUrl() {
return this.applicationConfigurationService.retrieveOAuthProviderDetails().getProviderBaseUrl();
}
@Override
public void hackToForceAuthentication() {
try {
URI restUri = URI
.create(getBaseServerUrl().concat("api/protected/reporting/forceauth"));
this.oauthRestServiceTemplate.exchange(restUri, HttpMethod.GET, emptyRequest(), null);
} catch (HttpStatusCodeException e) {
System.err.println(e.getMessage());
throw e;
}
}
}
@@ -0,0 +1,151 @@
package org.mifosng.ui.user;
import java.util.Collection;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.data.AppUserData;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.PermissionData;
import org.mifosng.data.RoleData;
import org.mifosng.data.command.RoleCommand;
import org.mifosng.data.command.UserCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class AppUserController {
private final CommonRestOperations commonRestOperations;
@Autowired
public AppUserController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/permission/all", method = RequestMethod.GET)
public @ResponseBody Collection<PermissionData> viewAllPermission() {
return this.commonRestOperations.retrieveAllPermissions();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/role/all", method = RequestMethod.GET)
public @ResponseBody Collection<RoleData> viewAllRoles() {
return this.commonRestOperations.retrieveAllRoles();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/role/new", method = RequestMethod.GET)
public @ResponseBody RoleData newRoleDetails() {
return this.commonRestOperations.retrieveNewRoleDetails();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/admin/role/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createNewRole(@RequestParam(value="selectedItems", required=false) String[] selectedRoles,
@RequestParam(value="name") String name,
@RequestParam(value="description") String description) {
RoleCommand command = new RoleCommand(name, description, selectedRoles);
return this.commonRestOperations.createRole(command);
}
@RequestMapping(consumes = "application/json", produces = "application/json", value = "/admin/role/{roleId}", method = RequestMethod.GET)
public @ResponseBody
RoleData retrieveRoleDetails(@PathVariable("roleId") Long roleId) {
return this.commonRestOperations.retrieveRole(roleId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/admin/role/{roleId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateRole(@PathVariable("roleId") Long roleId,
@RequestParam(value="selectedItems", required=false) String[] selectedRoles,
@RequestParam(value="name") String name,
@RequestParam(value="description") String description) {
RoleCommand command = new RoleCommand(name, description, selectedRoles);
command.setId(roleId);
return this.commonRestOperations.updateRole(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/user/all", method = RequestMethod.GET)
public @ResponseBody Collection<AppUserData> viewAllUsers() {
return this.commonRestOperations.retrieveAllUsers();
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/user/new", method = RequestMethod.GET)
public @ResponseBody AppUserData viewUser() {
return this.commonRestOperations.retrieveNewUserDetails();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/admin/user/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createNewUser(@RequestParam(value="selectedItems", required=false) String[] selectedRoles,
@RequestParam(value="username") String username,
@RequestParam(value="firstname") String firstname,
@RequestParam(value="lastname") String lastname,
@RequestParam(value="email") String email,
@RequestParam(value="officeId") Long officeId) {
UserCommand command = new UserCommand(username, firstname, lastname, "", email, selectedRoles, officeId);
return this.commonRestOperations.createUser(command);
}
@RequestMapping(consumes = "application/json", produces = "application/json", value = "/admin/user/{userId}", method = RequestMethod.DELETE)
public @ResponseBody
EntityIdentifier attemptToDeleteUser(@PathVariable("userId") Long userId) {
this.commonRestOperations.deleteUser(userId);
return new EntityIdentifier(Long.valueOf(1));
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/admin/user/{userId}", method = RequestMethod.GET)
public @ResponseBody AppUserData viewUser(@PathVariable("userId") Long userId) {
return this.commonRestOperations.retrieveUser(userId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/admin/user/{userId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateUser(@PathVariable("userId") Long userId,
@RequestParam(value="selectedItems", required=false) String[] selectedRoles,
@RequestParam(value="username") String username,
@RequestParam(value="firstname") String firstname,
@RequestParam(value="lastname") String lastname,
@RequestParam(value="email") String email,
@RequestParam(value="officeId") Long officeId) {
UserCommand command = new UserCommand(username, firstname, lastname, "", email, selectedRoles, officeId);
command.setId(userId);
return this.commonRestOperations.updateUser(command);
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
}
@@ -0,0 +1,159 @@
package org.mifosng.ui.user;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.reports.GenericResultset;
import org.mifosng.data.reports.ResultsetColumnHeader;
import org.mifosng.data.reports.ResultsetDataRow;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ExportCSVController {
private final static Logger logger = LoggerFactory.getLogger(ExportCSVController.class);
private final CommonRestOperations commonRestOperations;
@Autowired
public ExportCSVController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
@RequestMapping(value = "/exportcsv*", method = RequestMethod.GET)
public void handleExportCSV(final HttpServletRequest request, HttpServletResponse response) throws IOException {
@SuppressWarnings("unchecked")
Map<String, String[]> queryParams = request.getParameterMap();
String rptDB = queryParams.get("MRP_rptDB")[0];
String name = queryParams.get("MRP_Name")[0];
String type = queryParams.get("MRP_Type")[0];
Set<String> keys = queryParams.keySet();
Map<String, String> extractedQueryParams = new HashMap<String, String>();
String pKey;
String pValue;
for (String k : keys) {
if (k.startsWith("MRP_")) {
pKey = "${" + k.substring(4) + "}";
pValue = queryParams.get(k)[0];
// logger.info(name + ": " + pKey + ": " + pValue);
extractedQueryParams.put(pKey, pValue);
}
}
// if (extractedQueryParams.size() > 3) {
// throw new UnsupportedOperationException("Not able to support this report for export to CSV right now.");
// }
GenericResultset result = this.commonRestOperations.retrieveReportingData(rptDB, name, type, extractedQueryParams);
// response.setContentType("application/octet-stream");
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition",
"attachment;filename=" + name.replaceAll(" ", "") + ".csv");
ServletOutputStream out = response.getOutputStream();
StringBuffer sb = generateCsvFileBuffer(result);
InputStream in = new ByteArrayInputStream(sb.toString().getBytes(
"UTF-8"));
byte[] outputByte = new byte[4096];
Integer readLen = in.read(outputByte, 0, 4096);
while (readLen != -1) {
out.write(outputByte, 0, readLen);
readLen = in.read(outputByte, 0, 4096);
}
in.close();
out.flush();
out.close();
}
private static StringBuffer generateCsvFileBuffer(GenericResultset result) {
StringBuffer writer = new StringBuffer();
List<ResultsetColumnHeader> columnHeaders = result.getColumnHeaders();
logger.info("NO. of Columns: " + columnHeaders.size());
Integer chSize = columnHeaders.size();
for (int i = 0; i < chSize; i++) {
writer.append('"' + columnHeaders.get(i).getColumnName() + '"');
if (i < (chSize - 1))
writer.append(",");
}
writer.append('\n');
List<ResultsetDataRow> data = result.getData();
List<String> row;
Integer rSize;
// String currCol;
String currColType;
String currVal;
logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
row = data.get(i).getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
// currCol = columnHeaders.get(j).getColumnName();
currColType = columnHeaders.get(j).getColumnType();
currVal = row.get(j);
if (currVal != null) {
if (currColType.equals("DECIMAL")
|| currColType.equals("DOUBLE")
|| currColType.equals("BIGINT")
|| currColType.equals("SMALLINT")
|| currColType.equals("INT"))
writer.append(currVal);
else
writer.append('"' + currVal + '"');
}
if (j < (rSize - 1))
writer.append(",");
}
writer.append('\n');
}
return writer;
}
}