Java tutorial
/* * * * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * * * Licensed 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.sciquest.keymanager; import org.apache.axiom.om.util.Base64; import org.apache.commons.io.IOUtils; 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.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; 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.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.factory.KeyManagerHolder; import org.wso2.carbon.apimgt.impl.AbstractKeyManager; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * This class provides the implementation to use "Apis" {@link "https://github.com/OAuth-Apis/apis"} for managing * OAuth clients and Tokens needed by WSO2 API Manager. */ public class APIManagerOAuthClient extends AbstractKeyManager { private static final Log log = LogFactory.getLog(APIManagerOAuthClient.class); private KeyManagerConfiguration configuration; /** * {@code APIManagerComponent} calls this method, passing KeyManagerConfiguration as a {@code String}. * * @param configuration Configuration as a {@link org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration} */ public void loadConfiguration(KeyManagerConfiguration configuration) throws APIManagementException { this.configuration = configuration; } /** * This method will Register the client in Authorization Server. * * @param oauthAppRequest this object holds all parameters required to register an OAuth Client. * @return an {@code OAuthApplicationInfo} created OAuth Application details in the external OAuth Server * @throws APIManagementException */ public OAuthApplicationInfo createApplication(OAuthAppRequest oauthAppRequest) throws APIManagementException { log.warn("The Operation [Create OAuth Application] is not supported by the OAuth 2 Server"); return null; } /** * This method will update an existing OAuth Client. * * @param oauthAppRequest Parameters to be passed to Authorization Server, encapsulated as an {@code OAuthAppRequest} * @return an {@code OAuthApplicationInfo} having details of updated OAuth Client. * @throws APIManagementException * */ public OAuthApplicationInfo updateApplication(OAuthAppRequest oauthAppRequest) throws APIManagementException { log.warn("The Operation [Update OAuth Application] is not supported by the OAuth 2 Server"); return null; } /** * Deletes OAuth Client from Authorization Server. * * @param consumerKey consumer key of the OAuth Client. * @throws APIManagementException */ public void deleteApplication(String consumerKey) throws APIManagementException { log.warn("The Operation [Delete OAuth Application] is not supported by the OAuth 2 Server"); } /** * This method retrieves OAuth application details by given consumer key. * * @param consumerKey consumer key of the OAuth Client. * @return an {@code OAuthApplicationInfo} having all the details of an OAuth Client. * @throws APIManagementException */ public OAuthApplicationInfo retrieveApplication(String consumerKey) throws APIManagementException { if (log.isDebugEnabled()) { log.debug("The Operation [Retrieve OAuth Application details] is not supported by the OAuth 2 Server"); } // NO Client Registration URL provided to query, OAuthApplicationInfo info = new OAuthApplicationInfo(); info.setClientId(consumerKey); return info; } /** * This method retrieve access token from external OAuth Server * * @param tokenRequest having necessary parameters for token generation in the external OAuth Server * @return an {@code AccessTokenInfo} having newly created access token and it's meta informations * @throws APIManagementException */ public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest tokenRequest) throws APIManagementException { if (tokenRequest == null) { return null; } if (log.isDebugEnabled()) { log.debug("Calling OAuth Server for generating Access Token"); } KeyManagerConfiguration config = KeyManagerHolder.getKeyManagerInstance().getKeyManagerConfiguration(); String tokenEndpoint = config.getParameter(ClientConstants.TOKEN_URL); HttpPost httpPost = new HttpPost(tokenEndpoint.trim()); HttpClient httpClient = new DefaultHttpClient(); BufferedReader reader = null; try { String jsonPayload = "grant_type=client_credentials"; httpPost.setEntity(new StringEntity(jsonPayload, ClientConstants.UTF_8)); httpPost.setHeader(ClientConstants.CONTENT_TYPE, ClientConstants.URL_ENCODED_CONTENT_TYPE); httpPost.setHeader(ClientConstants.ACCEPT, ClientConstants.APPLICATION_JSON_CONTENT_TYPE); String encodedSecret = Base64.encode( new String(tokenRequest.getClientId() + ":" + tokenRequest.getClientSecret()).getBytes()); httpPost.setHeader(ClientConstants.AUTHORIZATION, ClientConstants.BASIC + encodedSecret); HttpResponse response = httpClient.execute(httpPost); int responseCode = response.getStatusLine().getStatusCode(); if (HttpStatus.SC_OK == responseCode) { HttpEntity entity = response.getEntity(); reader = new BufferedReader(new InputStreamReader(entity.getContent(), ClientConstants.UTF_8)); JSONObject parsedObject = getParsedObjectByReader(reader); return getAccessTokenFromResponse(parsedObject); } else { handleException("Some thing wrong here while retrieving new token HTTP Error response code is " + responseCode); } } catch (ClientProtocolException e) { handleException( "HTTP request error has occurred while sending request to OAuth Provider. " + e.getMessage(), e); } catch (IOException e) { handleException("Error has occurred while reading or closing buffer reader. " + e.getMessage(), e); } catch (ParseException e) { handleException("Error while parsing response json " + e.getMessage(), e); } finally { IOUtils.closeQuietly(reader); } return null; } @Override public AccessTokenRequest buildAccessTokenRequestFromOAuthApp(OAuthApplicationInfo oAuthApplication, AccessTokenRequest tokenRequest) throws APIManagementException { return null; } /** * This method validates the access token with external OAuth Server * * @param accessToken - the token which need to be validated * @return an {@code AccessTokenInfo} having token validation meta data * @throws APIManagementException */ public AccessTokenInfo getTokenMetaData(String accessToken) throws APIManagementException { AccessTokenInfo tokenInfo = new AccessTokenInfo(); tokenInfo.setAccessToken(accessToken); KeyManagerConfiguration config = KeyManagerHolder.getKeyManagerInstance().getKeyManagerConfiguration(); String introspectionURL = config.getParameter(ClientConstants.INTROSPECTION_URL); String introspectionConsumerKey = config.getParameter(ClientConstants.INTROSPECTION_CK); String introspectionConsumerSecret = config.getParameter(ClientConstants.INTROSPECTION_CS); String encodedSecret = Base64 .encode(new String(introspectionConsumerKey + ":" + introspectionConsumerSecret).getBytes()); BufferedReader reader = null; try { URIBuilder uriBuilder = new URIBuilder(introspectionURL); uriBuilder.addParameter("access_token", accessToken); uriBuilder.build(); HttpGet httpGet = new HttpGet(uriBuilder.build()); HttpClient client = new DefaultHttpClient(); httpGet.setHeader("Authorization", "Basic " + encodedSecret); HttpResponse response = client.execute(httpGet); int responseCode = response.getStatusLine().getStatusCode(); if (log.isDebugEnabled()) { log.debug("HTTP Response code for Token Validation from external OAuth Server: " + responseCode); } // Response Format from OAuth 2 Server /*{ "audience":"MappedClient", "scopes":[ "test" ], "principal":{ "name":"mappedclient", "roles":[ ], "groups":[ ], "adminPrincipal":false, "attributes":{ } }, "expires_in":1433059160531 }*/ HttpEntity entity = response.getEntity(); JSONObject parsedObject; reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8")); if (HttpStatus.SC_OK == responseCode) { //pass bufferReader object and get read it and retrieve the parsedJson object parsedObject = getParsedObjectByReader(reader); if (parsedObject != null) { Object principal = parsedObject.get("principal"); if (principal == null) { tokenInfo.setTokenValid(false); return tokenInfo; } Map principalMap = (Map) principal; String clientId = (String) principalMap.get("name"); Long expiryTimeString = (Long) parsedObject.get("expires_in"); // Returning false if mandatory attributes are missing. if (clientId == null || expiryTimeString == null) { tokenInfo.setTokenValid(false); tokenInfo.setErrorcode(APIConstants.KeyValidationStatus.API_AUTH_ACCESS_TOKEN_EXPIRED); return tokenInfo; } long currentTime = System.currentTimeMillis(); long expiryTime = expiryTimeString; if (expiryTime > currentTime) { tokenInfo.setTokenValid(true); tokenInfo.setConsumerKey(clientId); tokenInfo.setValidityPeriod(expiryTime - currentTime); // Considering Current Time as the issued time. tokenInfo.setIssuedTime(currentTime); JSONArray scopesArray = (JSONArray) parsedObject.get("scopes"); if (log.isDebugEnabled()) { StringBuilder tokens = new StringBuilder("["); Iterator iterator = scopesArray.iterator(); while (iterator.hasNext()) { tokens.append(iterator.next()); if (iterator.hasNext()) { tokens.append(", "); } } tokens.append("]"); log.debug("Tokens " + tokens); } if (scopesArray != null && !scopesArray.isEmpty()) { String[] scopes = new String[scopesArray.size()]; for (int i = 0; i < scopes.length; i++) { scopes[i] = (String) scopesArray.get(i); } tokenInfo.setScope(scopes); } JSONObject jso = new JSONObject(); jso.putAll(principalMap); tokenInfo.setEndUserName(jso.toString()); if (log.isDebugEnabled()) { log.debug("Token Response Principle : " + jso.toJSONString()); } } else { tokenInfo.setTokenValid(false); tokenInfo.setErrorcode(APIConstants.KeyValidationStatus.API_AUTH_ACCESS_TOKEN_EXPIRED); return tokenInfo; } } else { log.error("Invalid Token " + accessToken); tokenInfo.setTokenValid(false); tokenInfo.setErrorcode(APIConstants.KeyValidationStatus.API_AUTH_ACCESS_TOKEN_INACTIVE); return tokenInfo; } } //for other HTTP error codes we just pass generic message. else { log.error("Invalid Token " + accessToken); tokenInfo.setTokenValid(false); tokenInfo.setErrorcode(APIConstants.KeyValidationStatus.API_AUTH_ACCESS_TOKEN_INACTIVE); return tokenInfo; } } catch (UnsupportedEncodingException e) { handleException("The Character Encoding is not supported. " + e.getMessage(), e); } catch (ClientProtocolException e) { handleException( "HTTP request error has occurred while sending request to OAuth Provider. " + e.getMessage(), e); } catch (IOException e) { handleException("Error has occurred while reading or closing buffer reader. " + e.getMessage(), e); } catch (URISyntaxException e) { handleException("Error occurred while building URL with params." + e.getMessage(), e); } catch (ParseException e) { handleException("Error while parsing response json " + e.getMessage(), e); } finally { IOUtils.closeQuietly(reader); } return tokenInfo; } public KeyManagerConfiguration getKeyManagerConfiguration() throws APIManagementException { return configuration; } public OAuthApplicationInfo buildFromJSON(String jsonInput) throws APIManagementException { return null; } /** * This method will be called when mapping existing OAuth Clients with Application in API Manager * * @param appInfoRequest Details of the OAuth Client to be mapped. * @return {@code OAuthApplicationInfo} with the details of the mapped client. * @throws APIManagementException */ public OAuthApplicationInfo mapOAuthApplication(OAuthAppRequest appInfoRequest) throws APIManagementException { if (log.isDebugEnabled()) { log.debug("Client OAuth application creation not supported in OAuth Server"); } OAuthApplicationInfo oAuthApplicationInfo = appInfoRequest.getOAuthApplicationInfo(); return oAuthApplicationInfo; } public boolean registerNewResource(API api, Map resourceAttributes) throws APIManagementException { return true; } public Map getResourceByApiId(String apiId) throws APIManagementException { return null; } public boolean updateRegisteredResource(API api, Map resourceAttributes) throws APIManagementException { return true; } public void deleteRegisteredResourceByAPIId(String apiID) throws APIManagementException { } public void deleteMappedApplication(String s) throws APIManagementException { } public Set<String> getActiveTokensByConsumerKey(String s) throws APIManagementException { return null; } public AccessTokenInfo getAccessTokenByConsumerKey(String s) throws APIManagementException { return null; } /** * Can be used to parse {@code BufferedReader} object that are taken from response stream, to a {@code JSONObject}. * * @param reader {@code BufferedReader} object from response. * @return JSON payload as a name value map. */ private JSONObject getParsedObjectByReader(BufferedReader reader) throws ParseException, IOException { JSONObject parsedObject = null; JSONParser parser = new JSONParser(); if (reader != null) { parsedObject = (JSONObject) parser.parse(reader); } return parsedObject; } /** * common method to throw exceptions. * * @param msg this parameter contain error message that we need to throw. * @param e Exception object. * @throws APIManagementException */ private void handleException(String msg, Exception e) throws APIManagementException { log.error(msg, e); throw new APIManagementException(msg, e); } private void handleException(String msg) throws APIManagementException { log.error(msg); throw new APIManagementException(msg); } private AccessTokenInfo getAccessTokenFromResponse(JSONObject map) { //{"scope":"test","access_token":"b32875ac-bf5d-40c4-838d-a1c69b13479c","token_type":"bearer","expires_in":3600} AccessTokenInfo tokenInfo = new AccessTokenInfo(); tokenInfo.setAccessToken((String) map.get("access_token")); tokenInfo.setValidityPeriod((Long) map.get("expires_in")); tokenInfo.setScope(new String[] { (String) map.get("scope") }); return tokenInfo; } }