diff --git a/mifosng-provider/build.gradle b/mifosng-provider/build.gradle
index d17d54a32..4ff144f12 100644
--- a/mifosng-provider/build.gradle
+++ b/mifosng-provider/build.gradle
@@ -33,6 +33,7 @@ targetCompatibility = JavaVersion.VERSION_1_7
project.ext.springBootVersion = '1.1.6.RELEASE'
project.ext.springVersion = '4.0.7.RELEASE'
+project.ext.springOauthVersion = '2.0.4.RELEASE'
project.ext.jerseyVersion = '1.17'
project.ext.springDataJpaVersion = '1.7.0.RELEASE' // also change spring-boot-gradle-plugin version above
@@ -64,6 +65,21 @@ if (project.hasProperty('env') && project.getProperty('env') == 'dev') {
apply from: 'dependencies.gradle'
}
+/* Enable Oauth2 authentication based on environment, default to HTTP basic auth */
+if (project.hasProperty('security') && project.getProperty('security') == 'oauth') {
+ copy {
+ from './properties/oauth/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+} else {
+ copy {
+ from './properties/basicauth/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+}
+
task dist(type:Zip){
baseName = 'mifosplatform'
version = qualifyVersionIfNecessary(releaseVersion)
diff --git a/mifosng-provider/dependencies.gradle b/mifosng-provider/dependencies.gradle
index 19bf90bdd..cd7273207 100644
--- a/mifosng-provider/dependencies.gradle
+++ b/mifosng-provider/dependencies.gradle
@@ -21,6 +21,8 @@ dependencies {
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
+
+ [group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion],
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
diff --git a/mifosng-provider/dev-dependencies.gradle b/mifosng-provider/dev-dependencies.gradle
index aa3aa9a7e..05dd8dc6f 100644
--- a/mifosng-provider/dev-dependencies.gradle
+++ b/mifosng-provider/dev-dependencies.gradle
@@ -21,6 +21,7 @@ dependencies {
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
+ [group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion],
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
diff --git a/mifosng-provider/properties/basicauth/application.properties b/mifosng-provider/properties/basicauth/application.properties
new file mode 100644
index 000000000..4a1ef88aa
--- /dev/null
+++ b/mifosng-provider/properties/basicauth/application.properties
@@ -0,0 +1,7 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+spring.profiles.default=basicauth
\ No newline at end of file
diff --git a/mifosng-provider/properties/oauth/application.properties b/mifosng-provider/properties/oauth/application.properties
new file mode 100644
index 000000000..6d05eafa6
--- /dev/null
+++ b/mifosng-provider/properties/oauth/application.properties
@@ -0,0 +1,8 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+spring.profiles.default=basicauth
+spring.profiles.active=oauth
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/AbstractApplicationConfiguration.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/AbstractApplicationConfiguration.java
index 5c210dc39..72b280cf1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/AbstractApplicationConfiguration.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/AbstractApplicationConfiguration.java
@@ -26,7 +26,7 @@ import org.springframework.context.annotation.ImportResource;
* and MariaDB4j (because those differ in the subclasses).
*/
@Configuration
-@Import({ WebXmlConfiguration.class, WebFrontEndConfiguration.class })
+@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class })
@ImportResource({ "classpath*:META-INF/spring/appContext.xml" })
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlConfiguration.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlConfiguration.java
index 0d89388d8..31f58c6c0 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlConfiguration.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlConfiguration.java
@@ -15,6 +15,7 @@ import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
import org.springframework.web.filter.DelegatingFilterProxy;
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
@@ -26,6 +27,7 @@ import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
* href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot">#howto-convert-an-existing-application-to-spring-boot
*/
@Configuration
+@Profile("basicauth")
public class WebXmlConfiguration {
@Autowired
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlOauthConfiguration.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlOauthConfiguration.java
new file mode 100644
index 000000000..386dc129b
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/boot/WebXmlOauthConfiguration.java
@@ -0,0 +1,61 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.mifosplatform.infrastructure.core.boot;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.springframework.boot.context.embedded.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.web.filter.DelegatingFilterProxy;
+import org.springframework.web.servlet.DispatcherServlet;
+
+import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
+
+/**
+ * This Configuration replaces what formerly was in web.xml. Beans are loaded only when "oauth" Profile is enabled.
+ *
+ * @see
#howto-convert-an-existing-application-to-spring-boot
+ */
+@Configuration
+@Profile("oauth")
+public class WebXmlOauthConfiguration {
+
+
+ @Bean
+ public Filter springSecurityFilterChain() {
+ return new DelegatingFilterProxy();
+ }
+
+ @Bean
+ public ServletRegistrationBean jersey() {
+ Servlet jerseyServlet = new SpringServlet();
+ ServletRegistrationBean jerseyServletRegistration = new ServletRegistrationBean();
+ jerseyServletRegistration.setServlet(jerseyServlet);
+ jerseyServletRegistration.addUrlMappings("/api/v1/*");
+ jerseyServletRegistration.setName("jersey-servlet");
+ jerseyServletRegistration.setLoadOnStartup(1);
+ jerseyServletRegistration.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
+// jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
+// ResponseCorsFilter.class.getName());
+ jerseyServletRegistration.addInitParameter("com.sun.jersey.config.feature.DisableWADL", "true");
+ // debugging for development:
+ // jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
+ // LoggingFilter.class.getName());
+ return jerseyServletRegistration;
+ }
+
+ @Bean
+ public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
+ ServletRegistrationBean registrationBean = new ServletRegistrationBean(dispatcherServlet);
+ registrationBean.addUrlMappings("/api/oauth/token");
+ return registrationBean;
+ }
+
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/AuthenticationApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/AuthenticationApiResource.java
index 25648f6e8..e8a00a8ce 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/AuthenticationApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/AuthenticationApiResource.java
@@ -24,6 +24,7 @@ import org.mifosplatform.useradministration.domain.AppUser;
import org.mifosplatform.useradministration.domain.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -35,6 +36,7 @@ import com.sun.jersey.core.util.Base64;
@Path("/authentication")
@Component
+@Profile("basicauth")
@Scope("singleton")
public class AuthenticationApiResource {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/UserDetailsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/UserDetailsApiResource.java
new file mode 100644
index 000000000..ebdd15552
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/api/UserDetailsApiResource.java
@@ -0,0 +1,98 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.mifosplatform.infrastructure.security.api;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.mifosplatform.infrastructure.core.data.EnumOptionData;
+import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.mifosplatform.infrastructure.security.data.AuthenticatedOauthUserData;
+import org.mifosplatform.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
+import org.mifosplatform.useradministration.data.RoleData;
+import org.mifosplatform.useradministration.domain.AppUser;
+import org.mifosplatform.useradministration.domain.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Scope;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
+import org.springframework.stereotype.Component;
+
+/*
+ * Implementation of Oauth2 authentication APIs, loaded only when "oauth" profile is enabled.
+ */
+@Path("/userdetails")
+@Component
+@Profile("oauth")
+@Scope("singleton")
+public class UserDetailsApiResource {
+
+ private final ResourceServerTokenServices tokenServices;
+ private final ToApiJsonSerializer
apiJsonSerializerService;
+ private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
+
+ @Autowired
+ public UserDetailsApiResource(@Qualifier("tokenServices") final ResourceServerTokenServices tokenServices,
+ final ToApiJsonSerializer apiJsonSerializerService,
+ final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
+ this.tokenServices = tokenServices;
+ this.apiJsonSerializerService = apiJsonSerializerService;
+ this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
+ }
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String fetchAuthenticatedUserData(@QueryParam("access_token") final String accessToken) {
+
+ final Authentication authentication = this.tokenServices.loadAuthentication(accessToken);
+ if (authentication.isAuthenticated()) {
+ final AppUser principal = (AppUser) authentication.getPrincipal();
+
+ final Collection permissions = new ArrayList<>();
+ AuthenticatedOauthUserData authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), permissions);
+
+ final Collection authorities = new ArrayList<>(authentication.getAuthorities());
+ for (final GrantedAuthority grantedAuthority : authorities) {
+ permissions.add(grantedAuthority.getAuthority());
+ }
+
+ final Collection roles = new ArrayList<>();
+ final Set userRoles = principal.getRoles();
+ for (final Role role : userRoles) {
+ roles.add(role.toData());
+ }
+
+ final Long officeId = principal.getOffice().getId();
+ final String officeName = principal.getOffice().getName();
+
+ final Long staffId = principal.getStaffId();
+ final String staffDisplayName = principal.getStaffDisplayName();
+
+ final EnumOptionData organisationalRole = principal.organisationalRoleData();
+
+ if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
+ authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), principal.getId(), accessToken);
+ } else {
+
+ authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, staffId, staffDisplayName,
+ organisationalRole, roles, permissions, principal.getId(), accessToken);
+ }
+ return this.apiJsonSerializerService.serialize(authenticatedUserData);
+ }
+ return null;
+
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/data/AuthenticatedOauthUserData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/data/AuthenticatedOauthUserData.java
new file mode 100644
index 000000000..b6c900102
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/data/AuthenticatedOauthUserData.java
@@ -0,0 +1,90 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.mifosplatform.infrastructure.security.data;
+
+import java.util.Collection;
+
+import org.mifosplatform.infrastructure.core.data.EnumOptionData;
+import org.mifosplatform.useradministration.data.RoleData;
+
+/**
+ * Immutable data object for authentication. Used in case of Oauth2.
+ */
+public class AuthenticatedOauthUserData {
+
+ @SuppressWarnings("unused")
+ private final String username;
+ @SuppressWarnings("unused")
+ private final Long userId;
+ @SuppressWarnings("unused")
+ private final String accessToken;
+ @SuppressWarnings("unused")
+ private final boolean authenticated;
+ @SuppressWarnings("unused")
+ private final Long officeId;
+ @SuppressWarnings("unused")
+ private final String officeName;
+ @SuppressWarnings("unused")
+ private final Long staffId;
+ @SuppressWarnings("unused")
+ private final String staffDisplayName;
+ @SuppressWarnings("unused")
+ private final EnumOptionData organisationalRole;
+ @SuppressWarnings("unused")
+ private final Collection roles;
+ @SuppressWarnings("unused")
+ private final Collection permissions;
+
+ @SuppressWarnings("unused")
+ private final boolean shouldRenewPassword;
+
+ public AuthenticatedOauthUserData(final String username, final Collection permissions) {
+ this.username = username;
+ this.userId = null;
+ this.accessToken = null;
+ this.authenticated = false;
+ this.officeId = null;
+ this.officeName = null;
+ this.staffId = null;
+ this.staffDisplayName = null;
+ this.organisationalRole = null;
+ this.roles = null;
+ this.permissions = permissions;
+ this.shouldRenewPassword = false;
+ }
+
+ public AuthenticatedOauthUserData(final String username, final Long officeId, final String officeName, final Long staffId,
+ final String staffDisplayName, final EnumOptionData organisationalRole, final Collection roles,
+ final Collection permissions, final Long userId, final String accessToken) {
+ this.username = username;
+ this.officeId = officeId;
+ this.officeName = officeName;
+ this.staffId = staffId;
+ this.staffDisplayName = staffDisplayName;
+ this.organisationalRole = organisationalRole;
+ this.userId = userId;
+ this.accessToken = accessToken;
+ this.authenticated = true;
+ this.roles = roles;
+ this.permissions = permissions;
+ this.shouldRenewPassword = false;
+ }
+
+ public AuthenticatedOauthUserData(final String username, final Long userId, final String accessToken) {
+ this.username = username;
+ this.officeId = null;
+ this.officeName = null;
+ this.staffId = null;
+ this.staffDisplayName = null;
+ this.organisationalRole = null;
+ this.userId = userId;
+ this.accessToken = accessToken;
+ this.authenticated = true;
+ this.roles = null;
+ this.permissions = null;
+ this.shouldRenewPassword = true;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
index 1e4bb9aba..a95b9c96d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
@@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.security.service.BasicAuthTenantDetailsS
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -49,6 +50,7 @@ import org.springframework.stereotype.Service;
* is returned.
*/
@Service(value = "basicAuthenticationProcessingFilter")
+@Profile("basicauth")
public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter {
private static boolean firstRequestProcessed = false;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
new file mode 100644
index 000000000..017776240
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
@@ -0,0 +1,148 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.mifosplatform.infrastructure.security.filter;
+
+import java.io.IOException;
+
+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.time.StopWatch;
+import org.mifosplatform.infrastructure.cache.domain.CacheType;
+import org.mifosplatform.infrastructure.cache.service.CacheWritePlatformService;
+import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
+import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
+import org.mifosplatform.infrastructure.security.data.PlatformRequestLog;
+import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException;
+import org.mifosplatform.infrastructure.security.service.BasicAuthTenantDetailsService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.filter.GenericFilterBean;
+
+/**
+ *
+ * This filter is responsible for extracting multi-tenant from the request and
+ * setting Cross-Origin details to response.
+ *
+ * If multi-tenant are valid, the details of the tenant are stored in
+ * {@link MifosPlatformTenant} and stored in a {@link ThreadLocal} variable for
+ * this request using {@link ThreadLocalContextUtil}.
+ *
+ * If multi-tenant are invalid, a http error response is returned.
+ *
+ * Used to support Oauth2 authentication and the service is loaded only when "oauth" profile is active.
+ */
+@Service(value = "tenantIdentifierProcessingFilter")
+@Profile("oauth")
+public class TenantAwareTenantIdentifierFilter extends GenericFilterBean {
+
+ private static boolean firstRequestProcessed = false;
+ private final static Logger logger = LoggerFactory.getLogger(TenantAwareTenantIdentifierFilter.class);
+
+ private final BasicAuthTenantDetailsService basicAuthTenantDetailsService;
+ private final ToApiJsonSerializer toApiJsonSerializer;
+ private final ConfigurationDomainService configurationDomainService;
+ private final CacheWritePlatformService cacheWritePlatformService;
+
+ private final String tenantRequestHeader = "X-Mifos-Platform-TenantId";
+ private final boolean exceptionIfHeaderMissing = true;
+ private final String apiUri = "/api/v1/";
+
+ @Autowired
+ public TenantAwareTenantIdentifierFilter(final BasicAuthTenantDetailsService basicAuthTenantDetailsService,
+ final ToApiJsonSerializer toApiJsonSerializer, final ConfigurationDomainService configurationDomainService,
+ final CacheWritePlatformService cacheWritePlatformService) {
+ this.basicAuthTenantDetailsService = basicAuthTenantDetailsService;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.configurationDomainService = configurationDomainService;
+ this.cacheWritePlatformService = cacheWritePlatformService;
+ }
+
+ @Override
+ public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
+
+ final HttpServletRequest request = (HttpServletRequest) req;
+ final HttpServletResponse response = (HttpServletResponse) res;
+
+ final StopWatch task = new StopWatch();
+ task.start();
+
+ try {
+
+ // allows for Cross-Origin
+ // Requests (CORs) to be performed against the platform API.
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ final String reqHead = request.getHeader("Access-Control-Request-Headers");
+
+ if (null != reqHead && !reqHead.equals(null)) {
+ response.setHeader("Access-Control-Allow-Headers", reqHead);
+ }
+
+ if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
+
+ String tenantIdentifier = request.getHeader(this.tenantRequestHeader);
+ if (org.apache.commons.lang.StringUtils.isBlank(tenantIdentifier)) {
+ tenantIdentifier = request.getParameter("tenantIdentifier");
+ }
+
+ if (tenantIdentifier == null && this.exceptionIfHeaderMissing) { throw new InvalidTenantIdentiferException(
+ "No tenant identifier found: Add request header of '" + this.tenantRequestHeader
+ + "' or add the parameter 'tenantIdentifier' to query string of request URL."); }
+
+ String pathInfo = request.getRequestURI();
+ boolean isReportRequest = false;
+ if (pathInfo != null && pathInfo.contains("report")) {
+ isReportRequest = true;
+ }
+ final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier, isReportRequest);
+
+ ThreadLocalContextUtil.setTenant(tenant);
+ String authToken = request.getHeader("Authorization");
+
+ if (authToken != null && authToken.startsWith("bearer ")) {
+ ThreadLocalContextUtil.setAuthToken(authToken.replaceFirst("bearer ", ""));
+ }
+
+ if (!firstRequestProcessed) {
+ final String baseUrl = request.getRequestURL().toString()
+ .replace(request.getRequestURI(), request.getContextPath() + apiUri);
+ System.setProperty("baseUrl", baseUrl);
+
+ final boolean ehcacheEnabled = this.configurationDomainService.isEhcacheEnabled();
+ if (ehcacheEnabled) {
+ this.cacheWritePlatformService.switchToCache(CacheType.SINGLE_NODE);
+ } else {
+ this.cacheWritePlatformService.switchToCache(CacheType.NO_CACHE);
+ }
+ TenantAwareTenantIdentifierFilter.firstRequestProcessed = true;
+ }
+ chain.doFilter(request, response);
+ }
+ } catch (final InvalidTenantIdentiferException e) {
+ // deal with exception at low level
+ SecurityContextHolder.getContext().setAuthentication(null);
+
+ response.addHeader("WWW-Authenticate", "Basic realm=\"" + "Mifos Platform API" + "\"");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
+ } finally {
+ task.stop();
+ final PlatformRequestLog log = PlatformRequestLog.from(task, request);
+ logger.info(this.toApiJsonSerializer.serialize(log));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml
index 136a536e3..abb47a67e 100644
--- a/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml
+++ b/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml
@@ -11,55 +11,210 @@
You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+ http://www.springframework.org/schema/security/oauth2
+ http://www.springframework.org/schema/security/spring-security-oauth2.xsd
+ http://www.springframework.org/schema/security
+ http://www.springframework.org/schema/security/spring-security-3.2.xsd">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mifosng-provider/src/main/resources/application.properties b/mifosng-provider/src/main/resources/application.properties
new file mode 100644
index 000000000..4a1ef88aa
--- /dev/null
+++ b/mifosng-provider/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+spring.profiles.default=basicauth
\ No newline at end of file
diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V273__oauth_changes.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V273__oauth_changes.sql
new file mode 100644
index 000000000..6197606e9
--- /dev/null
+++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V273__oauth_changes.sql
@@ -0,0 +1,43 @@
+
+CREATE TABLE `oauth_client_details` (
+ `client_id` varchar(128) NOT NULL,
+ `resource_ids` varchar(256) DEFAULT NULL,
+ `client_secret` varchar(256) DEFAULT NULL,
+ `scope` varchar(256) DEFAULT NULL,
+ `authorized_grant_types` varchar(256) DEFAULT NULL,
+ `web_server_redirect_uri` varchar(256) DEFAULT NULL,
+ `authorities` varchar(256) DEFAULT NULL,
+ `access_token_validity` int(11) DEFAULT NULL,
+ `refresh_token_validity` int(11) DEFAULT NULL,
+ `additional_information` varchar(4096) DEFAULT NULL,
+ `autoapprove` BIT(1) NULL DEFAULT NULL,
+ PRIMARY KEY (`client_id`)
+)
+COLLATE='utf8_general_ci'
+ENGINE=InnoDB
+AUTO_INCREMENT=1;
+
+INSERT INTO `oauth_client_details` (`client_id`, `client_secret`, `scope`, `authorized_grant_types`) VALUES ('community-app', '123', 'all', 'password,refresh_token');
+
+
+CREATE TABLE `oauth_access_token` (
+ `token_id` varchar(256) DEFAULT NULL,
+ `token` blob,
+ `authentication_id` varchar(256) DEFAULT NULL,
+ `user_name` varchar(256) DEFAULT NULL,
+ `client_id` varchar(256) DEFAULT NULL,
+ `authentication` blob,
+ `refresh_token` varchar(256) DEFAULT NULL
+)
+COLLATE='utf8_general_ci'
+ENGINE=InnoDB
+AUTO_INCREMENT=1;
+
+CREATE TABLE `oauth_refresh_token` (
+ `token_id` varchar(256) DEFAULT NULL,
+ `token` blob,
+ `authentication` blob
+)
+COLLATE='utf8_general_ci'
+ENGINE=InnoDB;
+
diff --git a/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java
index c618c3260..ec575b78b 100644
--- a/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java
+++ b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java
@@ -14,12 +14,15 @@ import org.mifosplatform.infrastructure.configuration.spring.TestsWithoutDatabas
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
+@ActiveProfiles("basicauth")
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class CommandHandlerProviderTest {
diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/spring/SpringConfigurationTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/spring/SpringConfigurationTest.java
index e926d21f5..fdb2e5199 100644
--- a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/spring/SpringConfigurationTest.java
+++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/spring/SpringConfigurationTest.java
@@ -7,6 +7,7 @@ package org.mifosplatform.infrastructure.configuration.spring;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@@ -25,6 +26,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
+@ActiveProfiles("basicauth")
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class SpringConfigurationTest {
diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/boot/tests/AbstractSpringBootWithMariaDB4jIntegrationTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/boot/tests/AbstractSpringBootWithMariaDB4jIntegrationTest.java
index 5d3160208..93642c22d 100644
--- a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/boot/tests/AbstractSpringBootWithMariaDB4jIntegrationTest.java
+++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/boot/tests/AbstractSpringBootWithMariaDB4jIntegrationTest.java
@@ -9,12 +9,14 @@ import org.junit.runner.RunWith;
import org.mifosplatform.ServerWithMariaDB4jApplication;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ServerWithMariaDB4jApplication.Configuration.class)
@WebAppConfiguration
+@ActiveProfiles("basicauth")
@IntegrationTest({ "server.port=0", "management.port=0", "mariaDB4j.port=0", "mariaDB4j.dataDir=null" })
public abstract class AbstractSpringBootWithMariaDB4jIntegrationTest {