diff --git a/components/request-handler/io.entgra.request.handler/pom.xml b/components/request-handler/io.entgra.request.handler/pom.xml new file mode 100644 index 00000000000..88f207fd963 --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/pom.xml @@ -0,0 +1,155 @@ + + + + + + request-handler + io.entgra.devicemgt + 3.2.5-SNAPSHOT + + 4.0.0 + + io.entgra.request.handler + + 3.2.5-SNAPSHOT + war + Entgra - Request Handling Proxy Servlet + Proxy Service for Request Handling in Entgra EMM/IOT Server. + http://wso2.org + + + + + maven-war-plugin + + WEB-INF/lib/*cxf*.jar + api#application-mgt-handler#v1.0 + + + + + + + + deploy + + compile + + + org.apache.maven.plugins + maven-antrun-plugin + + + compile + + run + + + + + + + + + + + + + + + + + + client + + test + + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + + test + + java + + + + + + + + + + + + org.apache.cxf + cxf-rt-frontend-jaxws + provided + + + org.apache.cxf + cxf-rt-frontend-jaxrs + provided + + + org.apache.cxf + cxf-rt-transports-http + provided + + + javax.servlet + javax.servlet-api + provided + + + junit + junit + test + + + commons-logging + commons-logging + + + + org.apache.httpcomponents + httpclient + + + + + org.apache.httpcomponents + httpcore + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.application.mgt.core + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.application.mgt.common + provided + + + \ No newline at end of file diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/InvokerHandler.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/InvokerHandler.java new file mode 100644 index 00000000000..cff1725b50d --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/InvokerHandler.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2019, 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.request.handler; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.entgra.request.handler.beans.AuthData; +import io.entgra.request.handler.util.HandlerConstants; +import io.entgra.request.handler.util.HandlerUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.wso2.carbon.device.application.mgt.common.ProxyResponse; + +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +import static io.entgra.request.handler.util.HandlerUtil.execute; +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; + +@MultipartConfig +@WebServlet("/invoke") +public class InvokerHandler extends HttpServlet { + private static final Log log = LogFactory.getLog(LoginHandler.class); + private static final long serialVersionUID = -6508020875358160165L; + private static AuthData authData; + private static String apiEndpoint; + private static String method; + private static String serverUrl; + private static String platform; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + try { + if (!validateRequest(req, resp)) { + return; + } + HttpRequestBase executor = constructExecutor(req); + if (executor == null) { + resp.sendError(HTTP_BAD_REQUEST, "Bad Request, method: " + method + " is not supported"); + return; + } + executor.setHeader(HandlerConstants.AUTHORIZATION_HEADER_KEY, "Bearer " + authData.getAccessToken()); + ProxyResponse proxyResponse = execute(executor); + + if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { + if (!refreshToken(req, resp)) { + return; + } + executor.setHeader(HandlerConstants.AUTHORIZATION_HEADER_KEY, "Bearer " + authData.getAccessToken()); + proxyResponse = execute(executor); + if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the API after refreshing the token."); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return; + } + } + if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the API endpoint."); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return; + } + HandlerUtil.handleSuccess(req, resp, serverUrl, platform, proxyResponse); + } catch (IOException e) { + log.error("Error occured when processing invoke call.", e); + } + } + + /*** + * + * @param req {@link HttpServletRequest} + * @return {@link HttpRequestBase} if method equals to either GET, POST, PUT or DELETE otherwise returns NULL. + */ + private HttpRequestBase constructExecutor(HttpServletRequest req) { + String payload = req.getParameter("payload"); + String contentType = req.getParameter("content-type"); + if (contentType == null || contentType.isEmpty()) { + contentType = ContentType.APPLICATION_JSON.toString(); + } + + HttpRequestBase executor; + if (HttpGet.METHOD_NAME.equalsIgnoreCase(method)) { + executor = new HttpGet(serverUrl + HandlerConstants.API_COMMON_CONTEXT + apiEndpoint); + } else if (HttpPost.METHOD_NAME.equalsIgnoreCase(method)) { + executor = new HttpPost(serverUrl + HandlerConstants.API_COMMON_CONTEXT + apiEndpoint); + StringEntity payloadEntity = new StringEntity(payload, ContentType.create(contentType)); + ((HttpPost) executor).setEntity(payloadEntity); + } else if (HttpPut.METHOD_NAME.equalsIgnoreCase(method)) { + executor = new HttpPut(serverUrl + HandlerConstants.API_COMMON_CONTEXT + apiEndpoint); + StringEntity payloadEntity = new StringEntity(payload, ContentType.create(contentType)); + ((HttpPut) executor).setEntity(payloadEntity); + } else if (HttpDelete.METHOD_NAME.equalsIgnoreCase(method)) { + executor = new HttpDelete(serverUrl + HandlerConstants.API_COMMON_CONTEXT + apiEndpoint); + } else { + return null; + } + return executor; + } + + /*** + * + * @param req {@link HttpServletRequest} + * @param resp {@link HttpServletResponse} + * @return If request is a valid one, returns TRUE, otherwise return FALSE + * @throws IOException If and error occurs while witting error response to client side + */ + private static boolean validateRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException { + serverUrl = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort(); + apiEndpoint = req.getParameter("api-endpoint"); + method = req.getParameter("method"); + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Unauthorized, You are not logged in. Please log in to the portal"); + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_UNAUTHORIZED); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return false; + } + authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + platform = (String) session.getAttribute(HandlerConstants.PLATFORM); + if (authData == null) { + log.error("Unauthorized, Access token couldn't found in the current session"); + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_UNAUTHORIZED); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return false; + } + + if (apiEndpoint == null || method == null) { + log.error("Bad Request, Either api-endpoint or method is empty"); + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_BAD_REQUEST); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_BAD_REQUEST)); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return false; + } + return true; + } + + /*** + * + * @param req {@link HttpServletRequest} + * @param resp {@link HttpServletResponse} + * @return If successfully renew tokens, returns TRUE otherwise return FALSE + * @throws IOException If and error occurs while witting error response to client side or invoke token renewal API + */ + private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { + log.debug("refreshing the token"); + HttpPost tokenEndpoint = new HttpPost( + serverUrl + HandlerConstants.API_COMMON_CONTEXT + HandlerConstants.TOKEN_ENDPOINT); + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Couldn't find a session, hence it is required to login and proceed."); + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_UNAUTHORIZED); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return false; + } + + StringEntity tokenEndpointPayload = new StringEntity( + "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", + ContentType.APPLICATION_FORM_URLENCODED); + + tokenEndpoint.setEntity(tokenEndpointPayload); + String encodedClientApp = authData.getEncodedClientApp(); + tokenEndpoint.setHeader("Authorization", "Basic " + encodedClientApp); + tokenEndpoint.setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.toString()); + + ProxyResponse tokenResultResponse = execute(tokenEndpoint); + if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while refreshing access token."); + HandlerUtil.handleError(req, resp, serverUrl, platform, tokenResultResponse); + return false; + } + + JsonParser jsonParser = new JsonParser(); + JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + + if (jTokenResult.isJsonObject()) { + JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); + AuthData newAuthData = new AuthData(); + + newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); + newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); + newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); + newAuthData.setClientId(authData.getClientId()); + newAuthData.setClientSecret(authData.getClientSecret()); + newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); + newAuthData.setUsername(authData.getUsername()); + authData = newAuthData; + session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); + return true; + } + + log.error("Error Occurred in token renewal process."); + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_INTERNAL_SERVER_ERROR)); + HandlerUtil.handleError(req, resp, serverUrl, platform, proxyResponse); + return false; + } +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/LoginHandler.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/LoginHandler.java new file mode 100644 index 00000000000..051ee355044 --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/LoginHandler.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2019, 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.request.handler; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import io.entgra.request.handler.beans.AuthData; +import io.entgra.request.handler.exceptions.LoginException; +import io.entgra.request.handler.util.HandlerConstants; +import io.entgra.request.handler.util.HandlerUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HTTP; +import org.wso2.carbon.device.application.mgt.common.ProxyResponse; +import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; + +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.Base64; + +import static io.entgra.request.handler.util.HandlerUtil.execute; + +@MultipartConfig +@WebServlet("/login") +public class LoginHandler extends HttpServlet { + private static final Log log = LogFactory.getLog(LoginHandler.class); + private static final long serialVersionUID = 9050048549140517002L; + + private static String username; + private static String password; + private static String platform; + private static String serverUrl; + private static String uiConfigUrl; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + try { + validateLoginRequest(req, resp); + DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance() + .getDeviceManagementConfig(); + String adminUsername = deviceManagementConfig.getIdentityConfigurations().getAdminUsername(); + String adminPwd = deviceManagementConfig.getIdentityConfigurations().getAdminPassword(); + + HttpSession httpSession = req.getSession(false); + if (httpSession != null) { + httpSession.invalidate(); + } + httpSession = req.getSession(true); + //setting session to expiry in 5 mins + httpSession.setMaxInactiveInterval(Math.toIntExact(HandlerConstants.TIMEOUT)); + + HttpGet uiConfigEndpoint = new HttpGet(uiConfigUrl); + JsonParser jsonParser = new JsonParser(); + ProxyResponse uiConfigResponse = execute(uiConfigEndpoint); + String executorResponse = uiConfigResponse.getExecutorResponse(); + if (!StringUtils.isEmpty(executorResponse) && executorResponse + .contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while getting UI configurations by invoking " + uiConfigUrl); + HandlerUtil.handleError(req, resp, serverUrl, platform, uiConfigResponse); + return; + } + + String uiConfig = uiConfigResponse.getData(); + if (uiConfig == null){ + log.error("UI config retrieval is failed, and didn't find UI configuration for App manager."); + HandlerUtil.handleError(req, resp, serverUrl, platform, null); + return; + } + JsonElement uiConfigJsonElement = jsonParser.parse(uiConfigResponse.getData()); + JsonObject uiConfigJsonObject = null; + if (uiConfigJsonElement.isJsonObject()) { + uiConfigJsonObject = uiConfigJsonElement.getAsJsonObject(); + httpSession.setAttribute(HandlerConstants.UI_CONFIG_KEY, uiConfigJsonObject); + httpSession.setAttribute(HandlerConstants.PLATFORM, serverUrl); + } + if (uiConfigJsonObject == null) { + log.error( + "Either UI config json element is not an json object or converting rom json element to json object is failed."); + HandlerUtil.handleError(req, resp, serverUrl, platform, null); + return; + } + + boolean isSsoEnable = uiConfigJsonObject.get("isSsoEnable").getAsBoolean(); + JsonArray tags = uiConfigJsonObject.get("appRegistration").getAsJsonObject().get("tags").getAsJsonArray(); + JsonArray scopes = uiConfigJsonObject.get("scopes").getAsJsonArray(); + + if (isSsoEnable) { + log.debug("SSO is enabled"); + } else { + // default login + HttpPost apiRegEndpoint = new HttpPost(serverUrl + HandlerConstants.APP_REG_ENDPOINT); + apiRegEndpoint.setHeader(HandlerConstants.AUTHORIZATION, HandlerConstants.BASIC + Base64.getEncoder() + .encodeToString((adminUsername + HandlerConstants.COLON + adminPwd).getBytes())); + apiRegEndpoint.setHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); + apiRegEndpoint.setEntity(constructAppRegPayload(tags)); + + ProxyResponse clientAppResponse = execute(apiRegEndpoint); + String clientAppResult = clientAppResponse.getData(); + + if (!StringUtils.isEmpty(clientAppResult) && getTokenAndPersistInSession(req, resp, + clientAppResponse.getData(), scopes)) { + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_OK); + proxyResponse.setUrl(serverUrl + "/" + platform + uiConfigJsonObject.get(HandlerConstants.LOGIN_RESPONSE_KEY) + .getAsJsonObject().get("successCallback").getAsString()); + HandlerUtil.handleSuccess(req, resp, serverUrl, platform, proxyResponse); + return; + } + HandlerUtil.handleError(req, resp, serverUrl, platform, null); + } + } catch (IOException e) { + log.error("Error occured while sending the response into the socket. ", e); + } catch (JsonSyntaxException e) { + log.error("Error occured while parsing the response. ", e); + } catch (LoginException e) { + log.error("Error occured while getting token data. ", e); + } + } + + /*** + * + * @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 + */ + private boolean getTokenAndPersistInSession(HttpServletRequest req, HttpServletResponse resp, + String clientAppResult, 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 + ":" + clientSecret).getBytes()); + + ProxyResponse tokenResultResponse = getTokenResult(encodedClientApp, scopes); + + if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the API to get token data."); + HandlerUtil.handleError(req, resp, serverUrl, platform, tokenResultResponse); + return false; + } + String tokenResult = tokenResultResponse.getData(); + if (tokenResult == null){ + log.error("Invalid token response is received."); + HandlerUtil.handleError(req, resp, serverUrl, platform, tokenResultResponse); + return false; + } + + JsonElement jTokenResult = jsonParser.parse(tokenResult); + if (jTokenResult.isJsonObject()) { + JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); + HttpSession session = req.getSession(false); + if (session == null) { + return false; + } + AuthData authData = new AuthData(); + authData.setClientId(clientId); + authData.setClientSecret(clientSecret); + authData.setEncodedClientApp(encodedClientApp); + authData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); + authData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); + authData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); + session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData); + return true; + + } + } + return false; + } catch (IOException e) { + throw new LoginException("Error occured while sending the response into the socket", e); + } + } + + /*** + * + * @param scopes - scope Json Array and it is retrieved by reading UI config. + * @return string value of the defined scopes + */ + private String getScopeString(JsonArray scopes) { + if (scopes != null && scopes.size() > 0) { + StringBuilder builder = new StringBuilder(); + for (JsonElement scope : scopes) { + String tmpscope = scope.getAsString() + " "; + builder.append(tmpscope); + } + return builder.toString(); + } else { + return null; + } + } + + /*** + * + * @param req - {@link HttpServletRequest} + * Define username and password static parameters. + */ + private static void validateLoginRequest(HttpServletRequest req, HttpServletResponse resp) throws LoginException { + username = req.getParameter("username"); + password = req.getParameter("password"); + platform = req.getParameter("platform"); + serverUrl = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort(); + uiConfigUrl = serverUrl + HandlerConstants.UI_CONFIG_ENDPOINT; + + try { + if (platform == null) { + resp.sendRedirect(serverUrl + HandlerConstants.DEFAULT_ERROR_CALLBACK); + throw new LoginException("Invalid login request. Platform parameter is Null."); + } + if (username == null || password == null) { + resp.sendRedirect(serverUrl + "/" + platform + HandlerConstants.DEFAULT_ERROR_CALLBACK); + throw new LoginException( + " Invalid login request. Username or Password is not received for login request."); + } + } catch (IOException e) { + throw new LoginException("Error Occured while redirecting to default error page.", e); + } + } + + /*** + * + * @param tags - tags which are retrieved by reading app manager configuration + * @return {@link StringEntity} of the payload to create the client application + */ + private StringEntity constructAppRegPayload(JsonArray tags) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty(HandlerConstants.APP_NAME_KEY, HandlerConstants.PUBLISHER_APPLICATION_NAME); + jsonObject.addProperty("isAllowedToAllDomains", "false"); + jsonObject.add(HandlerConstants.TAGS_KEY, tags); + String payload = jsonObject.toString(); + return new StringEntity(payload, ContentType.APPLICATION_JSON); + } + + /*** + * + * @param encodedClientApp - Base64 encoded clientId:clientSecret. + * @param scopes - Scopes which are retrieved by reading application-mgt configuration + * @return Invoke token endpoint and return the response as string. + * @throws IOException IO exception throws if an error occured when invoking token endpoint + */ + private ProxyResponse getTokenResult(String encodedClientApp, JsonArray scopes) throws IOException { + + HttpPost tokenEndpoint = new HttpPost(serverUrl + HandlerConstants.TOKEN_ENDPOINT); + tokenEndpoint.setHeader("Authorization", "Basic " + encodedClientApp); + tokenEndpoint.setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.toString()); + String scopeString = getScopeString(scopes); + + if (scopeString != null) { + scopeString = scopeString.trim(); + } else { + scopeString = "default"; + } + + StringEntity tokenEPPayload = new StringEntity( + "grant_type=password&username=" + username + "&password=" + password + "&scope=" + scopeString, + ContentType.APPLICATION_FORM_URLENCODED); + tokenEndpoint.setEntity(tokenEPPayload); + return execute(tokenEndpoint); + } +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/beans/AuthData.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/beans/AuthData.java new file mode 100644 index 00000000000..3ad73d6e8ea --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/beans/AuthData.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, 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.request.handler.beans; + + +public class AuthData implements java.io.Serializable { + + private static final long serialVersionUID = -5156750882531944849L; + + private String accessToken; + private String refreshToken; + private String username; + private String clientId; + private String clientSecret; + private String encodedClientApp; + private String scope; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + 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; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/exceptions/LoginException.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/exceptions/LoginException.java new file mode 100644 index 00000000000..d792924fa48 --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/exceptions/LoginException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019, 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.request.handler.exceptions; + +public class LoginException extends Exception { + public LoginException(String message) { + super(message); + } + + public LoginException(String message, Throwable cause) { + super(message, cause); + } + + public LoginException(Throwable cause) { + super(cause); + } + + public LoginException(String message, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerConstants.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerConstants.java new file mode 100644 index 00000000000..6088adfae7f --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerConstants.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, 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.request.handler.util; + +public class HandlerConstants { + public static final String PUBLISHER_APPLICATION_NAME = "application-mgt-publisher"; + public static final String APP_REG_ENDPOINT = "/api-application-registration/register"; + public static final String UI_CONFIG_ENDPOINT = "/api/application-mgt/v1.0/config/ui-config"; + public static final String TOKEN_ENDPOINT = "/oauth2/token"; + public static final String AUTHORIZATION = "Authorization"; + public static final String BASIC = "Basic "; + public static final String COLON = ":"; + public static final String TAGS_KEY = "tags"; + public static final String APP_NAME_KEY = "applicationName"; + public static final String SESSION_AUTH_DATA_KEY = "application-mgt"; + public static final String AUTHORIZATION_HEADER_KEY = "Authorization"; + public static final String UI_CONFIG_KEY = "ui-config"; + public static final String PLATFORM = "platform"; + public static final String SERVER_HOST = "server-host"; + public static final String DEFAULT_ERROR_CALLBACK = "/pages/error/default"; + public static final String LOGIN_RESPONSE_KEY = "loginResponse"; + public static final String FAILURE_CALLBACK_KEY = "failureCallback"; + public static final String API_COMMON_CONTEXT = "/api"; + public static final String EXECUTOR_EXCEPTION_PREFIX = "ExecutorException-"; + public static final String TOKEN_IS_EXPIRED = "ACCESS_TOKEN_IS_EXPIRED"; + + public static final int INTERNAL_ERROR_CODE = 500; + public static final long TIMEOUT = 1200; +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerUtil.java b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerUtil.java new file mode 100644 index 00000000000..357a9ac1ed1 --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/java/io/entgra/request/handler/util/HandlerUtil.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2019, 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.request.handler.util; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.json.JSONException; +import org.json.JSONObject; +import org.wso2.carbon.device.application.mgt.common.ProxyResponse; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; + +public class HandlerUtil { + + private static final Log log = LogFactory.getLog(HandlerUtil.class); + + /*** + * + * @param httpMethod - httpMethod e.g:- HttpPost, HttpGet + * @param - HttpPost or HttpGet class + * @return response as string + * @throws IOException IO exception returns if error occurs when executing the httpMethod + */ + public static ProxyResponse execute(T httpMethod) throws IOException { + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpResponse response = null; + if (httpMethod instanceof HttpPost) { + HttpPost method = (HttpPost) httpMethod; + response = client.execute(method); + } else if (httpMethod instanceof HttpGet) { + HttpGet method = (HttpGet) httpMethod; + response = client.execute(method); + } + + ProxyResponse proxyResponse = new ProxyResponse(); + if (response == null) { + proxyResponse.setCode(HandlerConstants.INTERNAL_ERROR_CODE); + proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey( + HandlerConstants.INTERNAL_ERROR_CODE)); + return proxyResponse; + } else { + int statusCode = response.getStatusLine().getStatusCode(); + try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { + StringBuilder result = new StringBuilder(); + String line; + while ((line = rd.readLine()) != null) { + result.append(line); + } + + String jsonString = result.toString(); + if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) { + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setExecutorResponse("SUCCESS"); + return proxyResponse; + } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { + if (jsonString.contains("Access token expired") || jsonString + .contains("Invalid input. Access token validation failed")) { + proxyResponse.setCode(statusCode); + proxyResponse.setExecutorResponse("ACCESS_TOKEN_IS_EXPIRED"); + return proxyResponse; + } else { + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); + return proxyResponse; + } + } + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse + .setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); + return proxyResponse; + } + } + } + } + + /*** + * + * @param statusCode Provide status code, e.g:- 400, 401, 500 etc + * @return relative status code key for given status code. + */ + public static String getStatusKey (int statusCode){ + String statusCodeKey; + + switch (statusCode) { + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + statusCodeKey = "internalServerError"; + break; + case HttpStatus.SC_BAD_REQUEST: + statusCodeKey = "badRequest"; + break; + case HttpStatus.SC_UNAUTHORIZED: + statusCodeKey = "unauthorized"; + break; + case HttpStatus.SC_FORBIDDEN: + statusCodeKey = "forbidden"; + break; + case HttpStatus.SC_NOT_FOUND: + statusCodeKey = "notFound"; + break; + case HttpStatus.SC_METHOD_NOT_ALLOWED: + statusCodeKey = "methodNotAllowed"; + break; + case HttpStatus.SC_NOT_ACCEPTABLE: + statusCodeKey = "notAcceptable"; + break; + case HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE: + statusCodeKey = "unsupportedMediaType"; + break; + default: + statusCodeKey = "defaultPage"; + break; + } + return statusCodeKey; + } + + + /*** + * + * @param resp {@link HttpServletResponse} + * Return Error Response. + */ + public static void handleError(HttpServletRequest req, HttpServletResponse resp, String serverUrl, + String platform, ProxyResponse proxyResponse) throws IOException { + + HttpSession httpSession = req.getSession(true); + Gson gson = new Gson(); + if (proxyResponse == null){ + proxyResponse = new ProxyResponse(); + proxyResponse.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); + proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil + .getStatusKey(HandlerConstants.INTERNAL_ERROR_CODE)); + } + if (platform == null){ + platform = "default"; + } + + resp.setStatus(proxyResponse.getCode()); + resp.setContentType("application/json"); + resp.setCharacterEncoding("UTF-8"); + + if (httpSession != null) { + JsonObject uiConfig = (JsonObject) httpSession.getAttribute(HandlerConstants.UI_CONFIG_KEY); + if (uiConfig == null){ + proxyResponse.setUrl(serverUrl + "/" + platform + HandlerConstants.DEFAULT_ERROR_CALLBACK); + } else{ + proxyResponse.setUrl(serverUrl + uiConfig.get(HandlerConstants.LOGIN_RESPONSE_KEY).getAsJsonObject() + .get(HandlerConstants.FAILURE_CALLBACK_KEY).getAsJsonObject() + .get(proxyResponse.getExecutorResponse().split(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)[1]) + .getAsString()); + } + } else { + proxyResponse.setUrl(serverUrl + "/" + platform + HandlerConstants.DEFAULT_ERROR_CALLBACK); + } + + proxyResponse.setExecutorResponse(null); + try (PrintWriter writer = resp.getWriter()) { + writer.write(gson.toJson(proxyResponse)); + } + } + + /*** + * + * @param resp {@link HttpServletResponse} + * Return Success Response. + */ + public static void handleSuccess(HttpServletRequest req, HttpServletResponse resp, String serverUrl, + String platform, ProxyResponse proxyResponse) throws IOException { + if (proxyResponse == null){ + handleError(req,resp,serverUrl,platform,proxyResponse); + return; + } + + resp.setStatus(proxyResponse.getCode()); + resp.setContentType("application/json"); + resp.setCharacterEncoding("UTF-8"); + + JSONObject response = new JSONObject(); + String redirectUrl = proxyResponse.getUrl(); + String responseData = proxyResponse.getData(); + + if (!StringUtils.isEmpty(redirectUrl)){ + response.put("url", redirectUrl); + } + if (!StringUtils.isEmpty(responseData)){ + try { + JSONObject responseDataJsonObj = new JSONObject(responseData); + response.put("data", responseDataJsonObj); + } catch (JSONException e) { + log.debug("Response data is not valid json string"); + response.put("data", responseData); + } + } + + try (PrintWriter writer = resp.getWriter()) { + writer.write(response.toString()); + } + } + +} diff --git a/components/request-handler/io.entgra.request.handler/src/main/webapp/META-INF/webapp-classloading.xml b/components/request-handler/io.entgra.request.handler/src/main/webapp/META-INF/webapp-classloading.xml new file mode 100644 index 00000000000..cc5b3c66d7c --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/webapp/META-INF/webapp-classloading.xml @@ -0,0 +1,35 @@ + + + + + + + + + false + + + Carbon + diff --git a/components/request-handler/io.entgra.request.handler/src/main/webapp/WEB-INF/web.xml b/components/request-handler/io.entgra.request.handler/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..c4e45edcf61 --- /dev/null +++ b/components/request-handler/io.entgra.request.handler/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,103 @@ + + + + Entgra Request Handler Webapp + + 60 + + + doAuthentication + false + + + + + managed-api-enabled + false + + + managed-api-owner + admin + + + isSharedWithAllTenants + true + + + + CorsFilter + org.apache.catalina.filters.CorsFilter + + cors.allowed.origins + * + + + cors.allowed.methods + GET,POST,DELETE,PUT + + + cors.allowed.headers + Content-Type + + + + + HttpHeaderSecurityFilter + org.apache.catalina.filters.HttpHeaderSecurityFilter + + hstsEnabled + false + + + + + ContentTypeBasedCachePreventionFilter + org.wso2.carbon.ui.filters.cache.ContentTypeBasedCachePreventionFilter + + patterns + text/html" ,application/json" ,text/plain + + + filterAction + enforce + + + httpHeaders + Cache-Control: no-store, no-cache, must-revalidate, private + + + + + HttpHeaderSecurityFilter + /* + + + + ContentTypeBasedCachePreventionFilter + /* + + + + CorsFilter + /* + + + \ No newline at end of file diff --git a/components/request-handler/pom.xml b/components/request-handler/pom.xml new file mode 100644 index 00000000000..19004986bb9 --- /dev/null +++ b/components/request-handler/pom.xml @@ -0,0 +1,40 @@ + + + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 3.2.5-SNAPSHOT + ../../pom.xml + + 4.0.0 + + io.entgra.devicemgt + request-handler + Entgra - Request Handling Proxy + pom + + io.entgra.request.handler + + + + \ No newline at end of file diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/pom.xml b/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/pom.xml index 1894f992657..b2a6ac81a65 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/pom.xml +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/pom.xml @@ -86,29 +86,6 @@ - - handler-copy - package - - copy - - - - - org.wso2.carbon.devicemgt - org.wso2.carbon.device.application.mgt.handler - - ${project.version} - war - true - - ${project.build.directory}/maven-shared-archive-resources/webapps - - api#application-mgt-handler#v1.0.war - - - - app-mgt-copy package diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/src/main/resources/p2.inf b/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/src/main/resources/p2.inf index 4b464ae7e76..5f658e814db 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/src/main/resources/p2.inf +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.api.feature/src/main/resources/p2.inf @@ -2,5 +2,4 @@ instructions.configure = \ org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.application.mgt.api_${feature.version}/webapps/api#application-mgt-publisher#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#application-mgt-publisher#v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.application.mgt.api_${feature.version}/webapps/api#application-mgt-store#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#application-mgt-store#v1.0.war,overwrite:true);\ -org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.application.mgt.api_${feature.version}/webapps/api#application-mgt-handler#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#application-mgt-handler#v1.0.war,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.application.mgt.api_${feature.version}/webapps/api#application-mgt#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#application-mgt#v1.0.war,overwrite:true);\ diff --git a/features/request-handler/io.entgra.request.handler.feature/pom.xml b/features/request-handler/io.entgra.request.handler.feature/pom.xml new file mode 100644 index 00000000000..13b2fa10d3d --- /dev/null +++ b/features/request-handler/io.entgra.request.handler.feature/pom.xml @@ -0,0 +1,112 @@ + + + + + + request-handler-feature + io.entgra.devicemgt + 3.2.5-SNAPSHOT + + 4.0.0 + + io.entgra.request.handler.feature + Entgra - Request Handling Proxy Feature + + + + org.apache.maven.plugins + maven-dependency-plugin + + + request-handler-copy + package + + copy + + + + + io.entgra.devicemgt + io.entgra.request.handler + ${project.version} + war + true + + ${project.build.directory}/maven-shared-archive-resources/webapps + + api#application-mgt-handler#v1.0.war + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.request.handler + ../../../features/etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + org.eclipse.equinox.p2.type.group:false + + + + + + + + + \ No newline at end of file diff --git a/features/request-handler/io.entgra.request.handler.feature/src/main/resources/build.properties b/features/request-handler/io.entgra.request.handler.feature/src/main/resources/build.properties new file mode 100644 index 00000000000..9c86577d768 --- /dev/null +++ b/features/request-handler/io.entgra.request.handler.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/request-handler/io.entgra.request.handler.feature/src/main/resources/p2.inf b/features/request-handler/io.entgra.request.handler.feature/src/main/resources/p2.inf new file mode 100644 index 00000000000..4b6600f7083 --- /dev/null +++ b/features/request-handler/io.entgra.request.handler.feature/src/main/resources/p2.inf @@ -0,0 +1,3 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.request.handler_${feature.version}/webapps/api#application-mgt-handler#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#application-mgt-handler#v1.0.war,overwrite:true);\ diff --git a/features/request-handler/pom.xml b/features/request-handler/pom.xml new file mode 100644 index 00000000000..33c8984d0ce --- /dev/null +++ b/features/request-handler/pom.xml @@ -0,0 +1,40 @@ + + + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 3.2.5-SNAPSHOT + ../../pom.xml + + 4.0.0 + + io.entgra.devicemgt + request-handler-feature + Entgra - Request Handling Proxy Feature + pom + + io.entgra.request.handler.feature + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e761f5ec6d7..a4e06e74b43 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ components/certificate-mgt components/webapp-authenticator-framework components/email-sender + components/request-handler features/device-mgt features/apimgt-extensions features/application-mgt @@ -52,6 +53,7 @@ features/certificate-mgt features/oauth-extensions features/email-sender + features/request-handler features/jwt-client features/device-mgt-extensions