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 204806ea0c..c142783445 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 @@ -111,7 +111,14 @@ org.apache.commons.pool, org.apache.commons.pool.impl, org.apache.http.conn, - org.apache.http.impl.conn + org.apache.http.impl.conn, + javax.xml.soap, + javax.xml.stream, + org.apache.axiom.soap, + org.apache.axiom.soap.impl.builder, + org.apache.axiom.om, + org.apache.axiom.om.impl.builder, + org.apache.axiom.om.util diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticatorRepository.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticatorRepository.java index bb805c8c76..d7f1b5fccd 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticatorRepository.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/WebappAuthenticatorRepository.java @@ -33,6 +33,13 @@ public class WebappAuthenticatorRepository { } public void addAuthenticator(WebappAuthenticator authenticator) { + if (authenticator == null) { + throw new IllegalStateException("Authenticator implementation to be added to the webapp " + + "authenticator repository cannot be null or empty"); + } + if (authenticator.getName() == null || authenticator.getName().isEmpty()) { + throw new IllegalStateException("Authenticator name cannot be null or empty"); + } authenticators.put(authenticator.getName(), authenticator); } diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BSTAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BSTAuthenticator.java new file mode 100644 index 0000000000..fdd03b8b6f --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/BSTAuthenticator.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.webapp.authenticator.framework.authenticator; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.impl.builder.StAXBuilder; +import org.apache.axiom.om.util.StAXUtils; +import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axiom.soap.SOAPHeader; +import org.apache.axiom.soap.SOAPHeaderBlock; +import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.coyote.InputBuffer; +import org.apache.tomcat.util.buf.ByteChunk; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationFrameworkUtil; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; +import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuth2TokenValidator; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuthTokenValidationException; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuthValidationResponse; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuthValidatorFactory; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.*; + +public class BSTAuthenticator implements WebappAuthenticator { + + private Properties properties; + private OAuth2TokenValidator tokenValidator; + private static final List APPLICABLE_CONTENT_TYPES = new ArrayList<>(); + private static final Log log = LogFactory.getLog(BSTAuthenticator.class); + + static { + APPLICABLE_CONTENT_TYPES.add("application/xml"); + APPLICABLE_CONTENT_TYPES.add("application/soap+xml"); + } + + public void init() { + if (this.properties == null) { + throw new IllegalArgumentException("Required properties needed to initialize OAuthAuthenticator " + + "are not provided"); + } + + String url = this.properties.getProperty("TokenValidationEndpointUrl"); + if ((url == null) || (url.isEmpty())) { + throw new IllegalArgumentException("OAuth token validation endpoint url is not provided"); + } + String adminUsername = this.properties.getProperty("Username"); + if (adminUsername == null) { + throw new IllegalArgumentException("Username to connect to the OAuth token validation endpoint " + + "is not provided"); + } + + String adminPassword = this.properties.getProperty("Password"); + if (adminPassword == null) { + throw new IllegalArgumentException("Password to connect to the OAuth token validation endpoint " + + "is not provided"); + } + + boolean isRemote = Boolean.parseBoolean(this.properties.getProperty("IsRemote")); + + Properties validatorProperties = new Properties(); + validatorProperties.setProperty("MaxTotalConnections", this.properties.getProperty("MaxTotalConnections")); + validatorProperties.setProperty("MaxConnectionsPerHost", this.properties.getProperty("MaxConnectionsPerHost")); + this.tokenValidator = + OAuthValidatorFactory.getValidator(url, adminUsername, adminPassword, isRemote, validatorProperties); + } + + @Override + public boolean canHandle(Request request) { + String contentType = request.getContentType(); + if (contentType.contains("application/xml") || contentType.contains("application/soap+xml") || + contentType.contains("application/text")) { + try { + return isBSTHeaderExists(request); + } catch (IOException | XMLStreamException e) { + log.error("Error occurred while checking if BST authenticator can handle the incoming SOAP message"); + } + } + return false; + } + + @Override + public AuthenticationInfo authenticate(Request request, Response response) { + String requestUri = request.getRequestURI(); + String requestMethod = request.getMethod(); + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + if ((requestUri == null) || ("".equals(requestUri))) { + authenticationInfo.setStatus(WebappAuthenticator.Status.CONTINUE); + return authenticationInfo; + } + + StringTokenizer tokenizer = new StringTokenizer(requestUri, "/"); + String context = tokenizer.nextToken(); + if ((context == null) || ("".equals(context))) { + authenticationInfo.setStatus(WebappAuthenticator.Status.CONTINUE); + } + String apiVersion = tokenizer.nextToken(); + + String authLevel = "any"; + try { + if ("noMatchedAuthScheme".equals(authLevel)) { + AuthenticationFrameworkUtil.handleNoMatchAuthScheme( + request, response, requestMethod, apiVersion, context); + + authenticationInfo.setStatus(WebappAuthenticator.Status.CONTINUE); + } else { + String bearerToken = request.getContext().findParameter("BST"); + + String resource = requestUri + ":" + requestMethod; + + OAuthValidationResponse oAuthValidationResponse = + this.tokenValidator.validateToken(bearerToken, resource); + + if (oAuthValidationResponse.isValid()) { + String username = oAuthValidationResponse.getUserName(); + String tenantDomain = oAuthValidationResponse.getTenantDomain(); + + authenticationInfo.setUsername(username); + authenticationInfo.setTenantDomain(tenantDomain); + authenticationInfo.setTenantId(Utils.getTenantIdOFUser(username + "@" + tenantDomain)); + if (oAuthValidationResponse.isValid()) + authenticationInfo.setStatus(WebappAuthenticator.Status.CONTINUE); + } else { + authenticationInfo.setMessage(oAuthValidationResponse.getErrorMsg()); + } + } + } catch (AuthenticationException e) { + log.error("Failed to authenticate the incoming request", e); + } catch (OAuthTokenValidationException e) { + log.error("Failed to authenticate the incoming request due to oauth token validation error.", e); + } + return authenticationInfo; + } + + @Override + public String getName() { + return "BSTAuthenticator"; + } + + @Override + public void setProperties(Properties properties) { + this.properties = properties; + } + + @Override + public Properties getProperties() { + return properties; + } + + @Override + public String getProperty(String name) { + return properties.getProperty(name); + } + + private static byte[] getUTF8Bytes(String soapEnvelope) { + byte[] bytes; + try { + bytes = soapEnvelope.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error("Unable to extract bytes in UTF-8 encoding. " + + "Extracting bytes in the system default encoding" + + e.getMessage()); + bytes = soapEnvelope.getBytes(); + } + return bytes; + } + + private boolean isBSTHeaderExists(Request request) throws IOException, XMLStreamException { + String bstHeader = this.getBSTHeader(request); + if (bstHeader == null || bstHeader.isEmpty()) { + return false; + } + request.getContext().addParameter("BST", bstHeader); + return true; + } + + private String getBSTHeader(Request request) throws IOException, XMLStreamException { + org.apache.coyote.Request coyoteReq = request.getCoyoteRequest(); + InputBuffer buf = coyoteReq.getInputBuffer(); + ByteChunk bc = new ByteChunk(); + + buf.doRead(bc, coyoteReq); + try (InputStream is = new ByteArrayInputStream(getUTF8Bytes(bc.toString()))) { + XMLStreamReader reader = StAXUtils.createXMLStreamReader(is); + StAXBuilder builder = new StAXSOAPModelBuilder(reader); + SOAPEnvelope envelope = (SOAPEnvelope) builder.getDocumentElement(); + envelope.build(); + + SOAPHeader header = envelope.getHeader(); + Iterator headerEls = header.getChildrenWithLocalName("Security"); + if (!headerEls.hasNext()) { + return null; + } + OMElement securityHeader = (OMElement) headerEls.next(); + Iterator securityHeaderEls = securityHeader.getChildrenWithLocalName("BinarySecurityToken"); + if (!securityHeaderEls.hasNext()) { + return null; + } + OMElement bstHeader = (OMElement) securityHeaderEls.next(); + bstHeader.build(); + return bstHeader.getText(); + } + } + +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/WebappAuthenticatorRepositoryTest.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/WebappAuthenticatorRepositoryTest.java new file mode 100644 index 0000000000..edb094b3e3 --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/WebappAuthenticatorRepositoryTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.webapp.authenticator.framework.test; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.wso2.carbon.webapp.authenticator.framework.WebappAuthenticatorRepository; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator; +import org.wso2.carbon.webapp.authenticator.framework.test.util.MalformedAuthenticator; +import org.wso2.carbon.webapp.authenticator.framework.test.util.TestWebappAuthenticator; + +public class WebappAuthenticatorRepositoryTest { + + @Test + public void testAddAuthenticator() { + WebappAuthenticatorRepository repository = new WebappAuthenticatorRepository(); + + WebappAuthenticator addedAuthenticator = new TestWebappAuthenticator(); + repository.addAuthenticator(addedAuthenticator); + + WebappAuthenticator retriedAuthenticator = repository.getAuthenticator(addedAuthenticator.getName()); + Assert.assertEquals(addedAuthenticator.getName(), retriedAuthenticator.getName()); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testAddMalformedAuthenticator() { + WebappAuthenticatorRepository repository = new WebappAuthenticatorRepository(); + WebappAuthenticator malformedAuthenticator = new MalformedAuthenticator(); + repository.addAuthenticator(malformedAuthenticator); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testAddAuthenticatorWithNull() { + WebappAuthenticatorRepository repository = new WebappAuthenticatorRepository(); + repository.addAuthenticator(null); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testAddAuthenticatorWithEmptyString() { + WebappAuthenticatorRepository repository = new WebappAuthenticatorRepository(); + repository.addAuthenticator(null); + } + +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/MalformedAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/MalformedAuthenticator.java new file mode 100644 index 0000000000..cfa6afe6de --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/MalformedAuthenticator.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.webapp.authenticator.framework.test.util; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator; + +import java.util.Properties; + +public class MalformedAuthenticator implements WebappAuthenticator { + + + @Override + public void init() { + + } + + @Override + public boolean canHandle(Request request) { + return false; + } + + @Override + public AuthenticationInfo authenticate(Request request, Response response) { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public void setProperties(Properties properties) { + + } + + @Override + public Properties getProperties() { + return null; + } + + @Override + public String getProperty(String name) { + return null; + } + +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/TestWebappAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/TestWebappAuthenticator.java new file mode 100644 index 0000000000..aebe1bbcc6 --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/test/util/TestWebappAuthenticator.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.webapp.authenticator.framework.test.util; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; +import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator; + +import java.util.Properties; + +public class TestWebappAuthenticator implements WebappAuthenticator { + + private static final String NAME = "TestAuthenticator"; + + @Override + public void init() { + + } + + @Override + public boolean canHandle(Request request) { + return false; + } + + @Override + public AuthenticationInfo authenticate(Request request, Response response) { + return null; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void setProperties(Properties properties) { + + } + + @Override + public Properties getProperties() { + return null; + } + + @Override + public String getProperty(String name) { + return null; + } + +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/testng.xml b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/testng.xml index 8b9832e2e6..a73f1df2d3 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/testng.xml +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/testng.xml @@ -34,4 +34,10 @@ + + + + + + diff --git a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/src/main/resources/conf/webapp-authenticator-config.xml b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/src/main/resources/conf/webapp-authenticator-config.xml index 0f3c278b34..963406e2af 100644 --- a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/src/main/resources/conf/webapp-authenticator-config.xml +++ b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/src/main/resources/conf/webapp-authenticator-config.xml @@ -24,5 +24,17 @@ CertificateAuth org.wso2.carbon.webapp.authenticator.framework.authenticator.CertificateAuthenticator + + OAuth + org.wso2.carbon.webapp.authenticator.framework.authenticator.BSTAuthenticator + + false + https://localhost:9443 + admin + admin + 100 + 100 + +