merging jwt client extension component

4.x.x
prabathabey 9 years ago
parent 43926b64b4
commit 3fa17c12a7

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>identity-extensions</artifactId>
<version>1.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.identity.jwt.client.extension</artifactId>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Device Management JWT Client Extensions</name>
<description>WSO2 Carbon - Device Management JWT Client Extensions</description>
<url>http://wso2.org</url>
<dependencies>
<dependency>
<groupId>org.wso2.carbon.governance</groupId>
<artifactId>org.wso2.carbon.governance.api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.core</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.org.scannotation</groupId>
<artifactId>scannotation</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.tomcat</groupId>
<artifactId>tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2</artifactId>
</dependency>
<dependency>
<groupId>commons-lang.wso2</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.analytics</groupId>
<artifactId>org.wso2.carbon.analytics.api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.registry</groupId>
<artifactId>org.wso2.carbon.registry.indexing</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple.wso2</groupId>
<artifactId>json-simple</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.wso2</groupId>
<artifactId>httpcore</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Bundle-Version>${carbon.device.mgt.version}</Bundle-Version>
<Bundle-Description>Device Management JWT Client Bundle</Bundle-Description>
<Private-Package>org.wso2.carbon.device.mgt.jwt.client.extension.internal</Private-Package>
<Export-Package>
!org.wso2.carbon.device.mgt.jwt.client.extension.internal,
org.wso2.carbon.device.mgt.jwt.client.extension.*
</Export-Package>
<Import-Package>
org.osgi.framework,
org.osgi.service.component,
org.wso2.carbon.governance.api.*,
org.wso2.carbon.context,
org.wso2.carbon.registry.core,
org.wso2.carbon.registry.core.exceptions,
org.wso2.carbon.registry.core.session,
org.wso2.carbon.utils,
org.apache.commons.logging,
org.wso2.carbon.registry.core.*;resolution:=optional,
org.wso2.carbon.registry.common.*;version="${carbon.registry.imp.pkg.version.range}",
org.wso2.carbon.registry.indexing.*; version="${carbon.registry.imp.pkg.version.range}",
com.nimbusds.jwt.*;version="${nimbus.orbit.version.range}",
com.nimbusds.jose.*;version="${nimbus.orbit.version.range}",
javax.net.ssl,
org.apache.commons.codec.binary,
org.apache.commons.io,
org.apache.http;version="${httpclient.version.range}",
org.apache.http.client;version="${httpclient.version.range}",
org.apache.http.message;version="${httpclient.version.range}",
org.apache.http.client;version="${httpclient.version.range}",
org.apache.http.impl;version="${httpclient.version.range}",
org.apache.http.conn.*;version="${httpclient.version.range}",
org.apache.http.util;version="${httpclient.version.range}",
org.apache.http.client.entity;version="${httpclient.version.range}",
org.apache.http.client.methods;version="${httpclient.version.range}",
org.apache.http.impl.client;version="${httpclient.version.range}",
org.json.simple.*,
org.wso2.carbon.core.util,
javax.net.ssl,
org.wso2.carbon.user.core.service,
org.wso2.carbon.user.core.tenant
</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,248 @@
/*
* 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.
*/
package org.wso2.carbon.device.mgt.jwt.client.extension;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.device.mgt.jwt.client.extension.constant.JWTConstants;
import org.wso2.carbon.device.mgt.jwt.client.extension.dto.AccessTokenInfo;
import org.wso2.carbon.device.mgt.jwt.client.extension.dto.JWTConfig;
import org.wso2.carbon.device.mgt.jwt.client.extension.exception.JWTClientException;
import org.wso2.carbon.device.mgt.jwt.client.extension.util.JWTClientUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
* this class represents an implementation of Token Client which is based on JWT
*/
public class JWTClient {
private static Log log = LogFactory.getLog(JWTClient.class);
private static final String JWT_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
private static final String GRANT_TYPE_PARAM_NAME = "grant_type";
private static final String REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
private static final String REFRESH_TOKEN_GRANT_TYPE_PARAM_NAME = "refresh_token";
private static final String JWT_PARAM_NAME = "assertion";
private static final String SCOPE_PARAM_NAME = "scope";
private JWTConfig jwtConfig;
public JWTClient(JWTConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
/**
* {@inheritDoc}
*/
public AccessTokenInfo getAccessToken(String consumerKey, String consumerSecret, String username, String scopes)
throws JWTClientException {
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair(GRANT_TYPE_PARAM_NAME, JWT_GRANT_TYPE));
String assertion = generateSignedJWTAssertion(username);
if (assertion == null) {
throw new JWTClientException("JWT is not configured properly for user : " + username);
}
params.add(new BasicNameValuePair(JWT_PARAM_NAME, assertion));
params.add(new BasicNameValuePair(SCOPE_PARAM_NAME, scopes));
return getTokenInfo(params, consumerKey, consumerSecret);
}
/**
* {@inheritDoc}
*/
public AccessTokenInfo getAccessTokenFromRefreshToken(String refreshToken, String username, String scopes,
String consumerKey, String consumerSecret)
throws JWTClientException {
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair(GRANT_TYPE_PARAM_NAME, REFRESH_TOKEN_GRANT_TYPE));
params.add(new BasicNameValuePair(REFRESH_TOKEN_GRANT_TYPE_PARAM_NAME, refreshToken));
params.add(new BasicNameValuePair(SCOPE_PARAM_NAME, scopes));
return getTokenInfo(params, consumerKey, consumerSecret);
}
private AccessTokenInfo getTokenInfo(List<NameValuePair> nameValuePairs, String consumerKey, String consumerSecret)
throws JWTClientException {
String response = null;
try {
if (jwtConfig == null) {
return null;
}
URL tokenEndpoint = new URL(jwtConfig.getTokenEndpoint());
HttpClient httpClient = JWTClientUtil.getHttpClient(tokenEndpoint.getProtocol());
HttpPost postMethod = new HttpPost(tokenEndpoint.toString());
postMethod.setEntity(new UrlEncodedFormEntity(nameValuePairs));
postMethod.addHeader("Authorization", "Basic " + getBase64Encode(consumerKey, consumerSecret));
postMethod.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpResponse httpResponse = httpClient.execute(postMethod);
response = JWTClientUtil.getResponseString(httpResponse);
if (log.isDebugEnabled()) {
log.debug(response);
}
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(response);
AccessTokenInfo accessTokenInfo = new AccessTokenInfo();
accessTokenInfo.setAccess_token((String) jsonObject.get(JWTConstants.OAUTH_ACCESS_TOKEN));
accessTokenInfo.setRefresh_token((String) jsonObject.get(JWTConstants.OAUTH_REFRESH_TOKEN));
accessTokenInfo.setExpires_in((Long) jsonObject.get(JWTConstants.OAUTH_EXPIRES_IN));
accessTokenInfo.setToken_type((String) jsonObject.get(JWTConstants.OAUTH_TOKEN_TYPE));
return accessTokenInfo;
} catch (MalformedURLException e) {
throw new JWTClientException("Invalid URL for token endpoint " + jwtConfig.getTokenEndpoint(), e);
} catch (ParseException e) {
throw new JWTClientException("Error when parsing the response " + response, e);
} catch (IOException e) {
throw new JWTClientException("Error when reading the response from buffer.", e);
} catch (NoSuchAlgorithmException e) {
throw new JWTClientException("No such algorithm found when loading the ssl socket", e);
} catch (KeyStoreException e) {
throw new JWTClientException("Failed loading the keystore.", e);
} catch (KeyManagementException e) {
throw new JWTClientException("Failed setting up the ssl http client.", e);
}
}
private String getBase64Encode(String consumerKey, String consumerSecret) {
return new String(Base64.encodeBase64((consumerKey + ":" + consumerSecret).getBytes()));
}
public String generateSignedJWTAssertion(String username) throws JWTClientException {
try {
String subject = username;
long currentTimeMillis = System.currentTimeMillis();
// add the skew between servers
String iss = jwtConfig.getIssuer();
if (iss == null || iss.isEmpty()) {
return null;
}
currentTimeMillis += jwtConfig.getSkew();
long iat = currentTimeMillis + jwtConfig.getIssuedInternal() * 60 * 1000;
long exp = currentTimeMillis + jwtConfig.getExpirationTime() * 60 * 1000;
long nbf = currentTimeMillis + jwtConfig.getValidityPeriodFromCurrentTime() * 60 * 1000;
String jti = jwtConfig.getJti();
if (jti == null) {
String defaultTokenId = currentTimeMillis + "" + new Random().nextInt();
jti = defaultTokenId;
}
List<String> aud = jwtConfig.getAudiences();
//set up the basic claims
JWTClaimsSet claimsSet = new JWTClaimsSet();
claimsSet.setIssueTime(new Date(iat));
claimsSet.setExpirationTime(new Date(exp));
claimsSet.setIssuer(iss);
claimsSet.setSubject(username);
claimsSet.setNotBeforeTime(new Date(nbf));
claimsSet.setJWTID(jti);
claimsSet.setAudience(aud);
// get Keystore params
String keyStorePath = jwtConfig.getKeyStorePath();
String privateKeyAlias = jwtConfig.getPrivateKeyAlias();
String privateKeyPassword = jwtConfig.getPrivateKeyPassword();
KeyStore keyStore;
RSAPrivateKey rsaPrivateKey;
if (keyStorePath != null && !keyStorePath.isEmpty()) {
String keyStorePassword = jwtConfig.getKeyStorePassword();
keyStore = loadKeyStore(new File(keyStorePath), keyStorePassword, "JKS");
rsaPrivateKey = (RSAPrivateKey) keyStore.getKey(privateKeyAlias, privateKeyPassword.toCharArray());
} else {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
KeyStoreManager tenantKeyStoreManager = KeyStoreManager.getInstance(tenantId);
rsaPrivateKey = (RSAPrivateKey) tenantKeyStoreManager.getDefaultPrivateKey();
}
JWSSigner signer = new RSASSASigner(rsaPrivateKey);
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet);
signedJWT.sign(signer);
String assertion = signedJWT.serialize();
return assertion;
} catch (KeyStoreException e) {
throw new JWTClientException("Failed loading the keystore.", e);
} catch (IOException e) {
throw new JWTClientException("Failed parsing the keystore file.", e);
} catch (NoSuchAlgorithmException e) {
throw new JWTClientException("No such algorithm found RS256.", e);
} catch (CertificateException e) {
throw new JWTClientException("Failed loading the certificate from the keystore.", e);
} catch (UnrecoverableKeyException e) {
throw new JWTClientException("Failed loading the keys from the keystore.", e);
} catch (JOSEException e) {
throw new JWTClientException(e);
} catch (Exception e) {
//This is thrown when loading default private key.
throw new JWTClientException("Failed loading the private key.", e);
}
}
private KeyStore loadKeyStore(final File keystoreFile, final String password, final String keyStoreType)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null");
}
URI keystoreUri = keystoreFile.toURI();
URL keystoreUrl = keystoreUri.toURL();
KeyStore keystore = KeyStore.getInstance(keyStoreType);
InputStream is = null;
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray());
} finally {
if (null != is) {
is.close();
}
}
return keystore;
}
}

@ -0,0 +1,136 @@
/*
* 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.
*/
package org.wso2.carbon.device.mgt.jwt.client.extension;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.jwt.client.extension.dto.JWTConfig;
import org.wso2.carbon.device.mgt.jwt.client.extension.exception.JWTClientAlreadyExistsException;
import org.wso2.carbon.device.mgt.jwt.client.extension.exception.JWTClientConfigurationException;
import org.wso2.carbon.device.mgt.jwt.client.extension.exception.JWTClientException;
import org.wso2.carbon.device.mgt.jwt.client.extension.util.JWTClientUtil;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
/**
* This creates JWT Client for each tenant.
*/
public class JWTClientManager {
private static Map<String, JWTClient> jwtClientMap;
private static JWTClientManager jwtClientCreator;
private static final Log log = LogFactory.getLog(JWTClientManager.class);
private static final String TENANT_JWT_CONFIG_LOCATION = "/jwt-config/jwt.properties";
public static JWTClientManager getInstance() {
if (jwtClientCreator == null) {
synchronized (JWTClientManager.class) {
if (jwtClientCreator == null) {
jwtClientCreator = new JWTClientManager();
}
}
}
return jwtClientCreator;
}
private JWTClientManager() {
jwtClientMap = new ConcurrentHashMap<>();
}
/**
* this return the jwt based token client to generate token for the tenant.
*/
public JWTClient getJWTClient() throws JWTClientException {
String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain();
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
//Get jwt client which has been registered for the tenant.
JWTClient jwtClient = getJWTClient(tenantDomain);
if (jwtClient == null) {
//Create new jwt client for the tenant.
try {
JWTConfig jwtConfig = new JWTConfig(getJWTConfig(tenantId));
jwtClient = new JWTClient(jwtConfig);
addJWTClient(tenantDomain, jwtClient);
} catch (JWTClientAlreadyExistsException e) {
log.warn("Attempting to register a jwt client for the tenant " + tenantDomain +
" when one already exists. Returning existing jwt client");
return getJWTClient(tenantDomain);
} catch (JWTClientConfigurationException e) {
throw new JWTClientException("Failed to parse jwt configuration for tenant " + tenantDomain, e);
}
}
return jwtClient;
}
/**
* Fetch the jwt client which has been registered under the tenant domain.
*
* @param tenantDomain - The tenant domain under which the jwt client is registered
* @return - Instance of the jwt client which was registered. Null if not registered.
*/
private JWTClient getJWTClient(String tenantDomain) {
if (jwtClientMap.containsKey(tenantDomain)) {
return jwtClientMap.get(tenantDomain);
}
return null;
}
/**
* Adds a jwt client to the jwt client map.
*
* @param tenantDomain - The tenant domain under which the jwt client will be registered.
* @param jwtClient - Instance of the jwt client
* @throws JWTClientAlreadyExistsException - If a jwt client has already been registered under the tenantdomain
*/
private void addJWTClient(String tenantDomain, JWTClient jwtClient) throws JWTClientAlreadyExistsException {
if (jwtClientMap.containsKey(tenantDomain)) {
throw new JWTClientAlreadyExistsException("A jwt client has already been created for the tenant " + tenantDomain);
}
jwtClientMap.put(tenantDomain, jwtClient);
}
/**
* Retrieve JWT configs from registry.
*/
private Properties getJWTConfig(int tenantId) throws JWTClientConfigurationException {
try {
Resource config = JWTClientUtil.getConfigRegistryResourceContent(tenantId, TENANT_JWT_CONFIG_LOCATION);
Properties properties = new Properties();
if(config != null) {
properties.load(config.getContentStream());
} else {
throw new JWTClientConfigurationException("Failed to load jwt configuration for tenant id : " + tenantId);
}
return properties;
} catch (RegistryException e) {
throw new JWTClientConfigurationException("Failed to load the content from registry for tenant " +
tenantId, e);
} catch (IOException e) {
throw new JWTClientConfigurationException(
"Failed to parse the content from the registry for tenant " + tenantId, e);
}
}
}

@ -0,0 +1,28 @@
/*
* 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.device.mgt.jwt.client.extension.constant;
/**
* This holds the constants related JWT client component.
*/
public class JWTConstants {
public static final String OAUTH_ACCESS_TOKEN = "access_token";
public static final String OAUTH_REFRESH_TOKEN = "refresh_token";
public static final String OAUTH_EXPIRES_IN = "expires_in";
public static final String OAUTH_TOKEN_TYPE = "token_type";
}

@ -0,0 +1,62 @@
/*
* 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.
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.dto;
/**
* This holds the token information that return from the token endpoint.
*/
public class AccessTokenInfo {
private String token_type;
private long expires_in;
private String refresh_token;
private String access_token;
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expres_in) {
this.expires_in = expres_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
}

@ -0,0 +1,147 @@
package org.wso2.carbon.device.mgt.jwt.client.extension.dto;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class JWTConfig {
private static final String JWT_ISSUER = "iss";
private static final String JWT_EXPIRATION_TIME = "exp";
private static final String JWT_AUDIENCE = "aud";
private static final String VALIDITY_PERIOD = "nbf";
private static final String JWT_TOKEN_ID = "jti";
private static final String JWT_ISSUED_AT = "iat";
private static final String SERVER_TIME_SKEW="skew";
private static final String JKS_PATH ="KeyStore";
private static final String JKS_PRIVATE_KEY_ALIAS ="PrivateKeyAlias";
private static final String JKS_PASSWORD ="KeyStorePassword";
private static final String JKA_PRIVATE_KEY_PASSWORD = "PrivateKeyPassword";
private static final String TOKEN_ENDPOINT = "TokenEndpoint";
/**
* issuer of the JWT
*/
private String issuer;
/**
* skew between IDP and issuer(milliseconds)
*/
private int skew;
/**
* Audience of JWT claim
*/
private List<String> audiences;
/**
* expiration time of JWT (number of minutes from the current time).
*/
private int expirationTime;
/**
* issued Interval from current time of JWT (number of minutes from the current time).
*/
private int issuedInternal;
/**
* nbf time of JWT (number of minutes from current time).
*/
private int validityPeriodInterval;
/**
* JWT Id.
*/
private String jti;
/**
* Token Endpoint;
*/
private String tokenEndpoint;
/**
* Configuration for keystore.
*/
private String keyStorePath;
private String keyStorePassword;
private String privateKeyAlias;
private String privateKeyPassword;
/**
* @param properties load the config from the properties file.
*/
public JWTConfig(Properties properties) {
issuer = properties.getProperty(JWT_ISSUER, null);
skew = Integer.parseInt(properties.getProperty(SERVER_TIME_SKEW, "0"));
issuedInternal = Integer.parseInt(properties.getProperty(JWT_ISSUED_AT,"0"));
expirationTime = Integer.parseInt(properties.getProperty(JWT_EXPIRATION_TIME,"15"));
validityPeriodInterval = Integer.parseInt(properties.getProperty(VALIDITY_PERIOD,"0"));
jti = properties.getProperty(JWT_TOKEN_ID, null);
String audience = properties.getProperty(JWT_AUDIENCE, null);
if(audience != null) {
audiences = getAudience(audience);
}
//get Keystore params
keyStorePath = properties.getProperty(JKS_PATH);
keyStorePassword = properties.getProperty(JKS_PASSWORD);
privateKeyAlias = properties.getProperty(JKS_PRIVATE_KEY_ALIAS);
privateKeyPassword = properties.getProperty(JKA_PRIVATE_KEY_PASSWORD);
tokenEndpoint = properties.getProperty(TOKEN_ENDPOINT, "");
}
private static List<String> getAudience(String audience){
List<String> audiences = new ArrayList<String>();
for(String audi : audience.split(",")){
audiences.add(audi.trim());
}
return audiences;
}
public String getIssuer() {
return issuer;
}
public int getSkew() {
return skew;
}
public List<String> getAudiences() {
return audiences;
}
public int getExpirationTime() {
return expirationTime;
}
public int getIssuedInternal() {
return issuedInternal;
}
public int getValidityPeriodFromCurrentTime() {
return validityPeriodInterval;
}
public String getJti() {
return jti;
}
public String getKeyStorePath() {
return keyStorePath;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public String getPrivateKeyAlias() {
return privateKeyAlias;
}
public String getPrivateKeyPassword() {
return privateKeyPassword;
}
public String getTokenEndpoint() {
return tokenEndpoint;
}
}

@ -0,0 +1,44 @@
/*
* 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.
*
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.exception;
public class JWTClientAlreadyExistsException extends Exception {
public JWTClientAlreadyExistsException() {
super();
}
public JWTClientAlreadyExistsException(String message) {
super(message);
}
public JWTClientAlreadyExistsException(String message, Throwable cause) {
super(message, cause);
}
public JWTClientAlreadyExistsException(Throwable cause) {
super(cause);
}
protected JWTClientAlreadyExistsException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

@ -0,0 +1,43 @@
/*
* 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.
*
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.exception;
public class JWTClientConfigurationException extends Exception {
public JWTClientConfigurationException() {
super();
}
public JWTClientConfigurationException(String message) {
super(message);
}
public JWTClientConfigurationException(String message, Throwable cause) {
super(message, cause);
}
public JWTClientConfigurationException(Throwable cause) {
super(cause);
}
protected JWTClientConfigurationException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

@ -0,0 +1,41 @@
/*
* 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.
*
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.exception;
public class JWTClientException extends Exception{
public JWTClientException() {
super();
}
public JWTClientException(String message) {
super(message);
}
public JWTClientException(String message, Throwable cause) {
super(message, cause);
}
public JWTClientException(Throwable cause) {
super(cause);
}
protected JWTClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

@ -0,0 +1,91 @@
/*
* 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.
*
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.internal;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.registry.core.service.TenantRegistryLoader;
import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.TenantManager;
public class JWTClientExtensionDataHolder {
private static JWTClientExtensionDataHolder thisInstance = new JWTClientExtensionDataHolder();
private TenantRegistryLoader tenantRegistryLoader;
private TenantIndexingLoader indexLoader;
private RegistryService registryService;
private RealmService realmService;
private TenantManager tenantManager;
private JWTClientExtensionDataHolder() {
}
public static JWTClientExtensionDataHolder getInstance() {
return thisInstance;
}
public void setTenantRegistryLoader(TenantRegistryLoader tenantRegistryLoader){
this.tenantRegistryLoader = tenantRegistryLoader;
}
public TenantRegistryLoader getTenantRegistryLoader(){
return tenantRegistryLoader;
}
public void setIndexLoaderService(TenantIndexingLoader indexLoader) {
this.indexLoader = indexLoader;
}
public TenantIndexingLoader getIndexLoaderService(){
return indexLoader;
}
public RegistryService getRegistryService() {
return registryService;
}
public void setRegistryService(RegistryService registryService) {
this.registryService = registryService;
}
public RealmService getRealmService() {
if (realmService == null) {
throw new IllegalStateException("Realm service is not initialized properly");
}
return realmService;
}
public void setRealmService(RealmService realmService) {
this.realmService = realmService;
this.setTenantManager(realmService);
}
private void setTenantManager(RealmService realmService) {
if (realmService == null) {
throw new IllegalStateException("Realm service is not initialized properly");
}
this.tenantManager = realmService.getTenantManager();
}
public TenantManager getTenantManager() {
return tenantManager;
}
}

@ -0,0 +1,134 @@
/*
* 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.
*
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.device.mgt.jwt.client.extension.util.JWTClientUtil;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.registry.core.service.TenantRegistryLoader;
import org.wso2.carbon.registry.indexing.service.TenantIndexingLoader;
import org.wso2.carbon.user.core.service.RealmService;
import java.io.IOException;
/**
* @scr.component name="org.wso2.carbon.device.mgt.jwt.client.extension.internal.JWTClientExtensionServiceComponent"
* immediate="true"
* @scr.reference name="registry.service"
* interface="org.wso2.carbon.registry.core.service.RegistryService"
* cardinality="1..1"
* policy="dynamic"
* bind="setRegistryService"
* unbind="unsetRegistryService"
* @scr.reference name="tenant.registryloader"
* interface="org.wso2.carbon.registry.core.service.TenantRegistryLoader"
* cardinality="1..1"
* policy="dynamic"
* bind="setTenantRegistryLoader"
* unbind="unsetTenantRegistryLoader"
* @scr.reference name="tenant.indexloader"
* interface="org.wso2.carbon.registry.indexing.service.TenantIndexingLoader"
* cardinality="1..1"
* policy="dynamic"
* bind="setIndexLoader"
* unbind="unsetIndexLoader"
* @scr.reference name="realm.service"
* interface="org.wso2.carbon.user.core.service.RealmService"
* cardinality="1..1"
* policy="dynamic"
* bind="setRealmService"
* unbind="unsetRealmService"
*/
public class JWTClientExtensionServiceComponent {
private static Log log = LogFactory.getLog(JWTClientExtensionServiceComponent.class);
protected void activate(ComponentContext componentContext) {
if (log.isDebugEnabled()) {
log.debug("Initializing jwt extension bundle");
}
try {
JWTClientUtil.initialize();
} catch (RegistryException e) {
log.error("Failed loading the jwt config from registry.", e);
} catch (IOException e) {
log.error("Failed loading the jwt config from the file system.", e);
}
}
protected void deactivate(ComponentContext componentContext) {
//do nothing
}
protected void setRegistryService(RegistryService registryService) {
if (registryService != null && log.isDebugEnabled()) {
log.debug("Registry service initialized");
}
JWTClientExtensionDataHolder.getInstance().setRegistryService(registryService);
}
protected void unsetRegistryService(RegistryService registryService) {
JWTClientExtensionDataHolder.getInstance().setRegistryService(null);
}
protected void setTenantRegistryLoader(TenantRegistryLoader tenantRegistryLoader) {
JWTClientExtensionDataHolder.getInstance().setTenantRegistryLoader(tenantRegistryLoader);
}
protected void unsetTenantRegistryLoader(TenantRegistryLoader tenantRegistryLoader) {
JWTClientExtensionDataHolder.getInstance().setTenantRegistryLoader(null);
}
protected void setIndexLoader(TenantIndexingLoader indexLoader) {
if (indexLoader != null && log.isDebugEnabled()) {
log.debug("IndexLoader service initialized");
}
JWTClientExtensionDataHolder.getInstance().setIndexLoaderService(indexLoader);
}
protected void unsetIndexLoader(TenantIndexingLoader indexLoader) {
JWTClientExtensionDataHolder.getInstance().setIndexLoaderService(null);
}
/**
* Sets Realm Service.
*
* @param realmService An instance of RealmService
*/
protected void setRealmService(RealmService realmService) {
if (log.isDebugEnabled()) {
log.debug("Setting Realm Service");
}
JWTClientExtensionDataHolder.getInstance().setRealmService(realmService);
}
/**
* Unsets Realm Service.
*
* @param realmService An instance of RealmService
*/
protected void unsetRealmService(RealmService realmService) {
if (log.isDebugEnabled()) {
log.debug("Unsetting Realm Service");
}
JWTClientExtensionDataHolder.getInstance().setRealmService(null);
}
}

@ -0,0 +1,168 @@
/*
* 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.
*/
package org.wso2.carbon.device.mgt.jwt.client.extension.util;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.jwt.client.extension.internal.JWTClientExtensionDataHolder;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.registry.core.service.TenantRegistryLoader;
import org.wso2.carbon.utils.CarbonUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
/**
* This is the utility class that is used for JWT Client.
*/
public class JWTClientUtil {
private static final Log log = LogFactory.getLog(JWTClientUtil.class);
private static final String HTTPS_PROTOCOL = "https";
private static final String TENANT_JWT_CONFIG_LOCATION = "/jwt-config/jwt.properties";
private static final String JWT_CONFIG_FILE_NAME = "jwt.properties";
private static final String SUPERTENANT_JWT_CONFIG_LOCATION =
CarbonUtils.getEtcCarbonConfigDirPath() + File.separator + JWT_CONFIG_FILE_NAME;
/**
* Return a http client instance
* @param protocol- service endpoint protocol http/https
* @return
*/
public static HttpClient getHttpClient(String protocol)
throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
HttpClient httpclient;
if (HTTPS_PROTOCOL.equals(protocol)) {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
} else {
httpclient = HttpClients.createDefault();
}
return httpclient;
}
public static String getResponseString(HttpResponse httpResponse) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
String readLine;
String response = "";
while (((readLine = br.readLine()) != null)) {
response += readLine;
}
return response;
} finally {
EntityUtils.consumeQuietly(httpResponse.getEntity());
if (br != null) {
try {
br.close();
} catch (IOException e) {
log.warn("Error while closing the connection! " + e.getMessage());
}
}
}
}
public static void initialize() throws RegistryException, IOException {
Resource resource = getConfigRegistryResourceContent(MultitenantConstants.SUPER_TENANT_ID, TENANT_JWT_CONFIG_LOCATION);
if (resource == null) {
File configFile = new File(SUPERTENANT_JWT_CONFIG_LOCATION);
String contents = FileUtils.readFileToString(configFile, "UTF-8");
addJWTConfigResourceToRegistry(MultitenantConstants.SUPER_TENANT_ID, contents);
}
}
/**
* Get the jwt details from the registry for tenants.
*
* @param tenantId for identify tenant space.
* @param registryLocation retrive the config file from tenant space.
* @return the config for tenant
* @throws RegistryException
*/
public static Resource getConfigRegistryResourceContent(int tenantId, final String registryLocation)
throws RegistryException {
try {
Resource resource = null;
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true);
RegistryService registryService = JWTClientExtensionDataHolder.getInstance().getRegistryService();
if (registryService != null) {
Registry registry = registryService.getConfigSystemRegistry(tenantId);
JWTClientUtil.loadTenantRegistry(tenantId);
if (registry.resourceExists(registryLocation)) {
resource = registry.get(registryLocation);
}
}
return resource;
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
/**
* Get the jwt details from the registry for tenants.
*
* @param tenantId for accesing tenant space.
* @return the config for tenant
* @throws RegistryException
*/
public static void addJWTConfigResourceToRegistry(int tenantId, String content)
throws RegistryException {
try {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true);
RegistryService registryService = JWTClientExtensionDataHolder.getInstance().getRegistryService();
if (registryService != null) {
Registry registry = registryService.getConfigSystemRegistry(tenantId);
JWTClientUtil.loadTenantRegistry(tenantId);
if (!registry.resourceExists(TENANT_JWT_CONFIG_LOCATION)) {
Resource resource = registry.newResource();
resource.setContent(content.getBytes());
registry.put(TENANT_JWT_CONFIG_LOCATION, resource);
}
}
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
private static void loadTenantRegistry(int tenantId) throws RegistryException {
TenantRegistryLoader tenantRegistryLoader = JWTClientExtensionDataHolder.getInstance().getTenantRegistryLoader();
JWTClientExtensionDataHolder.getInstance().getIndexLoaderService().loadTenantIndex(tenantId);
tenantRegistryLoader.loadTenantRegistry(tenantId);
}
}

@ -29,13 +29,13 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>identity-extensions</artifactId>
<version>1.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>WSO2 Carbon - Dynamic Client Registration Component</name>
<url>http://wso2.org</url>
<modules>
<module>org.wso2.carbon.device.mgt.oauth.extensions</module>
<module>org.wso2.carbon.identity.jwt.client.extension</module>
<module>dynamic-client-registration</module>
<module>backend-oauth-authenticator</module>
</modules>

@ -199,7 +199,7 @@
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.wso2</groupId>
<groupId>org.wso2.orbit.org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>

Loading…
Cancel
Save