Skip to content

Commit

Permalink
Merge pull request #14 from chamilaadhi/master
Browse files Browse the repository at this point in the history
Okta external KM support for APIM 3.0.0
  • Loading branch information
chamilaadhi authored Feb 20, 2020
2 parents 982de46 + 77255c2 commit 540ee22
Show file tree
Hide file tree
Showing 18 changed files with 258 additions and 182 deletions.
300 changes: 141 additions & 159 deletions docs/config.md

Large diffs are not rendered by default.

Binary file modified docs/images/add_application.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/add_application_intro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/authorization_server.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/default_scope.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/delete_application.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/invoke.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/map_application_details.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/map_gen_token.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/scope_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/sign_in.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/signup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/subscribe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/token.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.okta.client</groupId>
<artifactId>OKTA-OAuth-Client</artifactId>
<version>1.0.3-SNAPSHOT</version>
<version>2.0.0</version>
<packaging>bundle</packaging>
<name>Client implementation to integrate with Okta Authorization Server</name>
<url>http://wso2.org</url>
Expand Down Expand Up @@ -133,8 +133,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
Expand Down Expand Up @@ -178,7 +178,7 @@
</plugins>
</build>
<properties>
<carbon.apimgt.version>6.1.66</carbon.apimgt.version>
<carbon.apimgt.version>6.5.349</carbon.apimgt.version>
<json.simple.version>1.1</json.simple.version>
<gson.version>2.1</gson.version>
</properties>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/wso2/okta/client/OktaConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class OktaConstants {
public static final String ACCESS_TOKEN_IDENTIFIER = "jti";
public static final String ACCESS_TOKEN_EXPIRES_IN = "expires_in";
public static final String OKTA_INSTANCE_URL = "oktaInstanceUrl";
public static final String OKTA_DEFAULT_SCOPE = "defaultScope";
public static final String OKTA_AUTHORIZATION_SERVER_ID = "authorizationServerId";
public static final String TOKEN_SCOPE = "tokenScope";
public static final String TOKEN_GRANT_TYPE = "tokenGrantType";
Expand All @@ -75,6 +76,7 @@ public class OktaConstants {
public static final String STRING_FORMAT = "%s %s";
public static final String ERROR_OCCURRED_WHILE_READ_OR_CLOSE_BUFFER_READER = "Error has occurred while reading " +
"or closing buffer reader";
public static final String ADDITIONAL_PROPERTIES = "additionalProperties";

OktaConstants() {
}
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/wso2/okta/client/OktaKeyValidationHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020, 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.okta.client;

import org.wso2.carbon.apimgt.keymgt.APIKeyMgtException;
import org.wso2.carbon.apimgt.keymgt.handlers.DefaultKeyValidationHandler;
import org.wso2.carbon.apimgt.keymgt.service.TokenValidationContext;

public class OktaKeyValidationHandler extends DefaultKeyValidationHandler{

@Override
public boolean validateScopes(TokenValidationContext validationContext) throws APIKeyMgtException {
//Implement scope validation if needed. Currently scope validation is disabled
return true;
}
}
98 changes: 79 additions & 19 deletions src/main/java/org/wso2/okta/client/OktaOAuthClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@
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.api.model.Scope;
import org.wso2.carbon.apimgt.api.model.AccessTokenInfo;
import org.wso2.carbon.apimgt.api.model.API;
import org.wso2.carbon.apimgt.api.model.ApplicationConstants;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.AbstractKeyManager;
import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO;
import org.wso2.carbon.apimgt.impl.factory.KeyManagerHolder;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;

Expand Down Expand Up @@ -169,7 +171,15 @@ public OAuthApplicationInfo createApplication(OAuthAppRequest oAuthAppRequest) t
*/
@Override
public OAuthApplicationInfo updateApplication(OAuthAppRequest oAuthAppRequest) throws APIManagementException {
Object updateAppInOkta = oAuthAppRequest.getOAuthApplicationInfo().getParameter(OktaConstants.UPDATE_APP_IN_OKTA);
JSONParser parser =new JSONParser();
JSONObject additionalProperties = null;
try {
additionalProperties = (JSONObject) parser
.parse((String) oAuthAppRequest.getOAuthApplicationInfo().getParameter(OktaConstants.ADDITIONAL_PROPERTIES));
} catch (ParseException e) {
handleException("Error while parsing additionalProperties.");
}
Object updateAppInOkta = additionalProperties.get(OktaConstants.UPDATE_APP_IN_OKTA);
if (updateAppInOkta == null || !Boolean.valueOf(String.valueOf(updateAppInOkta))) {
return null;
}
Expand Down Expand Up @@ -335,7 +345,41 @@ public OAuthApplicationInfo retrieveApplication(String clientId) throws APIManag
if (statusCode == HttpStatus.SC_OK) {
JSONParser parser = new JSONParser();
responseJSON = parser.parse(reader);
return createOAuthAppInfoFromResponse((JSONObject) responseJSON);
//Since /oauth2/v1/clients/{clientId} does not send the client_secret, we do and update request
//with the same body we receive from GET request. This returns the client secret without changing
//any app related properties. (PUT /oauth2/v1/clients/{clientId})

String jsonPayload = ((JSONObject)responseJSON).toJSONString();
if (log.isDebugEnabled()) {
log.debug(String.format("Payload to update an OAuth client : %s for the Consumer Key %s", jsonPayload,
clientId));
}
HttpPut httpPut = new HttpPut(registrationEndpoint);
httpPut.setEntity(new StringEntity(jsonPayload, OktaConstants.UTF_8));
httpPut.setHeader(OktaConstants.HTTP_HEADER_CONTENT_TYPE, OktaConstants.APPLICATION_JSON);
// Setting Authorization Header, with API Key.
httpPut.setHeader(OktaConstants.AUTHORIZATION, OktaConstants.AUTHENTICATION_SSWS + apiKey);
if (log.isDebugEnabled()) {
log.debug(String.format("Invoking HTTP request to update client in Okta for Consumer Key %s", clientId));
}
response = httpClient.execute(httpPut);
statusCode = response.getStatusLine().getStatusCode();
entity = response.getEntity();
if (entity == null) {
handleException(String.format(OktaConstants.STRING_FORMAT, OktaConstants.ERROR_COULD_NOT_READ_HTTP_ENTITY, response));
}
reader = new BufferedReader(new InputStreamReader(entity.getContent(), OktaConstants.UTF_8));
JSONObject responseObject = getParsedObjectByReader(reader);
if (statusCode == HttpStatus.SC_OK) {
if (responseObject != null) {
return createOAuthAppInfoFromResponse(responseObject);
} else {
handleException("ResponseObject is empty. Can not return oAuthApplicationInfo.");
}
} else {
handleException(String.format("Error occured when updating the Client with Consumer Key %s" +
" : Response: %s", clientId, responseObject.toJSONString()));
}
} else {
handleException(String.format("Error occured while retrieving client for the Consumer Key %s",
clientId));
Expand Down Expand Up @@ -379,7 +423,8 @@ public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest accessTok
parameters.add(new BasicNameValuePair(OktaConstants.GRANT_TYPE, (String) grantType));
String scopeString = convertToString(accessTokenRequest.getScope());
if (StringUtils.isEmpty(scopeString)) {
handleException(String.format("Scope cannot be empty for the Consumer Key %s", clientId));
parameters.add(new BasicNameValuePair(OktaConstants.ACCESS_TOKEN_SCOPE,
configuration.getParameter(OktaConstants.OKTA_DEFAULT_SCOPE)));
} else {
parameters.add(new BasicNameValuePair(OktaConstants.ACCESS_TOKEN_SCOPE, scopeString));
}
Expand Down Expand Up @@ -414,6 +459,7 @@ public AccessTokenInfo getNewApplicationAccessToken(AccessTokenRequest accessTok
@Override
public AccessTokenRequest buildAccessTokenRequestFromOAuthApp(
OAuthApplicationInfo oAuthApplication, AccessTokenRequest tokenRequest) throws APIManagementException {
log.debug("Invoking buildAccessTokenRequestFromOAuthApp() method..");
if (oAuthApplication == null) {
return tokenRequest;
}
Expand Down Expand Up @@ -466,6 +512,7 @@ public AccessTokenRequest buildAccessTokenRequestFromOAuthApp(
@Override
public OAuthApplicationInfo buildFromJSON(OAuthApplicationInfo oAuthApplicationInfo, String jsonInput) throws
APIManagementException {
log.debug("Invoking buildFromJSON() method..");
JSONParser parser = new JSONParser();
JSONObject jsonObject;
try {
Expand Down Expand Up @@ -667,6 +714,15 @@ public AccessTokenInfo getAccessTokenByConsumerKey(String s) throws APIManagemen
private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthApplicationInfo,
Map<String, Object> paramMap) throws APIManagementException {
String clientName = oAuthApplicationInfo.getClientName();
JSONParser parser =new JSONParser();
JSONObject additionalProperties = null;
try {
additionalProperties = (JSONObject) parser
.parse((String) oAuthApplicationInfo.getParameter(OktaConstants.ADDITIONAL_PROPERTIES));
} catch (ParseException e) {
handleException("Error while parsing additionalProperties.");
}
//additionalProperties = JSONParser.pa;
if (log.isDebugEnabled()) {
log.debug(String.format("Creating json payload from Oauth application info for the application: %s",
clientName));
Expand All @@ -682,7 +738,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA
List<String> redirectUris = Collections.singletonList(clientRedirectUri);
paramMap.put(OktaConstants.CLIENT_REDIRECT_URIS, redirectUris);

Object clientResponseTypes = oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_RESPONSE_TYPES);
Object clientResponseTypes = additionalProperties.get(OktaConstants.CLIENT_RESPONSE_TYPES);
if (clientResponseTypes != null) {
String[] responseTypes = ((String) clientResponseTypes).split(",");
JSONArray jsonArray = new JSONArray();
Expand All @@ -692,7 +748,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA
handleException("Mandatory parameter response_types is missing");
}

Object clientGrantTypes = oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_GRANT_TYPES);
Object clientGrantTypes = additionalProperties.get(OktaConstants.CLIENT_GRANT_TYPES);
if (clientGrantTypes != null) {
String[] grantTypes = ((String) clientGrantTypes).split(",");
JSONArray jsonArray = new JSONArray();
Expand All @@ -711,7 +767,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA
paramMap.put(OktaConstants.CLIENT_POST_LOGOUT_REDIRECT_URIS, jsonArray);
}

String tokenEndpointAuthMethod = (String) oAuthApplicationInfo.getParameter(
String tokenEndpointAuthMethod = (String) additionalProperties.get(
OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD);
if (StringUtils.isNotEmpty(tokenEndpointAuthMethod)) {
paramMap.put(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod);
Expand All @@ -734,7 +790,7 @@ private String createJsonPayloadFromOauthApplication(OAuthApplicationInfo oAuthA
paramMap.put(OktaConstants.CLIENT_INITIATE_LOGIN_URI, initiateLoginUri);
}

String applicationType = (String) oAuthApplicationInfo.getParameter(OktaConstants.CLIENT_APPLICATION_TYPE);
String applicationType = (String) additionalProperties.get(OktaConstants.CLIENT_APPLICATION_TYPE);
if (StringUtils.isNotEmpty(applicationType)) {
paramMap.put(OktaConstants.CLIENT_APPLICATION_TYPE, applicationType);
} else {
Expand Down Expand Up @@ -786,17 +842,19 @@ private OAuthApplicationInfo createOAuthAppInfoFromResponse(Map responseMap) {
appInfo.addParameter(OktaConstants.CLIENT_RESPONSE_TYPES, responseTypes);

Object grantTypes = responseMap.get(OktaConstants.CLIENT_GRANT_TYPES);
appInfo.addParameter(OktaConstants.CLIENT_GRANT_TYPES, grantTypes);
appInfo.addParameter(OktaConstants.CLIENT_GRANT_TYPES, String.join(" ", (JSONArray) grantTypes));

Object tokenEndpointAuthMethod = responseMap.get(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD);
appInfo.addParameter(OktaConstants.CLIENT_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod);

Object initiateLoginUri = responseMap.get(OktaConstants.CLIENT_INITIATE_LOGIN_URI);
appInfo.addParameter(OktaConstants.CLIENT_INITIATE_LOGIN_URI, initiateLoginUri);

appInfo.addParameter(OktaConstants.ADDITIONAL_PROPERTIES, responseMap.toString());

return appInfo;
}

/**
* Revokes an access token.
*
Expand Down Expand Up @@ -1043,15 +1101,17 @@ private static void handleException(String msg) throws APIManagementException {
throw new APIManagementException(msg);
}

/**
* Common method to throw exceptions.
*
* @param msg this parameter contain error message that we need to throw.
* @param e Exception object.
* @throws APIManagementException This is the custom exception class for API management
*/
private static void handleException(String msg, Exception e) throws APIManagementException {
log.error(msg, e);
throw new APIManagementException(msg, e);
@Override
public String getNewApplicationConsumerSecret(AccessTokenRequest arg0) throws APIManagementException {
log.warn("Consumer key updating is not supported");
return null;
}

@Override
public Map<String, Set<Scope>> getScopesForAPIS(String apiIdsString) throws APIManagementException {
log.debug("Invoking getScopesForAPIS() method for apiId " + apiIdsString);
ApiMgtDAO apiMgtDAO = ApiMgtDAO.getInstance();
Map<String, Set<Scope>> scopes = apiMgtDAO.getScopesForAPIS(apiIdsString);
return scopes;
}
}

0 comments on commit 540ee22

Please sign in to comment.