Add eidp login support for multi tenant level

Rajitha Kumara 2 months ago
parent 5f94113adf
commit cc264ed5fe

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 - 2024, 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.device.mgt.core.device.mgt.core.config.ui;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "ExtraQueryParam")
public class ExtraQueryParam {
private String queryParam;
private String paramValue;
@XmlElement(name = "QueryParam")
public String getQueryParam() {
return queryParam;
}
public void setQueryParam(String queryParam) {
this.queryParam = queryParam;
}
@XmlElement(name = "ParamValue")
public String getParamValue() {
return paramValue;
}
public void setParamValue(String paramValue) {
this.paramValue = paramValue;
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018 - 2024, 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.device.mgt.core.device.mgt.core.config.ui;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement(name = "SSOConfiguration")
public class SSOConfiguration {
private List<ExtraQueryParam> authEndpointExtraQueryParam;
@XmlElementWrapper(name = "AuthEndpointExtraQueryParams")
@XmlElement(name = "ExtraQueryParam")
public List<ExtraQueryParam> getAuthEndpointExtraQueryParam() {
return authEndpointExtraQueryParam;
}
public void setAuthEndpointExtraQueryParam(List<ExtraQueryParam> authEndpointExtraQueryParam) {
this.authEndpointExtraQueryParam = authEndpointExtraQueryParam;
}
}

@ -35,6 +35,7 @@ public class UIConfiguration {
private int loginCacheCapacity; private int loginCacheCapacity;
private Billing billing; private Billing billing;
private HubspotChat hubspotChat; private HubspotChat hubspotChat;
private SSOConfiguration ssoConfiguration;
private DeviceInfoConfigurations deviceInfoConfigurations; private DeviceInfoConfigurations deviceInfoConfigurations;
@ -121,4 +122,13 @@ public class UIConfiguration {
public void setDeviceStatusConfigurations(DeviceStatusConfigurations deviceStatusConfigurations) { public void setDeviceStatusConfigurations(DeviceStatusConfigurations deviceStatusConfigurations) {
this.deviceStatusConfigurations = deviceStatusConfigurations; this.deviceStatusConfigurations = deviceStatusConfigurations;
} }
@XmlElement(name = "SSOConfiguration", required = true)
public SSOConfiguration getSsoConfiguration() {
return ssoConfiguration;
}
public void setSsoConfiguration(SSOConfiguration ssoConfiguration) {
this.ssoConfiguration = ssoConfiguration;
}
} }

@ -18,7 +18,6 @@
package io.entgra.device.mgt.core.ui.request.interceptor; package io.entgra.device.mgt.core.ui.request.interceptor;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
@ -53,7 +52,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
import java.util.Objects; import java.util.Objects;
@WebServlet( @WebServlet(
@ -65,25 +63,26 @@ import java.util.Objects;
) )
public class JITEnrollmentCallbackHandler extends HttpServlet { public class JITEnrollmentCallbackHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITEnrollmentCallbackHandler.class); private static final Log log = LogFactory.getLog(JITEnrollmentCallbackHandler.class);
private String gatewayUrl;
private String keyManagerUrl; private String keyManagerUrl;
private String JITProvisionCallbackUrl;
private JITData JITInfo; private JITData JITInfo;
private String encodedClientCredentials;
private String applicationName;
private String clientId;
private String clientSecret;
private String scope; private String scope;
private String JITConfigurationPath; private String JITConfigurationPath;
private JITEnrollmentData JITEnrollmentInfo; private JITEnrollmentData JITEnrollmentInfo;
private String code;
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) { protected void doGet(HttpServletRequest request, HttpServletResponse response) {
gatewayUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getGatewayPort(request.getScheme());
keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme()); + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme());
JITProvisionCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath()
+ HandlerConstants.JIT_PROVISION_CALLBACK_URL;
JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml"; JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml";
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
try { try {
if (session == null) { if (session == null) {
@ -91,6 +90,14 @@ public class JITEnrollmentCallbackHandler extends HttpServlet {
return; return;
} }
String state = request.getParameter("state");
if (state == null || !Objects.equals(state, session.getAttribute("state").toString())) {
response.sendError(org.apache.http.HttpStatus.SC_BAD_REQUEST, "MismatchingStateError: CSRF Warning! " +
"State not equal in request and response");
return;
}
code = request.getParameter("code");
JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY); JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY);
if (JITInfo == null) { if (JITInfo == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED); response.sendError(HttpStatus.SC_UNAUTHORIZED);
@ -103,10 +110,8 @@ public class JITEnrollmentCallbackHandler extends HttpServlet {
response.sendError(HttpStatus.SC_UNAUTHORIZED); response.sendError(HttpStatus.SC_UNAUTHORIZED);
return; return;
} }
applicationName = request.getContextPath().substring(1,
request.getContextPath().indexOf("-ui-request-handler"));
initializeJITEnrollmentConfigurations(); initializeJITEnrollmentConfigurations();
populateApplicationData(registerApplication());
persistAuthData(session, getToken()); persistAuthData(session, getToken());
response.sendRedirect(JITEnrollmentInfo.getRedirectUrl() + "?ownershipType=" + response.sendRedirect(JITEnrollmentInfo.getRedirectUrl() + "?ownershipType=" +
JITEnrollmentInfo.getOwnershipType() + "&os=" + JITEnrollmentInfo.getOs() + "&username=" + JITEnrollmentInfo.getOwnershipType() + "&os=" + JITEnrollmentInfo.getOs() + "&username=" +
@ -177,55 +182,6 @@ public class JITEnrollmentCallbackHandler extends HttpServlet {
throw new JITEnrollmentException("Unexpected response body return"); throw new JITEnrollmentException("Unexpected response body return");
} }
/***
* Build application registration request
* @return {@link HttpPost} Application registration request
*/
private HttpPost buildApplicationRegistrationRequest() {
HttpPost applicationRegistrationRequest = new HttpPost(gatewayUrl + HandlerConstants.APP_REG_ENDPOINT);
applicationRegistrationRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC
+ JITInfo.getEncodedClientCredentials());
applicationRegistrationRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
JsonArray tags = new JsonArray();
tags.add("device_management");
JsonObject payload = new JsonObject();
payload.addProperty("applicationName", applicationName);
payload.add("tags", tags);
payload.addProperty("allowedToAllDomains", false);
payload.addProperty("mappingAnExistingOAuthApp", false);
applicationRegistrationRequest.setEntity(new StringEntity(payload.toString(), ContentType.APPLICATION_JSON));
return applicationRegistrationRequest;
}
/***
* Populate dynamic client's data
* @param application - application data receiving from dcr request
*/
private void populateApplicationData(JsonObject application) {
clientId = application.get("client_id").getAsString();
clientSecret = application.get("client_secret").getAsString();
String headerValue = clientId+ ':' + clientSecret;
encodedClientCredentials = Base64.getEncoder().encodeToString(headerValue.getBytes());
}
/***
* Register client application
* @return {@link JsonObject} Json object contain registered application data
* @throws JITEnrollmentException throws when error occurred while application registration
*/
private JsonObject registerApplication() throws JITEnrollmentException {
try {
ProxyResponse proxyResponse = HandlerUtil.execute(buildApplicationRegistrationRequest());
if (proxyResponse.getCode() == HttpStatus.SC_CREATED ||
proxyResponse.getCode() == HttpStatus.SC_OK) {
return parseResponseData(proxyResponse.getData());
}
throw new JITEnrollmentException("Unexpected response status return for application registration request");
} catch (IOException ex) {
throw new JITEnrollmentException("Error occurred while executing application registration request", ex);
}
}
/*** /***
* Acquire token * Acquire token
* @return {@link JsonObject} Json object containing token data * @return {@link JsonObject} Json object containing token data
@ -250,11 +206,11 @@ public class JITEnrollmentCallbackHandler extends HttpServlet {
*/ */
private HttpPost buildTokenAcquireRequest() { private HttpPost buildTokenAcquireRequest() {
HttpPost tokenAcquiringRequest = new HttpPost(keyManagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT); HttpPost tokenAcquiringRequest = new HttpPost(keyManagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT);
tokenAcquiringRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + JITInfo.getEncodedClientCredentials());
tokenAcquiringRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); tokenAcquiringRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString());
tokenAcquiringRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC
+ encodedClientCredentials);
StringEntity payload = new StringEntity( StringEntity payload = new StringEntity(
"grant_type=" + HandlerConstants.CLIENT_CREDENTIAL_GRANT_TYPE + "&scope=" + scope, "grant_type=" + HandlerConstants.CODE_GRANT_TYPE + "&code=" + code + "&scope=" + scope +
"&redirect_uri=" + JITProvisionCallbackUrl,
ContentType.APPLICATION_FORM_URLENCODED); ContentType.APPLICATION_FORM_URLENCODED);
tokenAcquiringRequest.setEntity(payload); tokenAcquiringRequest.setEntity(payload);
return tokenAcquiringRequest; return tokenAcquiringRequest;
@ -268,9 +224,10 @@ public class JITEnrollmentCallbackHandler extends HttpServlet {
private void persistAuthData(HttpSession session, JsonObject token) { private void persistAuthData(HttpSession session, JsonObject token) {
AuthData authData = new AuthData(); AuthData authData = new AuthData();
authData.setAccessToken(token.get("access_token").getAsString()); authData.setAccessToken(token.get("access_token").getAsString());
authData.setClientId(clientId); authData.setRefreshToken(token.get("refresh_token").getAsString());
authData.setClientSecret(clientSecret); authData.setClientId(JITInfo.getClientId());
authData.setEncodedClientApp(encodedClientCredentials); authData.setClientSecret(JITInfo.getClientSecret());
authData.setEncodedClientApp(JITInfo.getEncodedClientCredentials());
authData.setScope(token.get("scope").getAsString()); authData.setScope(token.get("scope").getAsString());
session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData); session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData);
} }

@ -49,6 +49,8 @@ public class JITEnrollmentHandler extends HttpServlet {
private String os; private String os;
private String redirectUrl; private String redirectUrl;
private String tenantDomain; private String tenantDomain;
private String state;
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) { protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try { try {
@ -58,11 +60,12 @@ public class JITEnrollmentHandler extends HttpServlet {
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme()) + HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath() + request.getContextPath()
+ HandlerConstants.JIT_PROVISION_HANDLER; + HandlerConstants.JIT_PROVISION_HANDLER;
String onCompletionUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR String JITEnrollmentCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme()) + HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath() + request.getContextPath()
+ "/jit-enrollment-callback"; + "/jit-enrollment-callback";
state = HandlerUtil.generateStateToken();
username = request.getParameter("username"); username = request.getParameter("username");
ownershipType = request.getParameter("ownershipType"); ownershipType = request.getParameter("ownershipType");
os = request.getParameter("os"); os = request.getParameter("os");
@ -71,7 +74,7 @@ public class JITEnrollmentHandler extends HttpServlet {
String sp = request.getParameter("sp"); String sp = request.getParameter("sp");
persistJITData(session); persistJITData(session);
response.sendRedirect(JITProvisionHandlerUrl + "?tenantDomain=" + tenantDomain response.sendRedirect(JITProvisionHandlerUrl + "?tenantDomain=" + tenantDomain
+ "&sp=" + sp + "&redirectUrl=" + onCompletionUrl); + "&sp=" + sp + "&redirectUrl=" + JITEnrollmentCallbackUrl);
} catch (IOException ex) { } catch (IOException ex) {
log.error("Error occurred while handling JIT enrollment request"); log.error("Error occurred while handling JIT enrollment request");
} }
@ -88,6 +91,7 @@ public class JITEnrollmentHandler extends HttpServlet {
JITEnrollmentInfo.setUsername(username); JITEnrollmentInfo.setUsername(username);
JITEnrollmentInfo.setRedirectUrl(redirectUrl); JITEnrollmentInfo.setRedirectUrl(redirectUrl);
JITEnrollmentInfo.setTenantDomain(tenantDomain); JITEnrollmentInfo.setTenantDomain(tenantDomain);
session.setAttribute("state", state);
session.setAttribute(HandlerConstants.SESSION_JIT_ENROLLMENT_DATA_KEY, JITEnrollmentInfo); session.setAttribute(HandlerConstants.SESSION_JIT_ENROLLMENT_DATA_KEY, JITEnrollmentInfo);
} }
} }

@ -18,13 +18,22 @@
package io.entgra.device.mgt.core.ui.request.interceptor; package io.entgra.device.mgt.core.ui.request.interceptor;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.AuthData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData; import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITEnrollmentData; import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITProvisionException;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants; import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil; import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@ -32,6 +41,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
import java.util.Objects; import java.util.Objects;
@WebServlet( @WebServlet(
@ -43,38 +53,129 @@ import java.util.Objects;
) )
public class JITProvisionCallbackHandler extends HttpServlet { public class JITProvisionCallbackHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITProvisionCallbackHandler.class); private static final Log log = LogFactory.getLog(JITProvisionCallbackHandler.class);
private String keyManagerUrl;
private String JITProvisionCallbackUrl;
private String scope;
private String code;
private JITData JITInfo;
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) { protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String state = request.getParameter("state"); JITProvisionCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
HttpSession session = request.getSession(false);
String JITProvisionCallbackURL = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme()) + HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath() + request.getContextPath()
+ HandlerConstants.JIT_PROVISION_CALLBACK_URL; + HandlerConstants.JIT_PROVISION_CALLBACK_URL;
HttpSession session = request.getSession(false);
try { try {
if (session == null) { if (session == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED); response.sendError(HttpStatus.SC_UNAUTHORIZED);
return; return;
} }
String state = request.getParameter("state");
if (state == null || !Objects.equals(state, session.getAttribute("state").toString())) { if (state == null || !Objects.equals(state, session.getAttribute("state").toString())) {
response.sendError(org.apache.http.HttpStatus.SC_BAD_REQUEST, "MismatchingStateError: CSRF Warning! " + response.sendError(org.apache.http.HttpStatus.SC_BAD_REQUEST, "MismatchingStateError: CSRF Warning! " +
"State not equal in request and response"); "State not equal in request and response");
return; return;
} }
JITData JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY); code = request.getParameter("code");
JITInfo = (JITData) session.getAttribute(HandlerConstants.SESSION_JIT_DATA_KEY);
if (JITInfo == null) { if (JITInfo == null) {
response.sendError(HttpStatus.SC_UNAUTHORIZED); response.sendError(HttpStatus.SC_UNAUTHORIZED);
return; return;
} }
response.sendRedirect(JITInfo.getRedirectUrl() + "?code=" + request.getParameter("code") if (JITInfo.getApplicationUrl() != null) {
+ "&redirectUrl=" + JITProvisionCallbackURL); handleApplicationLogin(request, response, session);
return;
}
response.sendRedirect(JITInfo.getRedirectUrl() + "?code=" + request.getParameter("code") + "&state=" + state);
} catch (IOException ex) { } catch (IOException ex) {
log.error("Error occurred while processing JIT provisioning callback request", ex); log.error("Error occurred while processing JIT provisioning callback request", ex);
} }
} }
private void handleApplicationLogin(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme());
try {
scope = session.getAttribute("scope").toString();
persistAuthData(session, getToken());
response.sendRedirect(JITInfo.getApplicationUrl());
} catch (JITProvisionException | IOException ex) {
log.error("Error encountered while handling login request for " + JITInfo.getApplicationUrl());
}
}
/***
* Parse string data and build json object
* @param data - Json string
* @return {@link JsonObject} Json object corresponding to provided json string
* @throws JITProvisionException throws when error occurred while parsing
*/
private JsonObject parseResponseData(String data) throws JITProvisionException {
JsonParser parser = new JsonParser();
JsonElement responseData = parser.parse(data);
if (responseData.isJsonObject()) {
return responseData.getAsJsonObject();
}
throw new JITProvisionException("Unexpected response body return");
}
/***
* Acquire token
* @return {@link JsonObject} Json object containing token data
* @throws JITProvisionException throws when error occurred while acquiring token
*/
private JsonObject getToken() throws JITProvisionException {
try {
ProxyResponse proxyResponse = HandlerUtil.execute(buildTokenAcquireRequest());
if (proxyResponse.getCode() == org.apache.http.HttpStatus.SC_CREATED ||
proxyResponse.getCode() == org.apache.http.HttpStatus.SC_OK) {
return parseResponseData(proxyResponse.getData());
}
throw new JITProvisionException("Unexpected response status return for token acquiring request");
} catch (IOException ex) {
throw new JITProvisionException("Error occurred while executing token acquiring request", ex);
}
}
/***
* Build token acquire request
* @return {@link HttpPost} Token acquire request
*/
private HttpPost buildTokenAcquireRequest() {
HttpPost tokenAcquiringRequest = new HttpPost(keyManagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT);
tokenAcquiringRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + JITInfo.getEncodedClientCredentials());
tokenAcquiringRequest.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString());
StringEntity payload = new StringEntity(
"grant_type=" + HandlerConstants.CODE_GRANT_TYPE + "&code=" + code + "&scope=" + scope +
"&redirect_uri=" + JITProvisionCallbackUrl,
ContentType.APPLICATION_FORM_URLENCODED);
tokenAcquiringRequest.setEntity(payload);
return tokenAcquiringRequest;
}
/***
* Persists auth data in session
* @param session {@link HttpSession}
* @param token Json object containing token data
*/
private void persistAuthData(HttpSession session, JsonObject token) {
AuthData authData = new AuthData();
authData.setAccessToken(token.get("access_token").getAsString());
authData.setRefreshToken(token.get("refresh_token").getAsString());
authData.setClientId(JITInfo.getClientId());
authData.setClientSecret(JITInfo.getClientSecret());
authData.setEncodedClientApp(JITInfo.getEncodedClientCredentials());
authData.setScope(token.get("scope").getAsString());
session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData);
}
} }

@ -18,22 +18,18 @@
package io.entgra.device.mgt.core.ui.request.interceptor; package io.entgra.device.mgt.core.ui.request.interceptor;
import com.google.gson.JsonElement; import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData; import io.entgra.device.mgt.core.ui.request.interceptor.beans.JITData;
import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse; import io.entgra.device.mgt.core.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITEnrollmentException; import io.entgra.device.mgt.core.ui.request.interceptor.beans.ServiceProvider;
import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITProvisionException; import io.entgra.device.mgt.core.ui.request.interceptor.exceptions.JITProvisionException;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants; import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerConstants;
import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil; import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@ -52,9 +48,11 @@ import javax.xml.parsers.ParserConfigurationException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@WebServlet( @WebServlet(
@ -66,42 +64,63 @@ import java.util.Objects;
) )
public class JITProvisionHandler extends HttpServlet { public class JITProvisionHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(JITProvisionHandler.class); private static final Log log = LogFactory.getLog(JITProvisionHandler.class);
private static final String JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml";
private static final Gson gson = new Gson();
private static Map<String, ServiceProvider> serviceProviders;
private String tenantDomain; private String tenantDomain;
private String clientId; private String clientId;
private String clientSecret;
private String JITServiceProviderName; private String JITServiceProviderName;
private String encodedClientCredentials; private String encodedClientCredentials;
private String JITConfigurationPath;
private String redirectUrl; private String redirectUrl;
private String applicationUrl;
private String state; private String state;
private static final Map<String, Element> tenantConfigs = new HashMap<>(); private String scope;
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) { protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR String keyManagerUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme()); + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(request.getScheme());
String iotCoreUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme());
String gatewayUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getGatewayPort(request.getScheme());
String JITCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR String JITCallbackUrl = request.getScheme() + HandlerConstants.SCHEME_SEPARATOR
+ System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme()) + HandlerConstants.COLON + HandlerUtil.getCorePort(request.getScheme())
+ request.getContextPath() + request.getContextPath()
+ HandlerConstants.JIT_PROVISION_CALLBACK_URL; + HandlerConstants.JIT_PROVISION_CALLBACK_URL;
JITConfigurationPath = CarbonUtils.getCarbonConfigDirPath() + File.separator + "jit-config.xml"; String uiConfigUrl = iotCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT;
String scope = "openid";
HttpSession session = request.getSession(true);
state = HandlerUtil.generateStateToken(); state = HandlerUtil.generateStateToken();
tenantDomain = request.getParameter("tenantDomain"); tenantDomain = request.getParameter("tenantDomain");
redirectUrl = request.getParameter("redirectUrl"); redirectUrl = request.getParameter("redirectUrl");
JITServiceProviderName = request.getParameter("sp"); JITServiceProviderName = request.getParameter("sp");
applicationUrl = request.getParameter("applicationUrl");
try { try {
JsonObject uiConfigJsonObject = HandlerUtil.getUIConfigAndPersistInSession(uiConfigUrl, gatewayUrl, session, response);
JsonArray scopeJson = uiConfigJsonObject.get("scopes").getAsJsonArray();
scope = "openid " + HandlerUtil.getScopeString(scopeJson);
if (tenantDomain == null || JITServiceProviderName == null) { if (tenantDomain == null || JITServiceProviderName == null) {
HandlerUtil.handleError(response, HttpStatus.SC_BAD_REQUEST); HandlerUtil.handleError(response, HttpStatus.SC_BAD_REQUEST);
return; return;
} }
if (serviceProviders == null) {
loadServiceProviders();
}
if (!initializeJITConfigurations()) { if (!initializeJITConfigurations()) {
HandlerUtil.handleError(response, HttpStatus.SC_SERVICE_UNAVAILABLE); HandlerUtil.handleError(response, HttpStatus.SC_SERVICE_UNAVAILABLE);
return; return;
} }
persistJITData(request.getSession(true)); persistJITData(session);
response.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT + response.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT +
"?response_type=code" + "?response_type=code" +
"&client_id=" + clientId + "&client_id=" + clientId +
@ -113,6 +132,32 @@ public class JITProvisionHandler extends HttpServlet {
} }
} }
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
tenantDomain = request.getParameter("tenantDomain");
try {
if (tenantDomain == null) {
HandlerUtil.handleError(response, HttpStatus.SC_BAD_REQUEST);
return;
}
if (serviceProviders == null) {
loadServiceProviders();
}
List<ServiceProvider.WhiteLabel> supportingServiceProviders = serviceProviders.values().stream().
filter(serviceProvider -> Objects.equals(tenantDomain,
serviceProvider.getTenantDomain())).map(ServiceProvider::getWhiteLabel).collect(Collectors.toList());
ProxyResponse proxyResponse = new ProxyResponse();
proxyResponse.setData(gson.toJson(supportingServiceProviders));
proxyResponse.setStatus(ProxyResponse.Status.SUCCESS);
proxyResponse.setCode(HttpStatus.SC_OK);
HandlerUtil.handleSuccess(response, proxyResponse);
} catch (JITProvisionException | IOException ex) {
log.error("Error occurred while processing request", ex);
}
}
/*** /***
* Retrieve JIT data from current session if session exists, otherwise build and return * Retrieve JIT data from current session if session exists, otherwise build and return
* @param session - {@link HttpSession} * @param session - {@link HttpSession}
@ -130,69 +175,74 @@ public class JITProvisionHandler extends HttpServlet {
private void persistJITData(HttpSession session) { private void persistJITData(HttpSession session) {
JITData JITInfo = getJITData(session); JITData JITInfo = getJITData(session);
JITInfo.setEncodedClientCredentials(encodedClientCredentials); JITInfo.setEncodedClientCredentials(encodedClientCredentials);
JITInfo.setClientId(clientId);
JITInfo.setClientSecret(clientSecret);
JITInfo.setTenantDomain(tenantDomain); JITInfo.setTenantDomain(tenantDomain);
JITInfo.setRedirectUrl(redirectUrl); JITInfo.setRedirectUrl(redirectUrl);
JITInfo.setSp(JITServiceProviderName); JITInfo.setSp(JITServiceProviderName);
JITInfo.setApplicationUrl(applicationUrl);
session.setMaxInactiveInterval(3600); session.setMaxInactiveInterval(3600);
session.setAttribute("state", state); session.setAttribute("state", state);
session.setAttribute("scope", scope);
session.setAttribute(HandlerConstants.SESSION_JIT_DATA_KEY, JITInfo); session.setAttribute(HandlerConstants.SESSION_JIT_DATA_KEY, JITInfo);
} }
/*** private void loadServiceProviders() throws JITProvisionException {
* Find the tenant based configurations and return serviceProviders = new ConcurrentHashMap<>();
* @param tenantDomain - Domain of the tenant try {
* @param document - Config doc File JITConfigurationFile = new File(JITConfigurationPath);
* @return {@link Element} If config found return configuration element, otherwise null DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
*/ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
private Element findServiceProvider(String tenantDomain, Document document) { Document JITConfigurationDoc = documentBuilder.parse(JITConfigurationFile);
NodeList serviceProviderConfiguration = document.getElementsByTagName("ServiceProvider"); JITConfigurationDoc.getDocumentElement().normalize();
NodeList serviceProviderConfiguration = JITConfigurationDoc.getElementsByTagName("ServiceProvider");
for (int idx = 0; idx < serviceProviderConfiguration.getLength(); idx++) { for (int idx = 0; idx < serviceProviderConfiguration.getLength(); idx++) {
Node configNode = serviceProviderConfiguration.item(idx); Node configNode = serviceProviderConfiguration.item(idx);
if (configNode.getNodeType() == Node.ELEMENT_NODE) { if (configNode.getNodeType() == Node.ELEMENT_NODE) {
Element configElement = (Element) configNode; Element configElement = (Element) configNode;
if (Objects.equals(configElement.getAttributes(). ServiceProvider serviceProvider = new ServiceProvider();
getNamedItem("tenantDomain").getNodeValue(), tenantDomain) && serviceProvider.setTenantDomain(configElement.getAttributes().getNamedItem("tenantDomain").
Objects.equals(configElement.getAttributes().getNamedItem("name").getNodeValue(), getNodeValue());
JITServiceProviderName)) { serviceProvider.setName(configElement.getAttributes().getNamedItem("name").getNodeValue());
return configElement; serviceProvider.setClientId(configElement.getElementsByTagName("ClientId").item(0).
getTextContent());
serviceProvider.setClientSecret(configElement.getElementsByTagName("ClientSecret").item(0).
getTextContent());
ServiceProvider.WhiteLabel whiteLabel = new ServiceProvider.WhiteLabel(configElement.getElementsByTagName("DisplayName").item(0).
getTextContent(), configElement.getElementsByTagName("IconUrl").item(0).
getTextContent());
whiteLabel.setTenantDomain(serviceProvider.getTenantDomain());
whiteLabel.setName(serviceProvider.getName());
serviceProvider.setWhiteLabel(whiteLabel);
String spKey = serviceProvider.getName() + "@" + serviceProvider.getTenantDomain();
serviceProviders.putIfAbsent(spKey, serviceProvider);
} }
} }
} catch (ParserConfigurationException ex) {
String msg = "Error occurred when document builder creating the file configuration";
throw new JITProvisionException(msg, ex);
} catch (IOException ex) {
String msg = "IO error occurred while parsing the JIT config file";
throw new JITProvisionException(msg, ex);
} catch (SAXException ex) {
String msg = "Parse error occurred while parsing the JIT config document";
throw new JITProvisionException(msg, ex);
} }
return null;
} }
/*** /***
* Initialize JIT configurations * Initialize JIT configurations
* @return boolean true when successful initialization, otherwise false * @return boolean true when successful initialization, otherwise false
* @throws JITProvisionException throws when error occurred
*/ */
private boolean initializeJITConfigurations() throws JITProvisionException { private boolean initializeJITConfigurations() {
try { ServiceProvider serviceProvider = serviceProviders.get(JITServiceProviderName + "@" + tenantDomain);
Element serviceProvider = tenantConfigs.get(tenantDomain);
if (serviceProvider == null) { if (serviceProvider == null) {
File JITConfigurationFile = new File(JITConfigurationPath); return false;
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document JITConfigurationDoc = documentBuilder.parse(JITConfigurationFile);
JITConfigurationDoc.getDocumentElement().normalize();
serviceProvider = findServiceProvider(tenantDomain, JITConfigurationDoc);
if (serviceProvider == null) return false;
tenantConfigs.put(tenantDomain, serviceProvider);
} }
clientId = serviceProvider.getElementsByTagName("ClientId").item(0).getTextContent(); clientId = serviceProvider.getClientId();
String clientSecret = serviceProvider.getElementsByTagName("ClientSecret").item(0).getTextContent(); clientSecret = serviceProvider.getClientSecret();
String headerValue = clientId + ":" + clientSecret; String headerValue = clientId + ":" + clientSecret;
encodedClientCredentials = Base64.getEncoder().encodeToString(headerValue.getBytes()); encodedClientCredentials = Base64.getEncoder().encodeToString(headerValue.getBytes());
return true; return true;
} catch (ParserConfigurationException ex) {
String msg = "Error occurred when document builder creating the file configuration";
throw new JITProvisionException(msg, ex);
} catch (IOException ex) {
String msg = "IO error occurred while parsing the JIT config file";
throw new JITProvisionException(msg, ex);
} catch (SAXException ex) {
String msg = "Parse error occurred while parsing the JIT config document";
throw new JITProvisionException(msg, ex);
}
} }
} }

@ -130,12 +130,16 @@ public class SsoLoginHandler extends HttpServlet {
String loginCallbackUrl = iotsCoreUrl + baseContextPath + HandlerConstants.SSO_LOGIN_CALLBACK; String loginCallbackUrl = iotsCoreUrl + baseContextPath + HandlerConstants.SSO_LOGIN_CALLBACK;
persistAuthSessionData(req, oAuthApp.getClientId(), oAuthApp.getClientSecret(), persistAuthSessionData(req, oAuthApp.getClientId(), oAuthApp.getClientSecret(),
oAuthApp.getEncodedClientApp(), scopesSsoString, state); oAuthApp.getEncodedClientApp(), scopesSsoString, state);
JsonObject ssoConfigurations = uiConfigJsonObject.getAsJsonObject("ssoConfiguration");
JsonArray authEndpointExtraQueryParam = ssoConfigurations.getAsJsonArray("authEndpointExtraQueryParam");
String extraParamString = HandlerUtil.getAuthEndpointExtraQueryParamString(authEndpointExtraQueryParam);
resp.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT + resp.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT +
"?response_type=code" + "?response_type=code" +
"&state=" + state + "&state=" + state +
"&client_id=" + clientId + "&client_id=" + clientId +
"&scope=openid " + scopesSsoString + "&scope=openid " + scopesSsoString +
"&redirect_uri=" + loginCallbackUrl); "&redirect_uri=" + loginCallbackUrl +
extraParamString);
} catch (IOException e) { } catch (IOException e) {
log.error("Error occurred while sending the response into the socket. ", e); log.error("Error occurred while sending the response into the socket. ", e);
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {

@ -25,6 +25,9 @@ public class JITData {
private String redirectUrl; private String redirectUrl;
private String sp; private String sp;
private String encodedClientCredentials; private String encodedClientCredentials;
private String clientId;
private String clientSecret;
private String applicationUrl;
public String getUsername() { public String getUsername() {
return username; return username;
@ -65,4 +68,28 @@ public class JITData {
public void setEncodedClientCredentials(String encodedClientCredentials) { public void setEncodedClientCredentials(String encodedClientCredentials) {
this.encodedClientCredentials = encodedClientCredentials; this.encodedClientCredentials = encodedClientCredentials;
} }
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 getApplicationUrl() {
return applicationUrl;
}
public void setApplicationUrl(String applicationUrl) {
this.applicationUrl = applicationUrl;
}
} }

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018 - 2024, 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.device.mgt.core.ui.request.interceptor.beans;
public class ServiceProvider {
private String tenantDomain;
private String name;
private String clientId;
private String clientSecret;
private WhiteLabel whiteLabel;
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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 WhiteLabel getWhiteLabel() {
return whiteLabel;
}
public void setWhiteLabel(WhiteLabel whiteLabel) {
this.whiteLabel = whiteLabel;
}
public static class WhiteLabel {
private String displayName;
private String iconUrl;
private String tenantDomain;
private String name;
public WhiteLabel(String displayName, String iconUrl) {
this.displayName = displayName;
this.iconUrl = iconUrl;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getIconUrl() {
return iconUrl;
}
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

@ -799,4 +799,18 @@ public class HandlerUtil {
public static String generateStateToken() { public static String generateStateToken() {
return new BigInteger(130, new SecureRandom()).toString(32); return new BigInteger(130, new SecureRandom()).toString(32);
} }
public static String getAuthEndpointExtraQueryParamString(JsonArray extraQueryParams) {
StringBuilder stringBuilder = new StringBuilder();
if (extraQueryParams != null && extraQueryParams.size() > 0) {
for (JsonElement param : extraQueryParams) {
if (param.isJsonObject()) {
JsonObject paramObj = param.getAsJsonObject();
stringBuilder.append("&").append(paramObj.get("queryParam").getAsString()).append("=").
append(paramObj.get("paramValue").getAsString());
}
}
}
return stringBuilder.toString();
}
} }

@ -39,6 +39,8 @@
<!--<ServiceProvider tenantDomain="" name=""> <!--<ServiceProvider tenantDomain="" name="">
<ClientId></ClientId> <ClientId></ClientId>
<ClientSecret></ClientSecret> <ClientSecret></ClientSecret>
<DisplayName></DisplayName>
<IconUrl></IconUrl>
</ServiceProvider>--> </ServiceProvider>-->
</ServiceProviderConfiguration> </ServiceProviderConfiguration>
</JITConfiguration> </JITConfiguration>

@ -429,6 +429,16 @@
</Scopes> </Scopes>
<SSOConfiguration> <SSOConfiguration>
<Issuer>device-mgt</Issuer> <Issuer>device-mgt</Issuer>
<AuthEndpointExtraQueryParams>
<ExtraQueryParam>
<QueryParam>idpAvailability</QueryParam>
<ParamValue>false</ParamValue>
</ExtraQueryParam>
<ExtraQueryParam>
<QueryParam>idpLoginProviderEndpoint</QueryParam>
<ParamValue>https://localhost:9443/enroll-web-agent/idp/login</ParamValue>
</ExtraQueryParam>
</AuthEndpointExtraQueryParams>
</SSOConfiguration> </SSOConfiguration>
</UIConfiguration> </UIConfiguration>

Loading…
Cancel
Save