Java tutorial
/* * Copyright (c) 2014, 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.apimgt.keymgt; import org.apache.amber.oauth2.common.OAuth; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axis2.util.URL; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; 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.apache.http.util.EntityUtils; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.json.simple.JSONArray; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.AccessTokenInfo; import org.wso2.carbon.apimgt.api.model.AccessTokenRequest; import org.wso2.carbon.apimgt.api.model.ApplicationConstants; import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration; import org.wso2.carbon.apimgt.api.model.OAuthAppRequest; import org.wso2.carbon.apimgt.api.model.OAuthApplicationInfo; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.APIManagerConfiguration; import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder; import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.keymgt.client.SubscriberKeyMgtClient; import org.wso2.carbon.apimgt.keymgt.util.APIKeyMgtDataHolder; import org.wso2.carbon.apimgt.keymgt.util.APIKeyMgtUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.identity.oauth2.dto.OAuth2ClientApplicationDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationRequestDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationResponseDTO; import javax.xml.stream.XMLStreamException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class holds the key manager implementation considering WSO2 as the identity provider * This is the default key manager supported by API Manager */ public class AMDefaultKeyManagerImpl extends AbstractKeyManager { private static final String OAUTH_RESPONSE_ACCESSTOKEN = "access_token"; private static final String OAUTH_RESPONSE_EXPIRY_TIME = "expires_in"; private static final String GRANT_TYPE_VALUE = "client_credentials"; private static final String GRANT_TYPE_PARAM_VALIDITY = "validity_period"; private KeyManagerConfiguration configuration; private static final Log log = LogFactory.getLog(AMDefaultKeyManagerImpl.class); @Override public OAuthApplicationInfo createApplication(OAuthAppRequest oauthAppRequest) throws APIManagementException { // OAuthApplications are created by calling to APIKeyMgtSubscriber Service SubscriberKeyMgtClient keyMgtClient = APIUtil.getKeyManagementClient(); OAuthApplicationInfo oAuthApplicationInfo = oauthAppRequest.getOAuthApplicationInfo(); // Subscriber's name should be passed as a parameter, since it's under the subscriber the OAuth App is created. String userId = (String) oAuthApplicationInfo.getParameter(ApplicationConstants.OAUTH_CLIENT_USERNAME); String applicationName = oAuthApplicationInfo.getClientName(); if (log.isDebugEnabled()) { log.debug("Trying to create OAuth application :" + applicationName); } String callBackURL = oAuthApplicationInfo.getCallBackURL(); String tokenScope = (String) oAuthApplicationInfo.getParameter("tokenScope"); String tokenScopes[] = new String[1]; tokenScopes[0] = tokenScope; oAuthApplicationInfo.addParameter("tokenScope", tokenScopes); org.wso2.carbon.apimgt.api.model.xsd.OAuthApplicationInfo info = null; try { info = keyMgtClient.createOAuthApplication(userId, applicationName, callBackURL); } catch (Exception e) { handleException("Can not create OAuth application : " + applicationName, e); } if (info == null || info.getJsonString() == null) { handleException("OAuth app does not contains required data : " + applicationName, new APIManagementException("OAuth app does not contains required data")); } oAuthApplicationInfo.setClientName(info.getClientName()); oAuthApplicationInfo.setClientId(info.getClientId()); oAuthApplicationInfo.setCallBackURL(info.getCallBackURL()); oAuthApplicationInfo.setClientSecret(info.getClientSecret()); try { JSONObject jsonObject = new JSONObject(info.getJsonString()); if (jsonObject.has(ApplicationConstants.OAUTH_REDIRECT_URIS)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_REDIRECT_URIS, jsonObject.get(ApplicationConstants.OAUTH_REDIRECT_URIS)); } if (jsonObject.has(ApplicationConstants.OAUTH_CLIENT_NAME)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_CLIENT_NAME, jsonObject.get(ApplicationConstants.OAUTH_CLIENT_NAME)); } if (jsonObject.has(ApplicationConstants.OAUTH_CLIENT_GRANT)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_CLIENT_GRANT, jsonObject.get(ApplicationConstants.OAUTH_CLIENT_GRANT)); } } catch (JSONException e) { handleException("Can not retrieve information of the created OAuth application", e); } return oAuthApplicationInfo; } @Override public OAuthApplicationInfo updateApplication(OAuthAppRequest appInfoDTO) throws APIManagementException { // TO DO return null; } @Override public void deleteApplication(String consumerKey) throws APIManagementException { SubscriberKeyMgtClient keyMgtClient = APIUtil.getKeyManagementClient(); if (log.isDebugEnabled()) { log.debug("Trying to delete OAuth application for consumer key :" + consumerKey); } try { keyMgtClient.deleteOAuthApplication(consumerKey); } catch (Exception e) { handleException("Can not remove service provider for the given consumer key : " + consumerKey, e); } } @Override public OAuthApplicationInfo retrieveApplication(String consumerKey) throws APIManagementException { SubscriberKeyMgtClient keyMgtClient = APIUtil.getKeyManagementClient(); if (log.isDebugEnabled()) { log.debug("Trying to retrieve OAuth application for consumer key :" + consumerKey); } OAuthApplicationInfo oAuthApplicationInfo = new OAuthApplicationInfo(); try { org.wso2.carbon.apimgt.api.model.xsd.OAuthApplicationInfo info = keyMgtClient .getOAuthApplication(consumerKey); if (info == null || info.getClientId() == null) { return null; } oAuthApplicationInfo.setClientName(info.getClientName()); oAuthApplicationInfo.setClientId(info.getClientId()); oAuthApplicationInfo.setCallBackURL(info.getCallBackURL()); oAuthApplicationInfo.setClientSecret(info.getClientSecret()); JSONObject jsonObject = new JSONObject(info.getJsonString()); if (jsonObject.has(ApplicationConstants.OAUTH_REDIRECT_URIS)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_REDIRECT_URIS, jsonObject.get(ApplicationConstants.OAUTH_REDIRECT_URIS)); } if (jsonObject.has(ApplicationConstants.OAUTH_CLIENT_NAME)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_CLIENT_NAME, jsonObject.get(ApplicationConstants.OAUTH_CLIENT_NAME)); } if (jsonObject.has(ApplicationConstants.OAUTH_CLIENT_GRANT)) { oAuthApplicationInfo.addParameter(ApplicationConstants.OAUTH_CLIENT_GRANT, jsonObject.get(ApplicationConstants.OAUTH_CLIENT_GRANT)); } } catch (Exception e) { handleException("Can not retrieve OAuth application for the given consumer key : " + consumerKey, e); } return oAuthApplicationInfo; } @Override public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest tokenRequest) throws APIManagementException { String newAccessToken; long validityPeriod; AccessTokenInfo tokenInfo = null; if (tokenRequest == null) { log.warn("No information available to generate Token."); return null; } String tokenEndpoint = configuration.getParameter(APIConstants.TOKEN_URL); //To revoke tokens we should call revoke API deployed in API gateway. String revokeEndpoint = configuration.getParameter(APIConstants.REVOKE_URL); URL keymgtURL = new URL(tokenEndpoint); int keyMgtPort = keymgtURL.getPort(); String keyMgtProtocol = keymgtURL.getProtocol(); // Call the /revoke only if there's a token to be revoked. try { if (tokenRequest.getTokenToRevoke() != null) { URL revokeEndpointURL = new URL(revokeEndpoint); String revokeEndpointProtocol = revokeEndpointURL.getProtocol(); int revokeEndpointPort = revokeEndpointURL.getPort(); HttpClient revokeEPClient = APIKeyMgtUtil.getHttpClient(revokeEndpointPort, revokeEndpointProtocol); HttpPost httpRevokepost = new HttpPost(revokeEndpoint); // Request parameters. List<NameValuePair> revokeParams = new ArrayList<NameValuePair>(3); revokeParams.add(new BasicNameValuePair(OAuth.OAUTH_CLIENT_ID, tokenRequest.getClientId())); revokeParams.add(new BasicNameValuePair(OAuth.OAUTH_CLIENT_SECRET, tokenRequest.getClientSecret())); revokeParams.add(new BasicNameValuePair("token", tokenRequest.getTokenToRevoke())); //Revoke the Old Access Token httpRevokepost.setEntity(new UrlEncodedFormEntity(revokeParams, "UTF-8")); HttpResponse revokeResponse = revokeEPClient.execute(httpRevokepost); if (revokeResponse.getStatusLine().getStatusCode() != 200) { throw new RuntimeException("Token revoke failed : HTTP error code : " + revokeResponse.getStatusLine().getStatusCode()); } else { if (log.isDebugEnabled()) { log.debug( "Successfully submitted revoke request for old application token. HTTP status : 200"); } } } //get default application access token name from config. String applicationScope = APIKeyMgtDataHolder.getApplicationTokenScope(); // When validity time set to a negative value, a token is considered never to expire. if (tokenRequest.getValidityPeriod() == OAuthConstants.UNASSIGNED_VALIDITY_PERIOD) { // Setting a different -ve value if the set value is -1 (-1 will be ignored by TokenValidator) tokenRequest.setValidityPeriod(-2); } //Generate New Access Token HttpClient tokenEPClient = APIKeyMgtUtil.getHttpClient(keyMgtPort, keyMgtProtocol); HttpPost httpTokpost = new HttpPost(tokenEndpoint); List<NameValuePair> tokParams = new ArrayList<NameValuePair>(3); tokParams.add(new BasicNameValuePair(OAuth.OAUTH_GRANT_TYPE, GRANT_TYPE_VALUE)); tokParams.add(new BasicNameValuePair(GRANT_TYPE_PARAM_VALIDITY, Long.toString(tokenRequest.getValidityPeriod()))); tokParams.add(new BasicNameValuePair(OAuth.OAUTH_CLIENT_ID, tokenRequest.getClientId())); tokParams.add(new BasicNameValuePair(OAuth.OAUTH_CLIENT_SECRET, tokenRequest.getClientSecret())); StringBuilder builder = new StringBuilder(); builder.append(applicationScope); for (String scope : tokenRequest.getScope()) { builder.append(" " + scope); } tokParams.add(new BasicNameValuePair("scope", builder.toString())); httpTokpost.setEntity(new UrlEncodedFormEntity(tokParams, "UTF-8")); HttpResponse tokResponse = tokenEPClient.execute(httpTokpost); HttpEntity tokEntity = tokResponse.getEntity(); if (tokResponse.getStatusLine().getStatusCode() != 200) { throw new RuntimeException("Error occurred while calling token endpoint: HTTP error code : " + tokResponse.getStatusLine().getStatusCode()); } else { tokenInfo = new AccessTokenInfo(); String responseStr = EntityUtils.toString(tokEntity); JSONObject obj = new JSONObject(responseStr); newAccessToken = obj.get(OAUTH_RESPONSE_ACCESSTOKEN).toString(); validityPeriod = Long.parseLong(obj.get(OAUTH_RESPONSE_EXPIRY_TIME).toString()); if (obj.has("scope")) { tokenInfo.setScope(((String) obj.get("scope")).split(" ")); } tokenInfo.setAccessToken(newAccessToken); tokenInfo.setValidityPeriod(validityPeriod); } } catch (ClientProtocolException e) { handleException("Error while creating token - Invalid protocol used", e); } catch (UnsupportedEncodingException e) { handleException("Error while preparing request for token/revoke APIs", e); } catch (IOException e) { handleException("Error while creating tokens - " + e.getMessage(), e); } catch (JSONException e) { handleException("Error while parsing response from token api", e); } return tokenInfo; } @Override public AccessTokenInfo getTokenMetaData(String accessToken) throws APIManagementException { AccessTokenInfo tokenInfo = new AccessTokenInfo(); OAuth2TokenValidationService oAuth2TokenValidationService = new OAuth2TokenValidationService(); OAuth2TokenValidationRequestDTO requestDTO = new OAuth2TokenValidationRequestDTO(); OAuth2TokenValidationRequestDTO.OAuth2AccessToken token = requestDTO.new OAuth2AccessToken(); token.setIdentifier(accessToken); token.setTokenType("bearer"); requestDTO.setAccessToken(token); //TODO: If these values are not set, validation will fail giving an NPE. Need to see why that happens OAuth2TokenValidationRequestDTO.TokenValidationContextParam contextParam = requestDTO.new TokenValidationContextParam(); contextParam.setKey("dummy"); contextParam.setValue("dummy"); OAuth2TokenValidationRequestDTO.TokenValidationContextParam[] contextParams = new OAuth2TokenValidationRequestDTO.TokenValidationContextParam[1]; contextParams[0] = contextParam; requestDTO.setContext(contextParams); OAuth2ClientApplicationDTO clientApplicationDTO = oAuth2TokenValidationService .findOAuthConsumerIfTokenIsValid(requestDTO); OAuth2TokenValidationResponseDTO responseDTO = clientApplicationDTO.getAccessTokenValidationResponse(); if (!responseDTO.isValid()) { tokenInfo.setTokenValid(responseDTO.isValid()); log.error("Invalid OAuth Token : " + responseDTO.getErrorMsg()); tokenInfo.setErrorcode(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS); return tokenInfo; } tokenInfo.setTokenValid(responseDTO.isValid()); tokenInfo.setEndUserName(responseDTO.getAuthorizedUser()); tokenInfo.setConsumerKey(clientApplicationDTO.getConsumerKey()); // Convert Expiry Time to milliseconds. if (responseDTO.getExpiryTime() == Long.MAX_VALUE) { tokenInfo.setValidityPeriod(Long.MAX_VALUE); } else { tokenInfo.setValidityPeriod(responseDTO.getExpiryTime() * 1000); } tokenInfo.setIssuedTime(System.currentTimeMillis()); tokenInfo.setScope(responseDTO.getScope()); // If token has am_application_scope, consider the token as an Application token. String[] scopes = responseDTO.getScope(); String applicationTokenScope = APIKeyMgtDataHolder.getApplicationTokenScope(); if (scopes != null && applicationTokenScope != null && !applicationTokenScope.isEmpty()) { if (Arrays.asList(scopes).contains(applicationTokenScope)) { tokenInfo.setApplicationToken(true); } } if (APIUtil.checkAccessTokenPartitioningEnabled() && APIUtil.checkUserNameAssertionEnabled()) { tokenInfo.setConsumerKey(ApiMgtDAO.getConsumerKeyForTokenWhenTokenPartitioningEnabled(accessToken)); } return tokenInfo; } @Override public KeyManagerConfiguration getKeyManagerConfiguration() throws APIManagementException { return configuration; } @Override public OAuthApplicationInfo buildFromJSON(String jsonInput) throws APIManagementException { return null; } /** * This method will create a new record at CLIENT_INFO table by given OauthAppRequest. * * @param appInfoRequest oAuth application properties will contain in this object * @return OAuthApplicationInfo with created oAuth application details. * @throws org.wso2.carbon.apimgt.api.APIManagementException */ @Override public OAuthApplicationInfo mapOAuthApplication(OAuthAppRequest appInfoRequest) throws APIManagementException { //initiate OAuthApplicationInfo OAuthApplicationInfo oAuthApplicationInfo = appInfoRequest.getOAuthApplicationInfo(); String consumerKey = oAuthApplicationInfo.getClientId(); String tokenScope = (String) oAuthApplicationInfo.getParameter("tokenScope"); String tokenScopes[] = new String[1]; tokenScopes[0] = tokenScope; String clientSecret = (String) oAuthApplicationInfo.getParameter("client_secret"); oAuthApplicationInfo.setClientSecret(clientSecret); //check whether given consumer key and secret match or not. If it does not match throw an exception. SubscriberKeyMgtClient keyMgtClient = APIUtil.getKeyManagementClient(); org.wso2.carbon.apimgt.api.model.xsd.OAuthApplicationInfo info = null; try { info = keyMgtClient.getOAuthApplication(oAuthApplicationInfo.getClientId()); if (!clientSecret.equals(info.getClientSecret())) { throw new APIManagementException( "The secret key is wrong for the given consumer key " + consumerKey); } } catch (Exception e) { handleException("Some thing went wrong while getting OAuth application for given consumer key " + oAuthApplicationInfo.getClientId(), e); } if (info.getClientId() == null) { return null; } oAuthApplicationInfo.addParameter("tokenScope", tokenScopes); if (log.isDebugEnabled()) { log.debug("Creating semi-manual application for consumer id : " + oAuthApplicationInfo.getClientId()); } return oAuthApplicationInfo; } @Override public void loadConfiguration(KeyManagerConfiguration configuration) throws APIManagementException { if (configuration != null) { this.configuration = configuration; } else { // If the provided configuration is null, read the Server-URL and other properties from // APIKeyValidator section. APIManagerConfiguration config = ServiceReferenceHolder.getInstance() .getAPIManagerConfigurationService().getAPIManagerConfiguration(); if (this.configuration == null) { this.configuration = new KeyManagerConfiguration(); this.configuration.setManualModeSupported(true); this.configuration.setResourceRegistrationEnabled(true); this.configuration.setTokenValidityConfigurable(true); this.configuration.addParameter(APIConstants.AUTHSERVER_URL, config.getFirstProperty(APIConstants.KEYMANAGER_SERVERURL)); this.configuration.addParameter(APIConstants.KEY_MANAGER_USERNAME, config.getFirstProperty(APIConstants.API_KEY_VALIDATOR_USERNAME)); this.configuration.addParameter(APIConstants.KEY_MANAGER_PASSWORD, config.getFirstProperty(APIConstants.API_KEY_VALIDATOR_PASSWORD)); this.configuration.addParameter(APIConstants.REVOKE_URL, config.getFirstProperty(APIConstants.API_KEY_VALIDATOR_REVOKE_API_URL)); String revokeUrl = config.getFirstProperty(APIConstants.API_KEY_VALIDATOR_REVOKE_API_URL); // Read the revoke url and replace revoke part to get token url. String tokenUrl = revokeUrl != null ? revokeUrl.replace("revoke", "token") : null; this.configuration.addParameter(APIConstants.TOKEN_URL, tokenUrl); } } } @Override public boolean registerNewResource(API api, Map resourceAttributes) throws APIManagementException { return true; } @Override public Map getResourceByApiId(String apiId) throws APIManagementException { return null; } @Override public boolean updateRegisteredResource(API api, Map resourceAttributes) throws APIManagementException { return false; } @Override public void deleteRegisteredResourceByAPIId(String apiID) throws APIManagementException { } @Override public void deleteMappedApplication(String consumerKey) throws APIManagementException { } @Override public Set<String> getActiveTokensByConsumerKey(String consumerKey) throws APIManagementException { ApiMgtDAO apiMgtDAO = new ApiMgtDAO(); Set<String> activeTokens = apiMgtDAO.getActiveTokensOfConsumerKey(consumerKey); return activeTokens; } @Override public AccessTokenInfo getAccessTokenByConsumerKey(String consumerKey) throws APIManagementException { return null; } /** * common method to throw exceptions. * * @param msg this parameter contain error message that we need to throw. * @param e Exception object. * @throws org.wso2.carbon.apimgt.api.APIManagementException */ private void handleException(String msg, Exception e) throws APIManagementException { log.error(msg, e); throw new APIManagementException(msg, e); } }