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 1339cc580ee..045e916fcfd 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 @@ -253,6 +253,11 @@ powermock-api-mockito test + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + test + diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticator.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticator.java index 99fd36d5342..7f9a8bb54c4 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticator.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/main/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticator.java @@ -86,6 +86,12 @@ public class JWTAuthenticator implements WebappAuthenticator { @Override public AuthenticationInfo authenticate(Request request, Response response) { String requestUri = request.getRequestURI(); + SignedJWT jwsObject; + String username; + String tenantDomain; + int tenantId; + String issuer; + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); if (requestUri == null || "".equals(requestUri)) { authenticationInfo.setStatus(Status.CONTINUE); @@ -101,12 +107,17 @@ public class JWTAuthenticator implements WebappAuthenticator { try { String authorizationHeader = request.getHeader(JWT_ASSERTION_HEADER); + jwsObject = SignedJWT.parse(authorizationHeader); + username = jwsObject.getJWTClaimsSet().getStringClaim(SIGNED_JWT_AUTH_USERNAME); + tenantDomain = MultitenantUtils.getTenantDomain(username); + tenantId = Integer.parseInt(jwsObject.getJWTClaimsSet().getStringClaim(SIGNED_JWT_AUTH_TENANT_ID)); + issuer = jwsObject.getJWTClaimsSet().getIssuer(); + } catch (ParseException e) { + log.error("Error occurred while parsing JWT header.", e); + return null; + } + try { - SignedJWT jwsObject = SignedJWT.parse(authorizationHeader); - String username = jwsObject.getJWTClaimsSet().getStringClaim(SIGNED_JWT_AUTH_USERNAME); - String tenantDomain = MultitenantUtils.getTenantDomain(username); - int tenantId = Integer.parseInt(jwsObject.getJWTClaimsSet().getStringClaim(SIGNED_JWT_AUTH_TENANT_ID)); - String issuer = jwsObject.getJWTClaimsSet().getIssuer(); PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId); @@ -116,7 +127,7 @@ public class JWTAuthenticator implements WebappAuthenticator { loadTenantRegistry(tenantId); KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { - String alias = properties.getProperty(issuer); + String alias = properties == null ? null : properties.getProperty(issuer); if (alias != null && !alias.isEmpty()) { ServerConfiguration serverConfig = CarbonUtils.getServerConfiguration(); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); @@ -139,10 +150,12 @@ public class JWTAuthenticator implements WebappAuthenticator { publicKeyHolder.put(issuerAlias, publicKey); } } - //Get the filesystem keystore default primary certificate - JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) publicKey); - if (jwsObject.verify(verifier)) { + JWSVerifier verifier = null; + if (publicKey != null) { + verifier = new RSASSAVerifier((RSAPublicKey) publicKey); + } + if (verifier != null && jwsObject.verify(verifier)) { username = MultitenantUtils.getTenantAwareUsername(username); if (tenantId == -1) { log.error("tenantDomain is not valid. username : " + username + ", tenantDomain " + @@ -162,9 +175,7 @@ public class JWTAuthenticator implements WebappAuthenticator { } } catch (UserStoreException e) { log.error("Error occurred while obtaining the user.", e); - } catch (ParseException e) { - log.error("Error occurred while parsing the JWT header.", e); - } catch (Exception e) { + } catch (Exception e) { log.error("Error occurred while verifying the JWT header.", e); } finally { PrivilegedCarbonContext.endTenantFlow(); diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/BaseWebAppAuthenticatorFrameworkTest.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/BaseWebAppAuthenticatorFrameworkTest.java index d4bcaf2e200..fc437004a00 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/BaseWebAppAuthenticatorFrameworkTest.java +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/BaseWebAppAuthenticatorFrameworkTest.java @@ -21,18 +21,25 @@ package org.wso2.carbon.webapp.authenticator.framework; import org.testng.annotations.BeforeSuite; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.internal.CarbonCoreDataHolder; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; import org.wso2.carbon.device.mgt.core.permission.mgt.PermissionUtils; +import org.wso2.carbon.registry.core.config.RegistryContext; import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.core.internal.RegistryDataHolder; import org.wso2.carbon.registry.core.jdbc.realm.InMemoryRealmService; +import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.user.api.Permission; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder; +import org.wso2.carbon.webapp.authenticator.framework.util.TestTenantIndexingLoader; +import org.wso2.carbon.webapp.authenticator.framework.util.TestTenantRegistryLoader; import java.io.File; +import java.io.InputStream; import java.net.URL; import static org.wso2.carbon.security.SecurityConstants.ADMIN_USER; @@ -56,6 +63,17 @@ public class BaseWebAppAuthenticatorFrameworkTest { .setTenantDomain(org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext() .setTenantId(org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_ID); + CarbonCoreDataHolder.getInstance().setRegistryService(getRegistryService()); + AuthenticatorFrameworkDataHolder.getInstance().setTenantRegistryLoader(new TestTenantRegistryLoader()); + AuthenticatorFrameworkDataHolder.getInstance().setTenantIndexingLoader(new TestTenantIndexingLoader()); + } + + /** + * To get the registry service. + * @return RegistryService + * @throws RegistryException Registry Exception + */ + private RegistryService getRegistryService() throws RegistryException, UserStoreException { RealmService realmService = new InMemoryRealmService(); AuthenticatorFrameworkDataHolder.getInstance().setRealmService(realmService); UserStoreManager userStoreManager = AuthenticatorFrameworkDataHolder.getInstance().getRealmService() @@ -63,5 +81,12 @@ public class BaseWebAppAuthenticatorFrameworkTest { Permission adminPermission = new Permission(PermissionUtils.ADMIN_PERMISSION_REGISTRY_PATH, CarbonConstants.UI_PERMISSION_ACTION); userStoreManager.addRole(ADMIN_ROLE + "t", new String[] { ADMIN_USER }, new Permission[] { adminPermission }); + RegistryDataHolder.getInstance().setRealmService(realmService); + DeviceManagementDataHolder.getInstance().setRealmService(realmService); + InputStream is = BaseWebAppAuthenticatorFrameworkTest.class.getClassLoader() + .getResourceAsStream("carbon-home/repository/conf/registry.xml"); + RegistryContext context = RegistryContext.getBaseInstance(is, realmService); + context.setSetup(true); + return context.getEmbeddedRegistryService(); } } diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticatorTest.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticatorTest.java new file mode 100644 index 00000000000..5ea9b1f88bd --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/authenticator/JWTAuthenticatorTest.java @@ -0,0 +1,109 @@ +package org.wso2.carbon.webapp.authenticator.framework.authenticator; + +import org.apache.catalina.connector.Request; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.MimeHeaders; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.identity.jwt.client.extension.dto.JWTConfig; +import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException; +import org.wso2.carbon.identity.jwt.client.extension.util.JWTClientUtil; +import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; +import org.wso2.carbon.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder; +import org.wso2.carbon.webapp.authenticator.framework.util.TestTenantIndexingLoader; +import org.wso2.carbon.webapp.authenticator.framework.util.TestTenantRegistryLoader; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class JWTAuthenticatorTest { + private JWTAuthenticator jwtAuthenticator; + private Field headersField; + private final String JWT_HEADER = "X-JWT-Assertion"; + private String jwtToken; + private static final String SIGNED_JWT_AUTH_USERNAME = "http://wso2.org/claims/enduser"; + private static final String SIGNED_JWT_AUTH_TENANT_ID = "http://wso2.org/claims/enduserTenantId"; + private Properties properties; + private final String ISSUER = "wso2.org/products/iot"; + private final String ALIAS = "wso2carbon"; + + @BeforeClass + public void setup() throws NoSuchFieldException, IOException, JWTClientException { + jwtAuthenticator = new JWTAuthenticator(); + properties = new Properties(); + properties.setProperty(ISSUER, ALIAS); + jwtAuthenticator.setProperties(properties); + headersField = org.apache.coyote.Request.class.getDeclaredField("headers"); + headersField.setAccessible(true); + ClassLoader classLoader = getClass().getClassLoader(); + URL resourceUrl = classLoader.getResource("jwt.properties"); + File jwtPropertyFile; + JWTConfig jwtConfig = null; + + if (resourceUrl != null) { + jwtPropertyFile = new File(resourceUrl.getFile()); + Properties jwtConfigProperties = new Properties(); + jwtConfigProperties.load(new FileInputStream(jwtPropertyFile)); + jwtConfig = new JWTConfig(jwtConfigProperties); + } + + Map customClaims = new HashMap<>(); + customClaims.put(SIGNED_JWT_AUTH_USERNAME, "admin"); + customClaims.put(SIGNED_JWT_AUTH_TENANT_ID, String.valueOf(MultitenantConstants.SUPER_TENANT_ID)); + jwtToken = JWTClientUtil.generateSignedJWTAssertion("admin", jwtConfig, false, customClaims); + } + + @Test(description = "This method tests the get methods in the JWTAuthenticator") + public void testGetMethods() { + Assert.assertEquals(jwtAuthenticator.getName(), "JWT", "GetName method returns wrong value"); + Assert.assertNotNull(jwtAuthenticator.getProperties(), "Properties are not properly added to JWT " + + "Authenticator"); + Assert.assertEquals(jwtAuthenticator.getProperties().size(), properties.size(), + "Added properties do not match with retrieved properties"); + Assert.assertNull(jwtAuthenticator.getProperty("test"), "Retrieved a propety that was never added"); + Assert.assertNotNull(jwtAuthenticator.getProperty(ISSUER), ALIAS); + } + + @Test(description = "This method tests the canHandle method under different conditions of request") + public void testHandle() throws IllegalAccessException, NoSuchFieldException { + Request request = new Request(); + org.apache.coyote.Request coyoteRequest = new org.apache.coyote.Request(); + request.setCoyoteRequest(coyoteRequest); + Assert.assertFalse(jwtAuthenticator.canHandle(request)); + MimeHeaders mimeHeaders = new MimeHeaders(); + MessageBytes bytes = mimeHeaders.addValue(JWT_HEADER); + bytes.setString("test"); + headersField.set(coyoteRequest, mimeHeaders); + request.setCoyoteRequest(coyoteRequest); + Assert.assertTrue(jwtAuthenticator.canHandle(request)); + } + + @Test(description = "This method tests authenticate method under the successful condition") + public void testAuthenticate() throws IllegalAccessException, NoSuchFieldException { + Request request = new Request(); + org.apache.coyote.Request coyoteRequest = new org.apache.coyote.Request(); + MimeHeaders mimeHeaders = new MimeHeaders(); + MessageBytes bytes = mimeHeaders.addValue(JWT_HEADER); + bytes.setString(jwtToken); + headersField.set(coyoteRequest, mimeHeaders); + Field uriMB = org.apache.coyote.Request.class.getDeclaredField("uriMB"); + uriMB.setAccessible(true); + bytes = MessageBytes.newInstance(); + bytes.setString("test"); + uriMB.set(coyoteRequest, bytes); + request.setCoyoteRequest(coyoteRequest); + + AuthenticationInfo authenticationInfo = jwtAuthenticator.authenticate(request, null); + Assert.assertNotNull(authenticationInfo.getUsername(), "Proper authentication request is not properly " + + "authenticated by the JWTAuthenticator"); + } +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantIndexingLoader.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantIndexingLoader.java new file mode 100644 index 00000000000..12203c35d83 --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantIndexingLoader.java @@ -0,0 +1,9 @@ +package org.wso2.carbon.webapp.authenticator.framework.util; + +import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader; + +public class TestTenantIndexingLoader implements TenantIndexingLoader { + @Override public void loadTenantIndex(int i) { + + } +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantRegistryLoader.java b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantRegistryLoader.java new file mode 100644 index 00000000000..1656e91bc08 --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/java/org/wso2/carbon/webapp/authenticator/framework/util/TestTenantRegistryLoader.java @@ -0,0 +1,11 @@ +package org.wso2.carbon.webapp.authenticator.framework.util; + +import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.core.service.TenantRegistryLoader; + +public class TestTenantRegistryLoader implements TenantRegistryLoader { + @Override + public void loadTenantRegistry(int i) throws RegistryException { + + } +} diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/client-truststore.jks b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/client-truststore.jks new file mode 100644 index 00000000000..3b9fdfb9e84 Binary files /dev/null and b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/client-truststore.jks differ diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/wso2carbon.jks b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/wso2carbon.jks new file mode 100644 index 00000000000..b4b6220baec Binary files /dev/null and b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/carbon-home/repository/resources/security/wso2carbon.jks differ diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/jwt.properties b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/jwt.properties new file mode 100644 index 00000000000..b7be2e296ac --- /dev/null +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/src/test/resources/jwt.properties @@ -0,0 +1,57 @@ +# +# Copyright (c) 2016, 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. +# + +#issuer of the JWT +iss=wso2.org/products/iot + +TokenEndpoint=https://${iot.gateway.host}:${iot.gateway.https.port}/token?tenantDomain=carbon.super + +#audience of JWT claim +#comma seperated values +aud=devicemgt + +#expiration time of JWT (number of minutes from the current time) +exp=1000 + +#issued at time of JWT (number of minutes from the current time) +iat=0 + +#nbf time of JWT (number of minutes from current time) +nbf=0 + +#skew between IDP and issuer(seconds) +skew=0 + +# JWT Id +#jti=token123 + +#KeyStore to cryptographic credentials +KeyStore=target/test-classes/carbon-home/repository/resources/security/wso2carbon.jks + +#Password of the KeyStore +KeyStorePassword=wso2carbon + +#Alias of the SP's private key +PrivateKeyAlias=wso2carbon + +#Private key password to retrieve the private key used to sign +#AuthnRequest and LogoutRequest messages +PrivateKeyPassword=wso2carbon + +#this will be used as the default IDP config if there isn't any config available for tenants. +default-jwt-client=false 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 d166d4b15ec..21430713793 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 @@ -31,6 +31,7 @@ +