Add login cache for sso and non-sso flow

(cherry picked from commit 872299a617)
4.x.x
Vigneshan Seshamany 4 years ago committed by Pahansith
parent 6a7099f97f
commit 143d62efd5

@ -24,6 +24,9 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import io.entgra.ui.request.interceptor.beans.AuthData;
import io.entgra.ui.request.interceptor.cache.LoginCacheManager;
import io.entgra.ui.request.interceptor.cache.OAuthApp;
import io.entgra.ui.request.interceptor.cache.OAuthAppCacheKey;
import io.entgra.ui.request.interceptor.exceptions.LoginException;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import io.entgra.ui.request.interceptor.util.HandlerUtil;
@ -74,6 +77,13 @@ public class LoginHandler extends HttpServlet {
JsonArray tags = uiConfigJsonObject.get("appRegistration").getAsJsonObject().get("tags").getAsJsonArray();
JsonArray scopes = uiConfigJsonObject.get("scopes").getAsJsonArray();
// Check if OAuth app cache exists. If not create a new application.
LoginCacheManager loginCacheManager = new LoginCacheManager();
loginCacheManager.initializeCacheManager();
OAuthAppCacheKey oAuthAppCacheKey = new OAuthAppCacheKey(HandlerConstants.PUBLISHER_APPLICATION_NAME, username);
OAuthApp oAuthApp = loginCacheManager.getOAuthAppCache(oAuthAppCacheKey);
if (oAuthApp == null) {
HttpPost apiRegEndpoint = new HttpPost(gatewayUrl + HandlerConstants.APP_REG_ENDPOINT);
apiRegEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + Base64.getEncoder()
.encodeToString((username + HandlerConstants.COLON + password).getBytes()));
@ -86,14 +96,42 @@ public class LoginHandler extends HttpServlet {
HandlerUtil.handleError(resp, clientAppResponse);
return;
}
if (clientAppResponse.getCode() == HttpStatus.SC_CREATED && getTokenAndPersistInSession(req, resp,
clientAppResponse.getData(), scopes)) {
if (clientAppResponse.getCode() == HttpStatus.SC_CREATED) {
JsonParser jsonParser = new JsonParser();
JsonElement jClientAppResult = jsonParser.parse(clientAppResponse.getData());
String clientId = null;
String clientSecret = null;
String encodedClientApp = null;
if (jClientAppResult.isJsonObject()) {
JsonObject jClientAppResultAsJsonObject = jClientAppResult.getAsJsonObject();
clientId = jClientAppResultAsJsonObject.get("client_id").getAsString();
clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString();
encodedClientApp = Base64.getEncoder()
.encodeToString((clientId + HandlerConstants.COLON + clientSecret).getBytes());
oAuthAppCacheKey = new OAuthAppCacheKey(HandlerConstants.PUBLISHER_APPLICATION_NAME, username);
oAuthApp = new OAuthApp(
HandlerConstants.PUBLISHER_APPLICATION_NAME,
username,
clientId,
clientSecret,
encodedClientApp
);
loginCacheManager.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp);
}
if (getTokenAndPersistInSession(req, resp, clientId, clientSecret, encodedClientApp, scopes)) {
ProxyResponse proxyResponse = new ProxyResponse();
proxyResponse.setCode(HttpStatus.SC_OK);
HandlerUtil.handleSuccess(resp, proxyResponse);
return;
}
}
HandlerUtil.handleError(resp, null);
} else {
getTokenAndPersistInSession(req, resp, oAuthApp.getClientId(), oAuthApp.getClientSecret(), oAuthApp.getEncodedClientApp(), scopes);
}
} catch (IOException e) {
log.error("Error occurred while sending the response into the socket. ", e);
} catch (JsonSyntaxException e) {
@ -103,25 +141,23 @@ public class LoginHandler extends HttpServlet {
}
}
/***
/**
* Generates token from token endpoint and persists them inside the session
*
* @param req - {@link HttpServletRequest}
* @param clientAppResult - clientAppResult
* @param scopes - scopes defied in the application-mgt.xml
* @throws LoginException - login exception throws when getting token result
* @param resp - {@link HttpServletResponse}
* @param clientId - clientId of the OAuth app
* @param clientSecret - clientSecret of the OAuth app
* @param encodedClientApp - Base64 encoded clientId:clientSecret.
* @param scopes - User scopes JSON Array
* @return boolean response
* @throws LoginException - Throws if any error occurs while getting login response
*/
private boolean getTokenAndPersistInSession(HttpServletRequest req, HttpServletResponse resp,
String clientAppResult, JsonArray scopes) throws LoginException {
String clientId, String clientSecret, String encodedClientApp,
JsonArray scopes) throws LoginException {
JsonParser jsonParser = new JsonParser();
try {
JsonElement jClientAppResult = jsonParser.parse(clientAppResult);
if (jClientAppResult.isJsonObject()) {
JsonObject jClientAppResultAsJsonObject = jClientAppResult.getAsJsonObject();
String clientId = jClientAppResultAsJsonObject.get("client_id").getAsString();
String clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString();
String encodedClientApp = Base64.getEncoder()
.encodeToString((clientId + HandlerConstants.COLON + clientSecret).getBytes());
ProxyResponse tokenResultResponse = getTokenResult(encodedClientApp, scopes);
@ -154,7 +190,6 @@ public class LoginHandler extends HttpServlet {
session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData);
return true;
}
}
return false;
} catch (IOException e) {
throw new LoginException("Error occurred while sending the response into the socket", e);

@ -23,6 +23,9 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import io.entgra.ui.request.interceptor.cache.LoginCacheManager;
import io.entgra.ui.request.interceptor.cache.OAuthApp;
import io.entgra.ui.request.interceptor.cache.OAuthAppCacheKey;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import io.entgra.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.lang.text.StrSubstitutor;
@ -72,24 +75,62 @@ public class SsoLoginHandler extends HttpServlet {
private static String encodedAdminCredentials;
private static String encodedClientApp;
private static String applicationId;
private static String applicationName;
private static String baseContextPath;
private JsonObject uiConfigJsonObject;
private HttpSession httpSession;
private LoginCacheManager loginCacheManager;
private OAuthApp oAuthApp;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
httpSession = req.getSession(false);
if (httpSession != null) {
httpSession.invalidate();
}
httpSession = req.getSession(true);
initializeAdminCredentials();
baseContextPath = req.getContextPath();
applicationName = baseContextPath.substring(1, baseContextPath.indexOf("-ui-request-handler"));
// Check if oauth app cache is available
loginCacheManager = new LoginCacheManager();
loginCacheManager.initializeCacheManager();
oAuthApp = loginCacheManager.getOAuthAppCache(
new OAuthAppCacheKey(applicationName, adminUsername)
);
if (oAuthApp == null) {
dynamicClientRegistration(req, resp);
String clientId = httpSession.getAttribute("clientId").toString();
}
String clientId = oAuthApp.getClientId();
JsonArray scopesSsoJson = uiConfigJsonObject.get("scopes").getAsJsonArray();
String scopesSsoString = HandlerUtil.getScopeString(scopesSsoJson);
String loginCallbackUrl = iotsCoreUrl + baseContextPath + HandlerConstants.SSO_LOGIN_CALLBACK;
persistAuthSessionData(req, oAuthApp.getClientId(), oAuthApp.getClientSecret(),
oAuthApp.getEncodedClientApp(), scopesSsoString);
resp.sendRedirect(iotsCoreUrl + HandlerConstants.AUTHORIZATION_ENDPOINT +
"?response_type=code" +
"&client_id=" + clientId +
"&state=" +
"&scope=openid " + scopesSsoString +
"&redirect_uri=" + loginCallbackUrl);
} catch (IOException e) {
log.error("Error occurred while sending the response into the socket. ", e);
} catch (JsonSyntaxException e) {
log.error("Error occurred while parsing the response. ", e);
} catch (ParserConfigurationException e) {
log.error("Error while creating the document builder.");
} catch (SAXException e) {
log.error("Error while parsing xml file.", e);
}
}
/***
@ -101,17 +142,6 @@ public class SsoLoginHandler extends HttpServlet {
*/
private void dynamicClientRegistration(HttpServletRequest req, HttpServletResponse resp) {
try {
File userMgtConf = new File("conf/user-mgt.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(userMgtConf);
adminUsername = doc.getElementsByTagName("UserName").item(0).getTextContent();
adminPassword = doc.getElementsByTagName("Password").item(0).getTextContent();
baseContextPath = req.getContextPath();
String applicationName = baseContextPath.substring(1, baseContextPath.indexOf("-ui-request-handler"));
String iotsCorePort = System.getProperty("iot.core.https.port");
if (HandlerConstants.HTTP_PROTOCOL.equals(req.getScheme())) {
@ -124,14 +154,7 @@ public class SsoLoginHandler extends HttpServlet {
+ HandlerConstants.COLON + iotsCorePort;
String uiConfigUrl = iotsCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT;
httpSession = req.getSession(false);
if (httpSession != null) {
httpSession.invalidate();
}
httpSession = req.getSession(true);
uiConfigJsonObject = HandlerUtil.getUIConfigAndPersistInSession(uiConfigUrl, gatewayUrl, httpSession, resp);
JsonArray tags = uiConfigJsonObject.get("appRegistration").getAsJsonObject().get("tags").getAsJsonArray();
JsonArray scopes = uiConfigJsonObject.get("scopes").getAsJsonArray();
@ -153,19 +176,22 @@ public class SsoLoginHandler extends HttpServlet {
if (clientAppResponse.getCode() == HttpStatus.SC_CREATED) {
JsonParser jsonParser = new JsonParser();
JsonElement jClientAppResult = jsonParser.parse(clientAppResponse.getData());
String clientId = null;
String clientSecret = null;
if (jClientAppResult.isJsonObject()) {
JsonObject jClientAppResultAsJsonObject = jClientAppResult.getAsJsonObject();
String clientId = jClientAppResultAsJsonObject.get("client_id").getAsString();
String clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString();
clientId = jClientAppResultAsJsonObject.get("client_id").getAsString();
clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString();
encodedClientApp = Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes());
String redirectUrl = req.getParameter("redirect");
httpSession = req.getSession(false);
httpSession.setAttribute("clientId", clientId);
httpSession.setAttribute("clientSecret", clientSecret);
httpSession.setAttribute("encodedClientApp", encodedClientApp);
httpSession.setAttribute("scope", HandlerUtil.getScopeString(scopes));
httpSession.setAttribute("redirectUrl", redirectUrl);
String scopesString = HandlerUtil.getScopeString(scopes);
persistAuthSessionData(req, clientId, clientSecret, encodedClientApp, scopesString);
}
// cache the oauth app credentials
OAuthAppCacheKey oAuthAppCacheKey = new OAuthAppCacheKey(applicationName, adminUsername);
oAuthApp = new OAuthApp(applicationName, adminUsername, clientId, clientSecret, encodedClientApp);
loginCacheManager.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp);
}
// Get the details of the registered application
@ -221,7 +247,6 @@ public class SsoLoginHandler extends HttpServlet {
if (updateApplicationGrantTypesEndpointResponse.getCode() == HttpStatus.SC_OK) {
return;
}
HandlerUtil.handleError(resp, null);
} catch (IOException e) {
log.error("Error occurred while sending the response into the socket. ", e);
@ -234,6 +259,43 @@ public class SsoLoginHandler extends HttpServlet {
}
}
/**
* Initialize the admin credential variables
*
* @throws ParserConfigurationException - Throws when error occur during initializing the document builder
* @throws IOException - Throws when error occur during document parsing
* @throws SAXException - Throws when error occur during document parsing
*/
private void initializeAdminCredentials() throws ParserConfigurationException, IOException, SAXException {
File userMgtConf = new File("conf/user-mgt.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(userMgtConf);
adminUsername = doc.getElementsByTagName("UserName").item(0).getTextContent();
adminPassword = doc.getElementsByTagName("Password").item(0).getTextContent();
}
/**
* Persist the Auth data inside the session
*
* @param req - Http Servlet request
* @param clientId - Client ID of the SP
* @param clientSecret - Client secret of the SP
* @param encodedClientApp - Base64 encoded clientId:clientSecret.
* @param scopes - User scopes
*/
private void persistAuthSessionData(HttpServletRequest req, String clientId, String clientSecret,
String encodedClientApp, String scopes) {
httpSession = req.getSession(false);
httpSession.setAttribute("clientId", clientId);
httpSession.setAttribute("clientSecret", clientSecret);
httpSession.setAttribute("encodedClientApp", encodedClientApp);
httpSession.setAttribute("scope", scopes);
httpSession.setAttribute("redirectUrl", req.getParameter("redirect"));
}
/***
* Generates payload for application grant_type update payload
*
@ -311,7 +373,7 @@ public class SsoLoginHandler extends HttpServlet {
*/
private void updateSaasApp(String appName) throws ParserConfigurationException, IOException, SAXException {
File getAppRequestXmlFile = new File(HandlerConstants.PAYLOADS_DIR + "/get-app-request.xml");
String identityAppMgtUrl = iotsCoreUrl + HandlerConstants.IDENTITY_APP_MGT_ENDPOINT;;
String identityAppMgtUrl = iotsCoreUrl + HandlerConstants.IDENTITY_APP_MGT_ENDPOINT;
HttpPost getApplicationEndpoint = new HttpPost(identityAppMgtUrl);
getApplicationEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC +

@ -0,0 +1,63 @@
/*
* Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.ui.request.interceptor.cache;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
/**
* Contains necessary functions to manage oAuth app cache during login handling
*/
public class LoginCacheManager {
private CacheManager cacheManager = null;
private Cache<OAuthAppCacheKey, OAuthApp> cache = null;
/**
* Initialize the cache manager if it is not already initialized
*/
public void initializeCacheManager() {
cacheManager = Caching.getCacheManagerFactory().getCacheManager(HandlerConstants.LOGIN_CACHE);
}
/**
* Persists OAuth app cache if it is not already persisted
*
* @param oAuthAppCacheKey - The identifier key of the cache
* @param oAuthApp - The value of the cache which contains OAuth app data
*/
public void addOAuthAppToCache(OAuthAppCacheKey oAuthAppCacheKey, OAuthApp oAuthApp) {
cache = cacheManager.getCache(HandlerConstants.LOGIN_CACHE);
cache.put(oAuthAppCacheKey, oAuthApp);
}
/**
* Retrieves the OAuth app cache
*
* @param oAuthAppCacheKey - The key to identify the cache
* @return - Returns OAuthApp object
*/
public OAuthApp getOAuthAppCache(OAuthAppCacheKey oAuthAppCacheKey) {
cache = cacheManager.getCache(HandlerConstants.LOGIN_CACHE);
return cache.get(oAuthAppCacheKey);
}
}

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.ui.request.interceptor.cache;
/**
* The data object used for Login Cache
*/
public class OAuthApp {
private String appName;
private String appOwner;
private String clientId;
private String clientSecret;
private String encodedClientApp;
public OAuthApp(String appName, String appOwner, String clientId, String clientSecret, String encodedClientApp) {
this.appName = appName;
this.appOwner = appOwner;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.encodedClientApp = encodedClientApp;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppOwner() {
return appOwner;
}
public void setAppOwner(String appOwner) {
this.appOwner = appOwner;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public String getEncodedClientApp() {
return encodedClientApp;
}
public void setEncodedClientApp(String encodedClientApp) {
this.encodedClientApp = encodedClientApp;
}
}

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.ui.request.interceptor.cache;
import java.util.Objects;
/**
* The key object used for Login Cache
*/
public class OAuthAppCacheKey {
private String appName;
private String appOwner;
private volatile int hashCode;
public OAuthAppCacheKey(String appName, String appOwner) {
this.appName = appName;
this.appOwner = appOwner;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppOwner() {
return appOwner;
}
public void setAppOwner(String appOwner) {
this.appOwner = appOwner;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof OAuthAppCacheKey) {
final OAuthAppCacheKey other = (OAuthAppCacheKey) obj;
String thisId = this.appName + "-" + this.appOwner;
String otherId = other.appName + "-" + other.appOwner;
return thisId.equals(otherId);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = Objects.hash(appName, appOwner);
}
return hashCode;
}
}

@ -55,6 +55,7 @@ public class HandlerConstants {
public static final String PASSWORD_GRANT_TYPE = "password";
public static final String JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
public static final String PRODUCTION_KEY = "PRODUCTION";
public static final String LOGIN_CACHE = "LOGIN_CACHE";
public static final String SCHEME_SEPARATOR = "://";
public static final String COLON = ":";

Loading…
Cancel
Save