diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml index 2d2ba8214b..5cb2b2a9b0 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml @@ -86,6 +86,7 @@ org.wso2.carbon.utils, org.wso2.carbon.utils.multitenancy, org.xml.sax, + com.google.gson.*, javax.servlet, javax.servlet.http, javax.xml, @@ -215,6 +216,10 @@ org.wso2.carbon.devicemgt org.wso2.carbon.device.mgt.common + + com.google.code.gson + gson + org.wso2.orbit.org.apache.httpcomponents httpclient diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/AuthenticationFrameworkUtil.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/AuthenticationFrameworkUtil.java index f3e0a4fe88..7c506737e7 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/AuthenticationFrameworkUtil.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/AuthenticationFrameworkUtil.java @@ -22,6 +22,13 @@ import org.apache.catalina.connector.Response; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.user.api.UserRealm; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; +import org.wso2.carbon.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; @@ -32,6 +39,7 @@ import java.io.IOException; public class AuthenticationFrameworkUtil { private static final Log log = LogFactory.getLog(AuthenticationFrameworkUtil.class); + private static final String UI_EXECUTE = "ui.execute"; static void handleResponse(Request request, Response response, int statusCode, String payload) { response.setStatus(statusCode); @@ -65,4 +73,43 @@ public class AuthenticationFrameworkUtil { } } + static boolean isUserAuthorized(int tenantId, String tenantDomain, String username, String + permission) throws + AuthenticationException { + boolean tenantFlowStarted = false; + + try{ + //If this is a tenant user + if(tenantId != MultitenantConstants.SUPER_TENANT_ID){ + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(username); + tenantFlowStarted = true; + } + + RealmService realmService = AuthenticatorFrameworkDataHolder.getInstance().getRealmService(); + if (realmService == null) { + String msg = "RealmService is not initialized"; + log.error(msg); + throw new AuthenticationException(msg); + } + UserRealm userRealm = realmService.getTenantUserRealm(tenantId); + + return userRealm.getAuthorizationManager() + .isUserAuthorized(MultitenantUtils + .getTenantAwareUsername(username), permission, UI_EXECUTE); + + } catch (UserStoreException e) { + String msg = "Error while getting username"; + log.error(msg, e); + throw new AuthenticationException(msg, e); + } + finally { + if (tenantFlowStarted) { + PrivilegedCarbonContext.endTenantFlow(); + } + } + } + } diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Constants.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Constants.java index af69a320ea..2694ea069a 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Constants.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/Constants.java @@ -22,7 +22,7 @@ public final class Constants { public static final String AUTHORIZATION_HEADER_PREFIX_BEARER = "Bearer"; public static final String NO_MATCHING_AUTH_SCHEME = "noMatchedAuthScheme"; - public static final String PROXY_TENANT_ID = "ProxyTenantId"; + public static final String PROXY_TENANT_ID = "Proxy-Tenant-Id"; public static final class HTTPHeaders { private HTTPHeaders() { diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValve.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValve.java index 16bf4d695e..1370482cd2 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValve.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValve.java @@ -18,9 +18,11 @@ */ package org.wso2.carbon.webapp.authenticator.framework; +import com.google.gson.Gson; import org.apache.catalina.Context; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.owasp.encoder.Encode; @@ -42,12 +44,15 @@ public class WebappAuthenticationValve extends CarbonTomcatValve { private static final Log log = LogFactory.getLog(WebappAuthenticationValve.class); private static TreeMap nonSecuredEndpoints = new TreeMap<>(); + private static final String PERMISSION_PREFIX = "/permission/admin"; + public static final String AUTHORIZE_PERMISSION = "Authorize-Permission"; @Override public void invoke(Request request, Response response, CompositeValve compositeValve) { - if (this.isContextSkipped(request) || this.skipAuthentication(request)) { - this.getNext().invoke(request, response, compositeValve); + if ((this.isContextSkipped(request) || this.skipAuthentication(request)) + && (StringUtils.isEmpty(request.getHeader(AUTHORIZE_PERMISSION)))) { + this.getNext().invoke(request, response, compositeValve); return; } @@ -64,6 +69,39 @@ public class WebappAuthenticationValve extends CarbonTomcatValve { authenticationInfo.setStatus(status); } + // This section will allow to validate a given access token is authenticated to access given + // resource(permission) + if (request.getCoyoteRequest() != null + && StringUtils.isNotEmpty(request.getHeader(AUTHORIZE_PERMISSION)) + && (authenticationInfo.getStatus() == WebappAuthenticator.Status.CONTINUE || + authenticationInfo.getStatus() == WebappAuthenticator.Status.SUCCESS)) { + boolean isAllowed; + try { + isAllowed = AuthenticationFrameworkUtil.isUserAuthorized( + authenticationInfo.getTenantId(), authenticationInfo.getTenantDomain(), + authenticationInfo.getUsername(), + PERMISSION_PREFIX + request.getHeader (AUTHORIZE_PERMISSION)); + } catch (AuthenticationException e) { + String msg = "Could not authorize permission"; + log.error(msg); + AuthenticationFrameworkUtil.handleResponse(request, response, + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); + return; + } + + if (isAllowed) { + Gson gson = new Gson(); + AuthenticationFrameworkUtil.handleResponse(request, response, HttpServletResponse.SC_OK, + gson.toJson(authenticationInfo)); + return; + } else { + log.error("Unauthorized message from user " + authenticationInfo.getUsername()); + AuthenticationFrameworkUtil.handleResponse(request, response, + HttpServletResponse.SC_FORBIDDEN, "Unauthorized to access the API"); + return; + } + } + Tenant tenant = null; if (authenticationInfo.getTenantId() != -1) { try { @@ -72,7 +110,8 @@ public class WebappAuthenticationValve extends CarbonTomcatValve { privilegedCarbonContext.setTenantId(authenticationInfo.getTenantId()); privilegedCarbonContext.setTenantDomain(authenticationInfo.getTenantDomain()); privilegedCarbonContext.setUsername(authenticationInfo.getUsername()); - if (authenticationInfo.isSuperTenantAdmin()) { + if (authenticationInfo.isSuperTenantAdmin() && request.getHeader(Constants + .PROXY_TENANT_ID) != null) { // If this is a call from super admin to an API and the ProxyTenantId is also // present, this is a call that is made with super admin credentials to call // an API on behalf of another tenant. Hence the actual tenants, details are diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValveTest.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValveTest.java index 69cbac221a..91dfad24ca 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValveTest.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticationValveTest.java @@ -55,8 +55,9 @@ public class WebappAuthenticationValveTest { @Test(description = "This method tests the invoke method of the WebAppAuthenticationValve with the context path " + "starting with carbon") - public void testInvokeWithContextSkippedScenario1() { + public void testInvokeWithContextSkippedScenario1() throws NoSuchFieldException, IllegalAccessException { Request request = new Request(); + getCoyoteRequest(request); Context context = new StandardContext(); context.setPath("carbon"); CompositeValve compositeValve = Mockito.mock(CompositeValve.class); @@ -64,6 +65,7 @@ public class WebappAuthenticationValveTest { request.setContext(context); webappAuthenticationValve.invoke(request, null, compositeValve); request = new TestRequest("", "test"); + getCoyoteRequest(request); context = new StandardContext(); compositeValve = Mockito.mock(CompositeValve.class); Mockito.doNothing().when(compositeValve).continueInvocation(Mockito.any(), Mockito.any()); @@ -73,8 +75,9 @@ public class WebappAuthenticationValveTest { @Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when " + "un-secured endpoints are invoked.") - public void testInvokeUnSecuredEndpoints() { + public void testInvokeUnSecuredEndpoints() throws IllegalAccessException, NoSuchFieldException { Request request = new TestRequest("", "test"); + getCoyoteRequest(request); Context context = new StandardContext(); context.setPath("carbon1"); context.addParameter("doAuthentication", String.valueOf(true)); @@ -85,6 +88,22 @@ public class WebappAuthenticationValveTest { webappAuthenticationValve.invoke(request, null, compositeValve); } + private void getCoyoteRequest(Request request) throws + IllegalAccessException, + NoSuchFieldException { + + Field headersField = org.apache.coyote.Request.class.getDeclaredField("headers"); + headersField.setAccessible(true); + org.apache.coyote.Request coyoteRequest = new org.apache.coyote.Request(); + + MimeHeaders mimeHeaders = new MimeHeaders(); + MessageBytes bytes = mimeHeaders.addValue("content-type"); + bytes.setString("test"); + + headersField.set(coyoteRequest, mimeHeaders); + request.setCoyoteRequest(coyoteRequest); + } + @Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when " + "secured endpoints are invoked.") public void testInvokeSecuredEndpoints() throws NoSuchFieldException, IllegalAccessException {