diff --git a/components/extensions/pom.xml b/components/extensions/pom.xml index ef211283f..d0c991d10 100644 --- a/components/extensions/pom.xml +++ b/components/extensions/pom.xml @@ -38,6 +38,7 @@ mb-extensions siddhi-extensions pull-notification-listeners + remote-session-extension diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/pom.xml b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/pom.xml new file mode 100644 index 000000000..5f8ce1289 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/pom.xml @@ -0,0 +1,69 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + remote-session-extension + 4.0.88-SNAPSHOT + ../pom.xml + + + + 4.0.0 + org.wso2.carbon.device.mgt.extensions.remote.session.endpoint + war + WSO2 - Webapp for UI Remote Session + http://wso2.org + + + + junit + junit + test + + + org.apache.tomcat + tomcat-websocket-api + + + javax.ws.rs + javax.ws.rs-api + + + org.apache.cxf + cxf-rt-frontend-jaxrs + + + org.json.wso2 + json + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.extensions.remote.session + provided + + + + + remote#session + + diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/ClientSessionSubscriptionEndpoint.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/ClientSessionSubscriptionEndpoint.java new file mode 100644 index 000000000..6fd1d9970 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/ClientSessionSubscriptionEndpoint.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * deviceId 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.HttpSessionConfigurator; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.ServiceHolder; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import javax.websocket.CloseReason; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +/** + * This class represents web socket endpoint to manage Remote Sessions + */ +@ServerEndpoint(value = "/clients/{deviceType}/{deviceId}", configurator = HttpSessionConfigurator.class) +public class ClientSessionSubscriptionEndpoint extends SubscriptionEndpoint { + + private static final Log log = LogFactory.getLog(ClientSessionSubscriptionEndpoint.class); + + /** + * Web socket onOpen use when client connect to web socket url + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnOpen + public void onOpen(Session session, @PathParam("deviceType") String deviceType, @PathParam("deviceId") String + deviceId) { + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().initializeSession(session, deviceType, + deviceId); + } catch (RemoteSessionManagementException e) { + if (log.isDebugEnabled()) { + log.error("Error occurred while initializing session ", e); + } + try { + session.close(e.getCloseReason()); + } catch (IOException ex) { + log.error("Failed to disconnect the client.", ex); + } + } + } + + /** + * Web socket onMessage use when client sends a string message + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnMessage + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onMessage use when client sends a byte message + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param message - Byte message which needs to send to peer + */ + @OnMessage + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onClose use to handle socket connection close + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param reason - Status code for web-socket close. + */ + @OnClose + public void onClose(Session session, CloseReason reason, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onClose(session, reason, deviceType, deviceId); + } + + /** + * Web socket onError use to handle socket connection error + * + * @param session - Registered session. + * @param throwable - Web socket exception + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnError + public void onError(Session session, Throwable throwable, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onError(session, throwable, deviceType, deviceId); + } +} \ No newline at end of file diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/DeviceSessionSubscriptionEndpoint.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/DeviceSessionSubscriptionEndpoint.java new file mode 100644 index 000000000..de504eacf --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/DeviceSessionSubscriptionEndpoint.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * deviceId 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.HttpSessionConfigurator; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.ServiceHolder; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import javax.websocket.CloseReason; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +/** + * This class represents web socket endpoint to manage Remote Sessions + */ +@ServerEndpoint(value = "/devices/{deviceType}/{deviceId}/{operationId}", configurator = HttpSessionConfigurator.class) +public class DeviceSessionSubscriptionEndpoint extends SubscriptionEndpoint { + + private static final Log log = LogFactory.getLog(DeviceSessionSubscriptionEndpoint.class); + + /** + * Web socket onOpen use when device connect to web socket url + * + * @param session - Web socket Session + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param operationId - Operations Id + */ + @OnOpen + public void onOpen(Session session, @PathParam("deviceType") String deviceType, @PathParam("deviceId") String + deviceId, @PathParam("operationId") String operationId) { + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().initializeSession(session, deviceType, + deviceId, operationId); + } catch (RemoteSessionManagementException e) { + if (log.isDebugEnabled()) { + log.error("Error occurred while initializing session ", e); + } + try { + session.close(e.getCloseReason()); + } catch (IOException ex) { + log.error("Failed to disconnect the client.", ex); + } + } + } + + /** + * Web socket onMessage use when device sends a string message + * + * @param session - Registered session. + * @param message - String message which needs to send to peer + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnMessage + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onMessage use when device sends a byte message + * + * @param session - Registered session. + * @param message - Byte message which needs to send to peer + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnMessage + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onClose use to handle socket connection close + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param reason - Status code for web-socket close. + */ + @OnClose + public void onClose(Session session, CloseReason reason, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onClose(session, reason, deviceType, deviceId); + } + + /** + * Web socket onError use to handle socket connection error + * + * @param session - Registered session. + * @param throwable - Web socket exception + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + @OnError + public void onError(Session session, Throwable throwable, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + super.onError(session, throwable, deviceType, deviceId); + } +} \ No newline at end of file diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/SubscriptionEndpoint.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/SubscriptionEndpoint.java new file mode 100644 index 000000000..f3ed4cd30 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/SubscriptionEndpoint.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * deviceId 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.ServiceHolder; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import javax.websocket.CloseReason; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import java.io.IOException; + +/** + * This class represents common web socket endpoint to manage Remote Sessions + */ +public class SubscriptionEndpoint { + + private static final Log log = LogFactory.getLog(SubscriptionEndpoint.class); + + /** + * Web socket onMessage - When client sends a message + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param message - String Message which needs to send to peer + */ + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + if (log.isDebugEnabled()) { + log.debug("Received message from client for RemoteSession id: " + session.getId() + " device type: " + + deviceType + " device id: " + deviceId); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().sendMessageToPeer(session, message); + } catch (RemoteSessionManagementException e) { + if (log.isDebugEnabled()) { + log.error("Error occurred while send message to peer session ", e); + } + try { + session.close(e.getCloseReason()); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } + } + + /** + * Web socket onMessage use When client sends a message + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param message - Byte Message which needs to send to peer + */ + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + if (log.isDebugEnabled()) { + log.debug("Received message from client for RemoteSession id: " + session.getId() + " device type: " + + deviceType + " device id: " + deviceId); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().sendMessageToPeer(session, message); + } catch (RemoteSessionManagementException e) { + if (log.isDebugEnabled()) { + log.error("Error occurred while send message to peer session ", e); + } + try { + session.close(e.getCloseReason()); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } + } + + /** + * Web socket onClose use to handle socket connection close + * + * @param session - Registered session. + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + * @param reason - Status code for web-socket close. + */ + public void onClose(Session session, CloseReason reason, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + + ServiceHolder.getInstance().getRemoteSessionManagementService().endSession(session, "Remote session closed"); + if (log.isDebugEnabled()) { + log.debug("websocket closed due to " + reason.getReasonPhrase() + ", for session ID:" + session.getId + () + ", for request URI - " + session.getRequestURI() + " device type: " + deviceType + " device " + + "id: " + deviceId); + } + + } + + /** + * Web socket onError use to handle socket connection error + * + * @param session - Registered session. + * @param throwable - Web socket exception + * @param deviceType - DeviceType + * @param deviceId - Device Identifier + */ + public void onError(Session session, Throwable throwable, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + + if (throwable instanceof IOException) { + if (log.isDebugEnabled()) { + log.error("Error occurred in session ID: " + session.getId() + " device type: " + deviceType + + "device id: " + deviceId + ", for request URI - " + session.getRequestURI() + + ", " + throwable.getMessage(), throwable); + } + } else { + log.error("Error occurred in session ID: " + session.getId() + " device type: " + deviceType + " device " + + "id: " + deviceId + ", for request URI - " + session.getRequestURI() + ", " + throwable.getMessage + (), throwable); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().endSession(session, "Remote session closed"); + if (session.isOpen()) { + session.close(new CloseReason(CloseReason.CloseCodes.PROTOCOL_ERROR, "Unexpected Error Occurred")); + } + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } +} \ No newline at end of file diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/constants/Constants.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/constants/Constants.java new file mode 100644 index 000000000..544b58d57 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/constants/Constants.java @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.constants; + +/** + * This holds the constants related to remote session web socket endpoint + */ +public class Constants { + public static final String HTTP_HEADERS = "HttpHeaders"; + + private Constants() { + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/HttpSessionConfigurator.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/HttpSessionConfigurator.java new file mode 100644 index 000000000..6d5513f95 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/HttpSessionConfigurator.java @@ -0,0 +1,39 @@ +/* + * + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils; + +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.constants.Constants; + +import javax.websocket.HandshakeResponse; +import javax.websocket.server.HandshakeRequest; +import javax.websocket.server.ServerEndpointConfig; +import java.util.List; +import java.util.Map; + +/** + * Class for handle HTTP session + */ +public class HttpSessionConfigurator extends ServerEndpointConfig.Configurator { + public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) { + Map> httpHeaders = request.getHeaders(); + config.getUserProperties().put(Constants.HTTP_HEADERS, httpHeaders); + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/ServiceHolder.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/ServiceHolder.java new file mode 100644 index 000000000..570238bb8 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/java/org/wso2/carbon/device/mgt/extensions/remote/session/endpoint/utils/ServiceHolder.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.extensions.remote.session.RemoteSessionManagementService; + +/** + * Class for store remote management service instances + */ +public class ServiceHolder { + + private static ServiceHolder instance; + private static final Log log = LogFactory.getLog(ServiceHolder.class); + + private ServiceHolder() { + } + + public synchronized static ServiceHolder getInstance() { + if (instance == null) { + instance = new ServiceHolder(); + } + return instance; + } + + public RemoteSessionManagementService getRemoteSessionManagementService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + RemoteSessionManagementService RemoteSessionManagementService = + (RemoteSessionManagementService) ctx.getOSGiService(RemoteSessionManagementService.class, null); + if (RemoteSessionManagementService == null) { + String msg = "Remote Session Management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return RemoteSessionManagementService; + } + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/webapp/WEB-INF/web.xml b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..9f8a509b1 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,59 @@ + + + + + Remote Session + + 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 + /* + + diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/pom.xml b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/pom.xml new file mode 100644 index 000000000..002a33592 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/pom.xml @@ -0,0 +1,134 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + remote-session-extension + 4.0.88-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.device.mgt.extensions.remote.session + bundle + WSO2 Carbon - Remote Session Manager Implementation + WSO2 Carbon - Remote Session Manager Implementation + http://wso2.org + + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.wso2.carbon + org.wso2.carbon.core + + + javax.websocket + javax.websocket-api + + + com.google.code.gson + gson + + + org.apache.httpcomponents.wso2 + httpcore + + + org.wso2.orbit.org.apache.httpcomponents + httpclient + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + + + commons-pool.wso2 + commons-pool + + + javax.ws.rs + jsr311-api + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + org.json.wso2 + json + + + + + + + + org.apache.felix + maven-scr-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.devicemgt.plugins.version} + Remote Session Manager Bundle + + !org.wso2.carbon.device.mgt.extensions.remote.session.internal, + org.wso2.carbon.device.mgt.extensions.remote.session.* + + + *;resolution:=optional + + + + + + + + + diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementService.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementService.java new file mode 100644 index 000000000..ce6f155d1 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementService.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session; + +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import javax.websocket.Session; +import java.io.IOException; + +/** + * Class @{@link RemoteSessionManagementService} use for managing remote sessions + */ +public interface RemoteSessionManagementService { + + /** + * Initialize session based on web socket request .This method is used by the device to connect + * + * @param session Web socket RemoteSession + * @param deviceType Device Type + * @param deviceId Device Id + * @throws RemoteSessionManagementException throws when session has errors with accessing device resources + */ + void initializeSession(Session session, String deviceType, String deviceId) throws + RemoteSessionManagementException; + + /** + * Initialize session based on web socket request . This method is used by the device to connect + * + * @param session Web socket RemoteSession + * @param deviceType Device Type + * @param deviceId Device Id + * @param operationId Operation Id that device needs to connec + * @throws RemoteSessionManagementException throws when session has errors with accessing device resources + */ + void initializeSession(Session session, String deviceType, String deviceId, String operationId) throws + RemoteSessionManagementException; + + /** + * Send string message to connected remote device or client + * + * @param session Web socket RemoteSession + * @param message Message needs to send to peer connection client + * @throws RemoteSessionManagementException + */ + void sendMessageToPeer(Session session, String message) throws RemoteSessionManagementException; + + /** + * Send byte message to connected remote device or client + * + * @param session Web socket RemoteSession + * @param message Message needs to send to peer connection + * @throws RemoteSessionManagementException + */ + void sendMessageToPeer(Session session, byte[] message) throws RemoteSessionManagementException; + + /** + * Close the session + * + * @param session Web socket RemoteSession + */ + void endSession(Session session, String closeReason); + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementServiceImpl.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementServiceImpl.java new file mode 100644 index 000000000..bf5182c0f --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/RemoteSessionManagementServiceImpl.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session; + +import org.apache.commons.collections.map.HashedMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.InvalidDeviceException; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException; +import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; +import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; +import org.wso2.carbon.device.mgt.core.operation.mgt.ConfigOperation; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.AuthenticationInfo; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.OAuthAuthenticator; +import org.wso2.carbon.device.mgt.extensions.remote.session.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.dto.RemoteSession; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +import org.wso2.carbon.device.mgt.extensions.remote.session.internal.RemoteSessionManagementDataHolder; + +import javax.websocket.CloseReason; +import javax.websocket.Session; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Class @{@link RemoteSessionManagementServiceImpl} is the implementation of @{@link RemoteSessionManagementService} + * which is used manage initial connection, sending messages to peer session, manage and close the session. + */ +public class RemoteSessionManagementServiceImpl implements RemoteSessionManagementService { + + private static final Log log = LogFactory.getLog(RemoteSessionManagementServiceImpl.class); + + @Override + public void initializeSession(Session session, String deviceType, String deviceId, String operationId) throws + RemoteSessionManagementException { + + // Check whether required configurations are enabled + if (!RemoteSessionManagementDataHolder.getInstance().isEnabled()) { + throw new RemoteSessionManagementException("Remote session feature is disabled."); + } else if (RemoteSessionManagementDataHolder.getInstance().getServerUrl() == null) { + throw new RemoteSessionManagementException("Server url has not been configured."); + } + + // Read Query Parameters for obtain the token + Map> sessionQueryParam = new HashedMap(); + List sessionQueryParamList = new LinkedList<>(); + sessionQueryParamList.add(session.getQueryString()); + sessionQueryParam.put(RemoteSessionConstants.QUERY_STRING, sessionQueryParamList); + + // Validate the token + OAuthAuthenticator oAuthAuthenticator = RemoteSessionManagementDataHolder.getInstance().getOauthAuthenticator(); + AuthenticationInfo authenticationInfo = oAuthAuthenticator.isAuthenticated(sessionQueryParam); + + if (authenticationInfo != null && authenticationInfo.isAuthenticated()) { + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(authenticationInfo + .getTenantDomain() + , true); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(authenticationInfo.getUsername()); + if (deviceId != null && !deviceId.isEmpty() && deviceType != null && !deviceType.isEmpty()) { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(deviceType); + + // Check authorization of user for given device + boolean userAuthorized = RemoteSessionManagementDataHolder.getInstance() + .getDeviceAccessAuthorizationService() + .isUserAuthorized(deviceIdentifier, authenticationInfo.getUsername()); + if (userAuthorized) { + // set common settings for session + session.setMaxBinaryMessageBufferSize(RemoteSessionConstants.MAX_BUFFER_SIZE); + session.setMaxTextMessageBufferSize(RemoteSessionConstants.MAX_BUFFER_SIZE); + session.setMaxIdleTimeout(RemoteSessionManagementDataHolder.getInstance().getMaxIdleTimeout()); + + // if session initiated using operation id means request came from device + if (operationId != null) { + // create new device session + initializeDeviceSession(session, authenticationInfo.getTenantDomain(), deviceType, deviceId, + operationId); + } else { + // create new client session + initializeClientSession(session, authenticationInfo.getTenantDomain(), deviceType, + deviceId); + } + log.info("Current remote sessions count: " + RemoteSessionManagementDataHolder.getInstance() + .getSessionMap().size()); + + } else { + throw new RemoteSessionManagementException("Missing device Id or type "); + } + } else { + throw new RemoteSessionManagementException("Unauthorized Access for the device Type : " + deviceType + + " , deviceId : " + deviceId); + } + } catch (OperationManagementException | InvalidDeviceException e) { + throw new RemoteSessionManagementException("Error occurred while adding initial operation for the " + + "device Type : " + deviceType + " , deviceId : " + deviceId); + } catch (DeviceAccessAuthorizationException e) { + throw new RemoteSessionManagementException("Error occurred while device access authorization for the " + + "device Type : " + deviceType + " , " + "deviceId : " + deviceId); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + + } else { + throw new RemoteSessionManagementException("Invalid token"); + } + } + + @Override + public void initializeSession(Session session, String deviceType, String deviceId) throws + RemoteSessionManagementException { + initializeSession(session, deviceType, deviceId, null); + } + + /** + * Implements the behaviour of sending message to peer connection + * + * @param session Web socket RemoteSession + * @param message String message needs to send to peer connection + * @throws RemoteSessionManagementException throws when session cannot be made due to invalid data + * @throws RemoteSessionManagementException throws when session has error with accessing device resources + */ + @Override + public void sendMessageToPeer(Session session, String message) throws RemoteSessionManagementException { + JSONObject jsonObject = new JSONObject(message); + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().get(session + .getId()); + if (remoteSession != null) { + remoteSession.sendMessageToPeer(jsonObject.toString()); + } else { + throw new RemoteSessionManagementException("Remote Session cannot be found "); + } + } + + + /** + * Implements the behaviour of sending message to peer connection + * + * @param session Web socket RemoteSession + * @param message Byte message needs to send to peer connection + * @throws RemoteSessionManagementException throws when session cannot be made due to invalid data + * @throws RemoteSessionManagementException throws when session has error with accessing device resources + */ + @Override + public void sendMessageToPeer(Session session, byte[] message) throws RemoteSessionManagementException { + + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().get(session + .getId()); + if (remoteSession != null) { + remoteSession.sendMessageToPeer(message); + } else { + throw new RemoteSessionManagementException("Remote Session cannot be found "); + } + } + + /** + * Closing the session and cleanup the resources + * + * @param session Web socket Remote Session + */ + @Override + public void endSession(Session session, String closeReason) { + + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().remove(session + .getId()); + if (remoteSession != null) { + //String operationId = remoteSession.getOperationId(); + String deviceKey = remoteSession.getTenantDomain() + "/" + remoteSession.getDeviceType() + "/" + + remoteSession.getDeviceId(); + RemoteSession lastSession = RemoteSessionManagementDataHolder.getInstance() + .getActiveDeviceClientSessionMap().get(deviceKey); + if (lastSession != null && lastSession.getMySession().getId().equals(session.getId())) { + RemoteSessionManagementDataHolder.getInstance().getActiveDeviceClientSessionMap().remove + (deviceKey); + } + if (remoteSession.getPeerSession() != null) { + Session peerSession = remoteSession.getPeerSession().getMySession(); + if (peerSession != null) { + RemoteSessionManagementDataHolder.getInstance().getSessionMap().remove(peerSession.getId()); + if (lastSession != null && lastSession.getMySession().getId().equals(peerSession.getId())) { + RemoteSessionManagementDataHolder.getInstance().getActiveDeviceClientSessionMap().remove + (deviceKey); + } + if (peerSession.isOpen()) { + try { + peerSession.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, closeReason)); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } + } + } + } + } + + + /** + * Starting new client session + * + * @param session Web socket Session + * @param tenantDomain Tenant domain + * @param deviceType Device Type + * @param deviceId Device Id + * @throws RemoteSessionManagementException throws when session has errors with accessing device resources + * @throws OperationManagementException throws when error occured during new operation + * @throws InvalidDeviceException throws when incorrect device identifier + */ + private void initializeClientSession(Session session, String tenantDomain, String deviceType, String deviceId) throws RemoteSessionManagementException, + OperationManagementException, InvalidDeviceException { + + RemoteSession clientRemote = new RemoteSession(session, tenantDomain, deviceType, deviceId, RemoteSessionConstants + .CONNECTION_TYPE.CLIENT); + String deviceKey = tenantDomain + "/" + deviceType + "/" + deviceId; + // Create new remote control operation to start the session + RemoteSession activeSession = RemoteSessionManagementDataHolder.getInstance().getActiveDeviceClientSessionMap + ().putIfAbsent(deviceKey, clientRemote); + if (activeSession != null && activeSession.getMySession().isOpen() && activeSession + .getPeerSession() == null) { + throw new RemoteSessionManagementException("Another client session waiting on device to connect."); + } else { + // if there is pending session exists but already closed, then we need to remove it. + if (activeSession != null) { + RemoteSessionManagementDataHolder.getInstance().getActiveDeviceClientSessionMap().remove + (deviceKey); + try { + activeSession.getMySession().close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Remote " + + "session closed due to new session request")); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + // Use put if absent for adding session to waiting list since we need to overcome + // multithreaded session requests. + activeSession = RemoteSessionManagementDataHolder.getInstance().getActiveDeviceClientSessionMap() + .putIfAbsent(deviceKey, clientRemote); + } + // If another client tried to start session same time then active session will be + // exist. So we are adding session request only no parallel sessions added to map + if (activeSession == null) { + + // Create operation if session initiated by client + Operation operation = new ConfigOperation(); + operation.setCode(RemoteSessionConstants.REMOTE_CONNECT); + operation.setEnabled(true); + operation.setControl(Operation.Control.NO_REPEAT); + JSONObject payload = new JSONObject(); + payload.put("serverUrl", RemoteSessionManagementDataHolder.getInstance().getServerUrl()); + operation.setPayLoad(payload.toString()); + String date = new SimpleDateFormat(RemoteSessionConstants.DATE_FORMAT_NOW).format(new Date()); + operation.setCreatedTimeStamp(date); + List deviceIdentifiers = new ArrayList<>(); + deviceIdentifiers.add(new DeviceIdentifier(deviceId, deviceType)); + Activity activity = RemoteSessionManagementDataHolder.getInstance(). + getDeviceManagementProviderService().addOperation(deviceType, operation, deviceIdentifiers); + clientRemote.setOperationId(activity.getActivityId().replace(DeviceManagementConstants + .OperationAttributes.ACTIVITY, "")); + RemoteSessionManagementDataHolder.getInstance().getSessionMap().put(session.getId(), clientRemote); + log.info("Client remote session opened for session id: " + session.getId() + " device Type : " + + deviceType + " , " + "deviceId : " + deviceId); + } else { + throw new RemoteSessionManagementException("Another client session waiting on " + + "device to connect."); + } + } + } + + /** + * Starting new device session + * + * @param session Web socket Session + * @param tenantDomain Tenant domain + * @param deviceType Device Type + * @param deviceId Device Id + * @param operationId Operation id + * @throws RemoteSessionManagementException throws when session has errors with accessing device resources + */ + private void initializeDeviceSession(Session session, String tenantDomain, String deviceType, String deviceId, + String operationId) throws RemoteSessionManagementException { + String deviceKey = tenantDomain + "/" + deviceType + "/" + deviceId; + RemoteSession activeSession = RemoteSessionManagementDataHolder.getInstance() + .getActiveDeviceClientSessionMap().get(deviceKey); + if (activeSession != null) { + RemoteSession clientRemote = RemoteSessionManagementDataHolder.getInstance().getSessionMap().get + (activeSession.getMySession().getId()); + if (clientRemote != null) { + if (clientRemote.getOperationId().equals(operationId)) { + RemoteSession deviceRemote = new RemoteSession(session, tenantDomain, deviceType, deviceId, + RemoteSessionConstants.CONNECTION_TYPE.DEVICE); + deviceRemote.setOperationId(operationId); + deviceRemote.setPeerSession(clientRemote); + clientRemote.setPeerSession(deviceRemote); + RemoteSessionManagementDataHolder.getInstance().getSessionMap().put(session.getId(), deviceRemote); + // Send Remote connect response + JSONObject message = new JSONObject(); + message.put(RemoteSessionConstants.REMOTE_CONNECT_CODE, RemoteSessionConstants.REMOTE_CONNECT); + deviceRemote.sendMessageToPeer(message.toString()); + log.info("Device session opened for session id: " + session.getId() + " device Type : " + + deviceType + " , " + "deviceId : " + deviceId); + } else { + throw new RemoteSessionManagementException("Device and Operation information " + + "does not matched with client information for operation id: " + operationId + " device " + + "Type : " + deviceType + " , " + "deviceId : " + deviceId); + } + } else { + throw new RemoteSessionManagementException("Device session is inactive for " + "operation id: " + + operationId + " device Type : " + deviceType + " , " + "deviceId : " + deviceId); + } + } else { + throw new RemoteSessionManagementException("Device session is inactive for operation " + "id: " + + operationId + " device Type : " + deviceType + " , " + "deviceId : " + deviceId); + } + + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/AuthenticationInfo.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/AuthenticationInfo.java new file mode 100644 index 000000000..e7d77e5bd --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/AuthenticationInfo.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.authentication; + +import java.util.Map; + +/** + * This is returned after authentication. + */ +public class AuthenticationInfo { + + /** + * this variable is used to check whether the client is authenticated. + */ + private boolean authenticated; + private String username; + private String tenantDomain; + + /** + * To hold authentication related properties eg: scopes in oauth + */ + private Map properties; + + /** + * returns whether the client is authenticated + */ + public boolean isAuthenticated() { + return authenticated; + } + + public void setAuthenticated(boolean authenticated) { + this.authenticated = authenticated; + } + + /** + * returns the authenticated client username + */ + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + /** + * return the authenticated client tenant domain + */ + public String getTenantDomain() { + return tenantDomain; + } + + public void setTenantDomain(String tenantDomain) { + this.tenantDomain = tenantDomain; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/OAuthAuthenticator.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/OAuthAuthenticator.java new file mode 100644 index 000000000..b6832fc48 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/OAuthAuthenticator.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.authentication; + + +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.oauth.OAuthTokenValidator; + +import java.util.List; +import java.util.Map; + +/** + * {@link OAuthAuthenticator} for validate tokens to web socket api + */ +public class OAuthAuthenticator { + OAuthTokenValidator oAuthTokenValidator; + + public void init(Map globalProperties) { + oAuthTokenValidator = new OAuthTokenValidator(globalProperties); + } + + public AuthenticationInfo isAuthenticated(Map> webSocketConnectionProperties) { + return oAuthTokenValidator.validateToken(webSocketConnectionProperties); + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidator.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidator.java new file mode 100644 index 000000000..352289cf2 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidator.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.authentication.oauth; + +import org.apache.axis2.context.ServiceContext; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.impl.GenericObjectPool; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.AuthenticationInfo; +import org.wso2.carbon.device.mgt.extensions.remote.session.constants.RemoteSessionConstants; +import org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO_OAuth2AccessToken; +import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationResponseDTO; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +import java.rmi.RemoteException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This acts as a contract point for OAuth token validation. + */ +public class OAuthTokenValidator { + + private static String cookie; + private GenericObjectPool stubs; + private static Log log = LogFactory.getLog(OAuthTokenValidator.class); + private static OAuthTokenValidator oAuthTokenValidator; + + + public OAuthTokenValidator(Map globalProperties) { + this.stubs = new GenericObjectPool(new OAuthTokenValidatorStubFactory(globalProperties)); + } + + /** + * This method gets a string accessToken and validates it + * + * @param webSocketConnectionProperties WebSocket connection information including http headers + * @return AuthenticationInfo with the validated results. + */ + public AuthenticationInfo validateToken(Map> webSocketConnectionProperties) { + String token = getTokenFromSession(webSocketConnectionProperties); + if (token == null) { + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + authenticationInfo.setAuthenticated(false); + return authenticationInfo; + } + OAuth2TokenValidationServiceStub tokenValidationServiceStub = null; + try { + Object stub = this.stubs.borrowObject(); + if (stub != null) { + tokenValidationServiceStub = (OAuth2TokenValidationServiceStub) stub; + if (cookie != null) { + tokenValidationServiceStub._getServiceClient().getOptions().setProperty( + HTTPConstants.COOKIE_STRING, cookie); + } + return getAuthenticationInfo(token, tokenValidationServiceStub); + } else { + log.warn("Stub initialization failed."); + } + } catch (RemoteException e) { + log.error("Error on connecting with the validation endpoint.", e); + } catch (Exception e) { + log.error("Error occurred in borrowing an validation stub from the pool.", e); + + } finally { + try { + if (tokenValidationServiceStub != null) { + this.stubs.returnObject(tokenValidationServiceStub); + } + } catch (Exception e) { + log.warn("Error occurred while returning the object back to the oauth token validation service " + + "stub pool.", e); + } + } + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + authenticationInfo.setAuthenticated(false); + return authenticationInfo; + } + + /** + * This creates an AuthenticationInfo object that is used for authorization. This method will validate the token + * and + * sets the required parameters to the object. + * + * @param token that needs to be validated. + * @param tokenValidationServiceStub stub that is used to call the external service. + * @return AuthenticationInfo This contains the information related to authenticated client. + * @throws RemoteException that triggers when failing to call the external service.. + */ + private AuthenticationInfo getAuthenticationInfo(String token, + OAuth2TokenValidationServiceStub tokenValidationServiceStub) + throws RemoteException { + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + OAuth2TokenValidationRequestDTO validationRequest = new OAuth2TokenValidationRequestDTO(); + OAuth2TokenValidationRequestDTO_OAuth2AccessToken accessToken = + new OAuth2TokenValidationRequestDTO_OAuth2AccessToken(); + accessToken.setTokenType(RemoteSessionConstants.OAuthTokenValidator.TOKEN_TYPE); + accessToken.setIdentifier(token); + validationRequest.setAccessToken(accessToken); + boolean authenticated; + OAuth2TokenValidationResponseDTO tokenValidationResponse; + tokenValidationResponse = tokenValidationServiceStub.validate(validationRequest); + if (tokenValidationResponse == null) { + authenticationInfo.setAuthenticated(false); + return authenticationInfo; + } + authenticated = tokenValidationResponse.getValid(); + if (authenticated) { + String authorizedUser = tokenValidationResponse.getAuthorizedUser(); + String username = MultitenantUtils.getTenantAwareUsername(authorizedUser); + String tenantDomain = MultitenantUtils.getTenantDomain(authorizedUser); + authenticationInfo.setUsername(username); + authenticationInfo.setTenantDomain(tenantDomain); + String scopes[] = tokenValidationResponse.getScope(); + if (scopes != null) { + Map properties = new HashMap<>(); + properties.put(RemoteSessionConstants.SCOPE_IDENTIFIER, scopes); + authenticationInfo.setProperties(properties); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Token validation failed for token: " + token); + } + } + ServiceContext serviceContext = tokenValidationServiceStub._getServiceClient() + .getLastOperationContext().getServiceContext(); + cookie = (String) serviceContext.getProperty(HTTPConstants.COOKIE_STRING); + authenticationInfo.setAuthenticated(authenticated); + return authenticationInfo; + } + + /** + * Retrieving the token from the http header + * + * @param webSocketConnectionProperties WebSocket connection information including http headers + * @return retrieved token + */ + private String getToken(Map> webSocketConnectionProperties) { + String cookieString = webSocketConnectionProperties.get(RemoteSessionConstants.OAuthTokenValidator.COOKIE) + .get(0); + String[] properties = cookieString.split(RemoteSessionConstants.OAuthTokenValidator.COOKIE_KEYPAIR_SEPERATOR); + String token; + for (String keyValuePair : properties) { + if (RemoteSessionConstants.OAuthTokenValidator.TOKEN_IDENTIFIER.equals((keyValuePair. + split(RemoteSessionConstants.OAuthTokenValidator.COOKIE_KEY_VALUE_SEPERATOR)[0]).trim())) { + token = (keyValuePair.split(RemoteSessionConstants.OAuthTokenValidator.COOKIE_KEY_VALUE_SEPERATOR) + [1]).trim(); + return token; + } + } + log.error("WebSocket token should be specified in cookie"); + return null; + } + + /** + * Retrieving the token from the http session + * + * @param webSocketConnectionProperties WebSocket connection information including http headers + * @return retrieved token + */ + private String getTokenFromSession(Map> webSocketConnectionProperties) { + String queryString = webSocketConnectionProperties.get(RemoteSessionConstants.OAuthTokenValidator + .QUERY_STRING).get(0); + if (queryString != null) { + String[] allQueryParamPairs = queryString.split(RemoteSessionConstants.OAuthTokenValidator + .QUERY_STRING_SEPERATOR); + for (String keyValuePair : allQueryParamPairs) { + String[] queryParamPair = keyValuePair.split(RemoteSessionConstants.OAuthTokenValidator + .QUERY_KEY_VALUE_SEPERATOR); + if (queryParamPair.length != 2) { + log.warn("Invalid query string [" + queryString + "] passed in."); + break; + } + if (queryParamPair[0].equals(RemoteSessionConstants.OAuthTokenValidator.TOKEN_IDENTIFIER)) { + return queryParamPair[1]; + } + } + } + return null; + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidatorStubFactory.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidatorStubFactory.java new file mode 100644 index 000000000..15723a2c8 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/OAuthTokenValidatorStubFactory.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.authentication.oauth; + +import org.apache.axis2.AxisFault; +import org.apache.axis2.Constants; +import org.apache.axis2.client.Options; +import org.apache.axis2.client.ServiceClient; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.BasePoolableObjectFactory; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.oauth.exception.OAuthTokenValidationException; +import org.wso2.carbon.device.mgt.extensions.remote.session.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +import org.wso2.carbon.device.mgt.extensions.remote.session.util.PropertyUtils; +import org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.util.Map; + + +/** + * This follows object pool pattern to manage the stub for oauth validation service. + */ +public class OAuthTokenValidatorStubFactory extends BasePoolableObjectFactory { + private static final Log log = LogFactory.getLog(OAuthTokenValidatorStubFactory.class); + private HttpClient httpClient; + Map tokenValidationProperties; + + public OAuthTokenValidatorStubFactory(Map globalProperties) { + this.tokenValidationProperties = globalProperties; + this.httpClient = createHttpClient(); + } + + /** + * This creates a OAuth2TokenValidationServiceStub object to the pool. + * + * @return an OAuthValidationStub object + * @throws Exception thrown when creating the object. + */ + @Override + public Object makeObject() throws Exception { + return this.generateStub(); + } + + /** + * This is used to clean up the OAuth validation stub and releases to the object pool. + * + * @param o object that needs to be released. + * @throws Exception throws when failed to release to the pool + */ + @Override + public void passivateObject(Object o) throws Exception { + if (o instanceof OAuth2TokenValidationServiceStub) { + OAuth2TokenValidationServiceStub stub = (OAuth2TokenValidationServiceStub) o; + stub._getServiceClient().cleanupTransport(); + } + } + + /** + * This is used to create a stub which will be triggered through object pool factory, which will create an + * instance of it. + * + * @return OAuth2TokenValidationServiceStub stub that is used to call an external service. + * @throws OAuthTokenValidationException will be thrown when initialization failed. + */ + private OAuth2TokenValidationServiceStub generateStub() throws OAuthTokenValidationException { + OAuth2TokenValidationServiceStub stub; + try { + URL hostURL = new URL(PropertyUtils.replaceProperty(tokenValidationProperties.get( + (RemoteSessionConstants.TOKEN_VALIDATION_ENDPOINT_URL))) + + RemoteSessionConstants.TOKEN_VALIDATION_CONTEX); + stub = new OAuth2TokenValidationServiceStub(hostURL.toString()); + ServiceClient client = stub._getServiceClient(); + client.getServiceContext().getConfigurationContext().setProperty( + HTTPConstants.CACHED_HTTP_CLIENT, httpClient); + + HttpTransportProperties.Authenticator auth = + new HttpTransportProperties.Authenticator(); + auth.setPreemptiveAuthentication(true); + String username = tokenValidationProperties.get(RemoteSessionConstants.USERNAME); + String password = tokenValidationProperties.get(RemoteSessionConstants.PASSWORD); + auth.setPassword(username); + auth.setUsername(password); + Options options = client.getOptions(); + options.setProperty(HTTPConstants.AUTHENTICATE, auth); + options.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE); + client.setOptions(options); + if (hostURL.getProtocol().equals("https")) { + // set up ssl factory since axis2 https notification is used. + EasySSLProtocolSocketFactory sslProtocolSocketFactory = createProtocolSocketFactory(); + Protocol authhttps = new Protocol(hostURL.getProtocol() + , (ProtocolSocketFactory) sslProtocolSocketFactory, hostURL.getPort()); + Protocol.registerProtocol(hostURL.getProtocol(), authhttps); + options.setProperty(HTTPConstants.CUSTOM_PROTOCOL_HANDLER, authhttps); + } + + } catch (AxisFault axisFault) { + throw new OAuthTokenValidationException( + "Error occurred while creating the OAuth2TokenValidationServiceStub.", axisFault); + } catch (MalformedURLException e) { + throw new OAuthTokenValidationException( + "Error occurred while parsing token endpoint URL", e); + } catch (RemoteSessionManagementException e) { + throw new OAuthTokenValidationException("Invalid token endpoint url", e); + } + + return stub; + } + + /** + * This is required to create a trusted connection with the external entity. + * Have to manually configure it since we use CommonHTTPTransport(axis2 notification) in axis2. + * + * @return an EasySSLProtocolSocketFactory for SSL communication. + */ + private EasySSLProtocolSocketFactory createProtocolSocketFactory() throws OAuthTokenValidationException { + try { + return new EasySSLProtocolSocketFactory(); + } catch (IOException e) { + String errorMsg = "Failed to initiate EasySSLProtocolSocketFactory."; + throw new OAuthTokenValidationException(errorMsg, e); + } catch (GeneralSecurityException e) { + String errorMsg = "Failed to set the key material in easy ssl factory."; + throw new OAuthTokenValidationException(errorMsg, e); + } + } + + /** + * This created httpclient pool that can be used to connect to external entity. This connection can be configured + * via broker.xml by setting up the required http connection parameters. + * + * @return an instance of HttpClient that is configured with MultiThreadedHttpConnectionManager + */ + private HttpClient createHttpClient() { + HttpConnectionManagerParams params = new HttpConnectionManagerParams(); + params.setDefaultMaxConnectionsPerHost(Integer.parseInt(tokenValidationProperties.get( + RemoteSessionConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST))); + params.setMaxTotalConnections(Integer.parseInt(tokenValidationProperties.get( + RemoteSessionConstants.MAXIMUM_TOTAL_HTTP_CONNECTION))); + HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); + connectionManager.setParams(params); + return new HttpClient(connectionManager); + } +} \ No newline at end of file diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/exception/OAuthTokenValidationException.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/exception/OAuthTokenValidationException.java new file mode 100644 index 000000000..069aa1187 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/authentication/oauth/exception/OAuthTokenValidationException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.authentication.oauth.exception; + +/** + * This Exception will be thrown, when there any interference with token validation flow. + */ +public class OAuthTokenValidationException extends Exception { + + public OAuthTokenValidationException(String msg, Exception nestedEx) { + super(msg, nestedEx); + } + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/constants/RemoteSessionConstants.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/constants/RemoteSessionConstants.java new file mode 100644 index 000000000..33684eac2 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/constants/RemoteSessionConstants.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.constants; + +/** + * This holds the constants related to remote session + */ +public class RemoteSessionConstants { + + public static final String SCOPE_IDENTIFIER = "scopes"; + public static final String TOKEN_VALIDATION_ENDPOINT_URL = "tokenValidationUrl"; + public static final String TOKEN_VALIDATION_CONTEX = "/services/OAuth2TokenValidationService"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss"; + public static final int MAX_BUFFER_SIZE = 640 * 1024; + + public enum CONNECTION_TYPE { + CLIENT, DEVICE + } + + public static final String QUERY_STRING = "queryString"; + public static final String MAXIMUM_TOTAL_HTTP_CONNECTION = "maximumTotalHttpConnection"; + public static final String MAXIMUM_HTTP_CONNECTION_PER_HOST = "maximumHttpConnectionPerHost"; + public static final String DEFAULT_MAXIMUM_HTTP_CONNECTION_PER_HOST = "2"; + public static final String DEFAULT_MAXIMUM_TOTAL_HTTP_CONNECTIONS = "100"; + + // Initial remote connection operation related Constants + public static final String REMOTE_CONNECT = "REMOTE_CONNECT"; + public static final String REMOTE_CONNECT_CODE = "code"; + + private RemoteSessionConstants() { + } + + /** + * OAuthTokenValidator specific constants + */ + public final class OAuthTokenValidator { + public static final String COOKIE_KEY_VALUE_SEPERATOR = "="; + public static final String COOKIE_KEYPAIR_SEPERATOR = ";"; + public static final String COOKIE = "cookie"; + public static final String TOKEN_TYPE = "bearer"; + public static final String TOKEN_IDENTIFIER = "websocketToken"; + public static final String QUERY_STRING_SEPERATOR = "&"; + public static final String QUERY_KEY_VALUE_SEPERATOR = "="; + public static final String QUERY_STRING = "queryString"; + + private OAuthTokenValidator() { + } + } + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/dto/RemoteSession.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/dto/RemoteSession.java new file mode 100644 index 000000000..5b2337c33 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/dto/RemoteSession.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.dto; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.extensions.remote.session.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +import org.wso2.carbon.device.mgt.extensions.remote.session.internal.RemoteSessionManagementDataHolder; + +import javax.websocket.Session; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * {@link RemoteSession} will represent remote websocket session + * This class implements the behaviours of sending message to the session in multithreaded context. + */ +public class RemoteSession { + + private static final Log log = LogFactory.getLog(RemoteSession.class); + private String tenantDomain, operationId, deviceType, deviceId; + private long lastMessageTimeStamp = System.currentTimeMillis(); + private RemoteSession peerSession; + private Session mySession; + private final Object writeLockObject = new Object(); + private int maxMessagesPerSecond; + private int messageAllowance; + private double messageRatePerSecond; + private RemoteSessionConstants.CONNECTION_TYPE connectionType; + + public RemoteSession(Session session, String tenantDomain, String deviceType, String deviceId, + RemoteSessionConstants.CONNECTION_TYPE connectionType) { + this.mySession = session; + this.deviceType = deviceType; + this.deviceId = deviceId; + this.tenantDomain = tenantDomain; + this.connectionType = connectionType; + maxMessagesPerSecond = RemoteSessionManagementDataHolder.getInstance().getMaxMessagesPerSecond(); + messageAllowance = maxMessagesPerSecond; + messageRatePerSecond = (double) maxMessagesPerSecond / 1000; + } + + private void sendMessage(Object message) throws RemoteSessionManagementException { + if (message != null) { + boolean isMessageCountExceed = false; + if (mySession != null && mySession.isOpen()) { + synchronized (writeLockObject) { + try { + isMessageCountExceed = applyRateLimit(); + if (!isMessageCountExceed) { + if (message instanceof String) { + mySession.getBasicRemote().sendText(message.toString()); + } else { + mySession.getBasicRemote().sendBinary(ByteBuffer.wrap((byte[]) message)); + } + this.lastMessageTimeStamp = System.currentTimeMillis(); + } else { + log.warn("Message count per second is exceeded for device id :" + deviceId); + } + } catch (IOException e) { + log.warn("Send data to session failed due to ", e); + } + } + } else { + throw new RemoteSessionManagementException("Peer Session already closed "); + } + } else { + throw new RemoteSessionManagementException("Message is empty"); + } + } + + public void sendMessageToPeer(Object message) throws RemoteSessionManagementException { + peerSession.sendMessage(message); + } + + /** + * Use for limit the messages for given time + * + * @return message rate applied + */ + private boolean applyRateLimit() { + long currentTime = System.currentTimeMillis(); + messageAllowance += (currentTime - lastMessageTimeStamp) * messageRatePerSecond; + if (messageAllowance > maxMessagesPerSecond) { + messageAllowance = maxMessagesPerSecond; + } + if (messageAllowance >= 1) { + lastMessageTimeStamp = currentTime; + messageAllowance -= 1; + return false; + } else { + return true; + } + } + + public Session getMySession() { + return mySession; + } + + public RemoteSession getPeerSession() { + return peerSession; + } + + public void setPeerSession(RemoteSession peerSession) { + this.peerSession = peerSession; + } + + public String getTenantDomain() { + return tenantDomain; + } + + public String getOperationId() { + return operationId; + } + + public void setOperationId(String operationId) { + this.operationId = operationId; + } + + public String getDeviceType() { + return deviceType; + } + + public String getDeviceId() { + return deviceId; + } + + public RemoteSessionConstants.CONNECTION_TYPE getConnectionType() { + return connectionType; + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/exception/RemoteSessionManagementException.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/exception/RemoteSessionManagementException.java new file mode 100644 index 000000000..741f04e31 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/exception/RemoteSessionManagementException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.exception; + + +import javax.websocket.CloseReason; + +/** + * This Exception will be thrown, when there any management issues with Remote Session. + */ +public class RemoteSessionManagementException extends Exception { + + CloseReason closeReason; + + public RemoteSessionManagementException(String msg) { + super(msg); + this.closeReason = new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, msg); + } + + public CloseReason getCloseReason() { + return closeReason; + } + + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementDataHolder.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementDataHolder.java new file mode 100644 index 000000000..310affbb0 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementDataHolder.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.internal; + +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; +import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.OAuthAuthenticator; +import org.wso2.carbon.device.mgt.extensions.remote.session.dto.RemoteSession; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Class {@link RemoteSessionManagementDataHolder} will hold the configurations and in memory storage strictures to + * manage remote sessions. + */ +public class RemoteSessionManagementDataHolder { + + private static RemoteSessionManagementDataHolder thisInstance = new RemoteSessionManagementDataHolder(); + private DeviceManagementProviderService deviceManagementProviderService; + private DeviceAccessAuthorizationService deviceAccessAuthorizationService; + private boolean isEnabled; + private String serverUrl; + private long maxIdleTimeout; + private int maxMessagesPerSecond; + private OAuthAuthenticator oAuthAuthenticator; + private Map activeDeviceClientSessionMap = new ConcurrentHashMap(); + private Map sessionMap = new ConcurrentHashMap(); + + public static RemoteSessionManagementDataHolder getInstance() { + return thisInstance; + } + + public DeviceManagementProviderService getDeviceManagementProviderService() { + return deviceManagementProviderService; + } + + public void setDeviceManagementProviderService(DeviceManagementProviderService deviceManagementProviderService) { + this.deviceManagementProviderService = deviceManagementProviderService; + } + + public DeviceAccessAuthorizationService getDeviceAccessAuthorizationService() { + return deviceAccessAuthorizationService; + } + + public void setDeviceAccessAuthorizationService(DeviceAccessAuthorizationService deviceAccessAuthorizationService) { + this.deviceAccessAuthorizationService = deviceAccessAuthorizationService; + } + + public OAuthAuthenticator getOauthAuthenticator() { + return oAuthAuthenticator; + } + + public void setOauthAuthenticator(OAuthAuthenticator oAuthAuthenticator) { + this.oAuthAuthenticator = oAuthAuthenticator; + } + + public Map getSessionMap() { + return sessionMap; + } + + public Map getActiveDeviceClientSessionMap() { + return activeDeviceClientSessionMap; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public String getServerUrl() { + return serverUrl; + } + + public void setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + } + + public int getMaxMessagesPerSecond() { + return maxMessagesPerSecond; + } + + public void setMaxMessagesPerSecond(int maxMessagesPerSecond) { + this.maxMessagesPerSecond = maxMessagesPerSecond; + } + + public long getMaxIdleTimeout() { + return maxIdleTimeout; + } + + public void setMaxIdleTimeout(long maxIdleTimeout) { + this.maxIdleTimeout = maxIdleTimeout; + } +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementServiceComponent.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementServiceComponent.java new file mode 100644 index 000000000..da394210c --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/internal/RemoteSessionManagementServiceComponent.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.core.ServerStartupObserver; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; +import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.extensions.remote.session.RemoteSessionManagementService; +import org.wso2.carbon.device.mgt.extensions.remote.session.RemoteSessionManagementServiceImpl; +import org.wso2.carbon.device.mgt.extensions.remote.session.listener.RemoteSessionManagerStartupListener; + +/** + * @scr.component name="RemoteSessionManagementServiceComponent" immediate="true" + * @scr.reference name="carbon.device.mgt.provider" + * interface="org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService" + * cardinality="1..1" + * policy="dynamic" + * bind="setDeviceManagementProviderService" + * unbind="unsetDeviceManagementProviderService" + * @scr.reference name="device.manager.service" + * interface="org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService" + * cardinality="0..n" + * policy="dynamic" + * bind="setDeviceAccessAuthorizationService" + * unbind="unsetDeviceAccessAuthorizationService" + */ + +public class RemoteSessionManagementServiceComponent { + + private static final Log log = LogFactory.getLog(RemoteSessionManagementServiceComponent.class); + + protected void activate(ComponentContext componentContext) { + try { + + BundleContext bundleContext = componentContext.getBundleContext(); + bundleContext.registerService(ServerStartupObserver.class.getName(), new + RemoteSessionManagerStartupListener(), null); + bundleContext.registerService(RemoteSessionManagementService.class.getName(), new + RemoteSessionManagementServiceImpl(), null); + if (log.isDebugEnabled()) { + log.debug("Remote Session device access service implementation bundle has been successfully " + + "initialized"); + } + } catch (Throwable e) { + log.error("Error occurred while initializing Remote Session device access service " + + "implementation bundle", e); + } + } + + protected void deactivate(ComponentContext componentContext) { + //Do nothing + } + + protected void setDeviceManagementProviderService(DeviceManagementProviderService deviceManagementProviderService) { + RemoteSessionManagementDataHolder.getInstance() + .setDeviceManagementProviderService(deviceManagementProviderService); + } + + protected void unsetDeviceManagementProviderService(DeviceManagementProviderService + deviceManagementProviderService) { + RemoteSessionManagementDataHolder.getInstance().setDeviceManagementProviderService(null); + } + + protected void setDeviceAccessAuthorizationService(DeviceAccessAuthorizationService + deviceAccessAuthorizationService) { + RemoteSessionManagementDataHolder.getInstance() + .setDeviceAccessAuthorizationService(deviceAccessAuthorizationService); + } + + protected void unsetDeviceAccessAuthorizationService(DeviceAccessAuthorizationService + deviceAccessAuthorizationService) { + RemoteSessionManagementDataHolder.getInstance().setDeviceManagementProviderService(null); + } + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/listener/RemoteSessionManagerStartupListener.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/listener/RemoteSessionManagerStartupListener.java new file mode 100644 index 000000000..19b89f369 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/listener/RemoteSessionManagerStartupListener.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.extensions.remote.session.listener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.ServerStartupObserver; +import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.config.keymanager.KeyManagerConfigurations; +import org.wso2.carbon.device.mgt.core.config.remote.session.RemoteSessionConfiguration; +import org.wso2.carbon.device.mgt.extensions.remote.session.authentication.OAuthAuthenticator; +import org.wso2.carbon.device.mgt.extensions.remote.session.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.internal.RemoteSessionManagementDataHolder; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import java.util.HashMap; +import java.util.Map; + +/** + * Startup listener is been used to make sure the receiver gets activated after the server start up to avoid + * Bundle not loading issues. + * This will configure the values for remote session management + */ +public class RemoteSessionManagerStartupListener implements ServerStartupObserver { + + private static final Log log = LogFactory.getLog(RemoteSessionManagerStartupListener.class); + + @Override + public void completingServerStartup() { + } + + @Override + public void completedServerStartup() { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, true); + try { + RemoteSessionConfiguration rsConfig = DeviceConfigurationManager.getInstance().getDeviceManagementConfig + ().getRemoteSessionConfiguration(); + KeyManagerConfigurations kmConfig = DeviceConfigurationManager.getInstance().getDeviceManagementConfig() + .getKeyManagerConfigurations(); + + RemoteSessionManagementDataHolder.getInstance().setEnabled(rsConfig.isEnabled()); + RemoteSessionManagementDataHolder.getInstance().setServerUrl(rsConfig.getRemoteSessionServerUrl()); + Map configProperties = new HashMap<>(); + + // Set max idle timeout in milliseconds + RemoteSessionManagementDataHolder.getInstance().setMaxIdleTimeout((long) rsConfig.getSessionIdleTimeOut() * + 60000); + + // Set max messages per second. + if (rsConfig.getMaxMessagesPerSession() > 0) { + RemoteSessionManagementDataHolder.getInstance().setMaxMessagesPerSecond(rsConfig + .getMaxMessagesPerSession()); + } + // Token validation related configuration + configProperties.put(RemoteSessionConstants.TOKEN_VALIDATION_ENDPOINT_URL, kmConfig.getServerUrl()); + configProperties.put(RemoteSessionConstants.USERNAME, kmConfig.getAdminUsername()); + configProperties.put(RemoteSessionConstants.PASSWORD, kmConfig.getAdminPassword()); + if (rsConfig.getMaxHTTPConnectionPerHost() > 0) { + + configProperties.put(RemoteSessionConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST, + String.valueOf(rsConfig.getMaxHTTPConnectionPerHost())); + } else { + configProperties.put(RemoteSessionConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST, RemoteSessionConstants + .DEFAULT_MAXIMUM_HTTP_CONNECTION_PER_HOST); + } + if (rsConfig.getMaxTotalHTTPConnections() > 0) { + configProperties.put(RemoteSessionConstants.MAXIMUM_TOTAL_HTTP_CONNECTION, String.valueOf(rsConfig + .getMaxTotalHTTPConnections())); + } else { + configProperties.put(RemoteSessionConstants.MAXIMUM_TOTAL_HTTP_CONNECTION, RemoteSessionConstants + .DEFAULT_MAXIMUM_TOTAL_HTTP_CONNECTIONS); + } + + OAuthAuthenticator oAuthAuthenticator = new OAuthAuthenticator(); + oAuthAuthenticator.init(configProperties); + RemoteSessionManagementDataHolder.getInstance().setOauthAuthenticator(oAuthAuthenticator); + + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + +} diff --git a/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/util/PropertyUtils.java b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/util/PropertyUtils.java new file mode 100644 index 000000000..cb63b97ac --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/src/main/java/org/wso2/carbon/device/mgt/extensions/remote.session/util/PropertyUtils.java @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you under the Apache License, +* Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +package org.wso2.carbon.device.mgt.extensions.remote.session.util; + +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Class for reading web socket url parameters + */ +public class PropertyUtils { + + /** + * Replace URL with placeholders with properties + * @param urlWithPlaceholders URL + * @return replaced url + * @throws RemoteSessionManagementException + */ + public static String replaceProperty(String urlWithPlaceholders) throws RemoteSessionManagementException { + String regex = "\\$\\{(.*?)\\}"; + Pattern pattern = Pattern.compile(regex); + Matcher matchPattern = pattern.matcher(urlWithPlaceholders); + while (matchPattern.find()) { + String sysPropertyName = matchPattern.group(1); + String sysPropertyValue = System.getProperty(sysPropertyName); + if (sysPropertyValue != null && !sysPropertyName.isEmpty()) { + urlWithPlaceholders = urlWithPlaceholders.replaceAll("\\$\\{(" + sysPropertyName + ")\\}", sysPropertyValue); + } else { + throw new RemoteSessionManagementException("System property - " + sysPropertyName + + " is not defined, hence cannot resolve : " + urlWithPlaceholders); + } + } + return urlWithPlaceholders; + } +} diff --git a/components/extensions/remote-session-extension/pom.xml b/components/extensions/remote-session-extension/pom.xml new file mode 100644 index 000000000..3bc4c2ca2 --- /dev/null +++ b/components/extensions/remote-session-extension/pom.xml @@ -0,0 +1,59 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + extensions + 4.0.88-SNAPSHOT + ../pom.xml + + + 4.0.0 + remote-session-extension + pom + WSO2 Carbon - Remote Sessions Extension + http://wso2.org + + + org.wso2.carbon.device.mgt.extensions.remote.session + org.wso2.carbon.device.mgt.extensions.remote.session.endpoint + + + + + + + org.apache.felix + maven-scr-plugin + 1.7.2 + + + generate-scr-scrdescriptor + + scr + + + + + + + + diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.hbs index 55379c2c7..1c5038755 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.hbs +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.hbs @@ -1,6 +1,8 @@ {{unit "cdmf.unit.lib.qrcode"}} {{unit "cdmf.unit.device.type.qr-modal"}} - +{{#zone "topCss"}} + {{css "css/terminal.css"}} +{{/zone}} {{#if deviceFound}} {{#if isAuthorized}} @@ -16,12 +18,12 @@ data-owner="{{device.owner}}" data-status="{{device.status}}"> {{device.owner}}'s {{device.name}} - - - - + + + {{#if device.model}}

{{device.vendor}} {{device.model}}

{{/if}} @@ -82,6 +84,7 @@
  • Applications
  • Policy Compliance
  • +
  • Remote Session
  • {{/zone}} {{#zone "device-view-tab-injected-conents"}} @@ -127,6 +130,213 @@
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + +
    +
    +
    +
    +
    + +
    + + +
    +
    + {{#if device.location}} + {{unit "cdmf.unit.geo-dashboard" device=device noGeoFencing=false hideSearch=true}} + {{else}} +
    +

    + + Device location information is not available. +

    +
    +

    +

    +

    + {{/if}} +
    +
    +
    + +
    + + +
    +
    + + + + + + +
    +
    +

    + + No applications found. +

    +

    Please try refreshing in a while.

    +
    +
    +
    +
    +
    {{/zone}} @@ -159,5 +369,7 @@ type="text/x-handlebars-template"> {{js "js/device-detail.js"}} + {{js "js/remote-session.js"}} + {{js "js/jquery.touchSwipe.min.js"}} {{/if}} {{/zone}} diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.js index 51eacbc50..7e33ea036 100644 --- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.js +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/device-view.js @@ -23,6 +23,8 @@ function onRequest(context) { var owner = request.getParameter("owner"); var deviceViewData = {}; var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"]; + var carbonServer = require("carbon").server; + var constants = require("/app/modules/constants.js") if (deviceType && deviceId) { var deviceModule = require("/app/modules/business-controllers/device.js")["deviceModule"]; @@ -183,6 +185,23 @@ function onRequest(context) { } else if (response["status"] == "notFound") { deviceViewData["deviceFound"] = false; } + + var remoteSessionEndpoint = devicemgtProps["remoteSessionWSURL"].replace("https", "wss"); + var jwtService = carbonServer.osgiService('org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService'); + var jwtClient = jwtService.getJWTClient(); + var encodedClientKeys = session.get(constants["ENCODED_TENANT_BASED_WEB_SOCKET_CLIENT_CREDENTIALS"]); + var token = ""; + if (encodedClientKeys) { + var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"]; + var resp = tokenUtil.decode(encodedClientKeys).split(":"); + var tokenPair = jwtClient.getAccessToken(resp[0], resp[1], context.user.username,"default", {}); + if (tokenPair) { + token = tokenPair.accessToken; + } + } + remoteSessionEndpoint = remoteSessionEndpoint + "/remote/session/clients/" + deviceType + "/" + deviceId + + "?websocketToken=" + token + deviceViewData["remoteSessionEndpoint"] = remoteSessionEndpoint; } else { deviceViewData["deviceFound"] = false; } diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/css/terminal.css b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/css/terminal.css new file mode 100644 index 000000000..c6899148f --- /dev/null +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/css/terminal.css @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +.terminal { + background-color: #1f1d1d; + border: 1px solid #000; + color: #39ef39; + padding: 8px; + font-family: courier new; +} diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/js/jquery.touchSwipe.min.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/js/jquery.touchSwipe.min.js new file mode 100644 index 000000000..703634a25 --- /dev/null +++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.device-view/public/js/jquery.touchSwipe.min.js @@ -0,0 +1,14 @@ +/*! + * @fileOverview TouchSwipe - jQuery Plugin + * @version 1.6.18 + * + * @author Matt Bryson http://www.github.com/mattbryson + * @see https://github.com/mattbryson/TouchSwipe-Jquery-Plugin + * @see http://labs.rampinteractive.co.uk/touchSwipe/ + * @see http://plugins.jquery.com/project/touchSwipe + * @license + * Copyright (c) 2010-2015 Matt Bryson + * Dual licensed under the MIT or GPL Version 2 licenses. + * + */ +!function(factory){"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],factory):factory("undefined"!=typeof module&&module.exports?require("jquery"):jQuery)}(function($){"use strict";function init(options){return!options||void 0!==options.allowPageScroll||void 0===options.swipe&&void 0===options.swipeStatus||(options.allowPageScroll=NONE),void 0!==options.click&&void 0===options.tap&&(options.tap=options.click),options||(options={}),options=$.extend({},$.fn.swipe.defaults,options),this.each(function(){var $this=$(this),plugin=$this.data(PLUGIN_NS);plugin||(plugin=new TouchSwipe(this,options),$this.data(PLUGIN_NS,plugin))})}function TouchSwipe(element,options){function touchStart(jqEvent){if(!(getTouchInProgress()||$(jqEvent.target).closest(options.excludedElements,$element).length>0)){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;if(!event.pointerType||"mouse"!=event.pointerType||0!=options.fallbackToMouseEvents){var ret,touches=event.touches,evt=touches?touches[0]:event;return phase=PHASE_START,touches?fingerCount=touches.length:options.preventDefaultEvents!==!1&&jqEvent.preventDefault(),distance=0,direction=null,currentDirection=null,pinchDirection=null,duration=0,startTouchesDistance=0,endTouchesDistance=0,pinchZoom=1,pinchDistance=0,maximumsMap=createMaximumsData(),cancelMultiFingerRelease(),createFingerData(0,evt),!touches||fingerCount===options.fingers||options.fingers===ALL_FINGERS||hasPinches()?(startTime=getTimeStamp(),2==fingerCount&&(createFingerData(1,touches[1]),startTouchesDistance=endTouchesDistance=calculateTouchesDistance(fingerData[0].start,fingerData[1].start)),(options.swipeStatus||options.pinchStatus)&&(ret=triggerHandler(event,phase))):ret=!1,ret===!1?(phase=PHASE_CANCEL,triggerHandler(event,phase),ret):(options.hold&&(holdTimeout=setTimeout($.proxy(function(){$element.trigger("hold",[event.target]),options.hold&&(ret=options.hold.call($element,event,event.target))},this),options.longTapThreshold)),setTouchInProgress(!0),null)}}}function touchMove(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;if(phase!==PHASE_END&&phase!==PHASE_CANCEL&&!inMultiFingerRelease()){var ret,touches=event.touches,evt=touches?touches[0]:event,currentFinger=updateFingerData(evt);if(endTime=getTimeStamp(),touches&&(fingerCount=touches.length),options.hold&&clearTimeout(holdTimeout),phase=PHASE_MOVE,2==fingerCount&&(0==startTouchesDistance?(createFingerData(1,touches[1]),startTouchesDistance=endTouchesDistance=calculateTouchesDistance(fingerData[0].start,fingerData[1].start)):(updateFingerData(touches[1]),endTouchesDistance=calculateTouchesDistance(fingerData[0].end,fingerData[1].end),pinchDirection=calculatePinchDirection(fingerData[0].end,fingerData[1].end)),pinchZoom=calculatePinchZoom(startTouchesDistance,endTouchesDistance),pinchDistance=Math.abs(startTouchesDistance-endTouchesDistance)),fingerCount===options.fingers||options.fingers===ALL_FINGERS||!touches||hasPinches()){if(direction=calculateDirection(currentFinger.start,currentFinger.end),currentDirection=calculateDirection(currentFinger.last,currentFinger.end),validateDefaultEvent(jqEvent,currentDirection),distance=calculateDistance(currentFinger.start,currentFinger.end),duration=calculateDuration(),setMaxDistance(direction,distance),ret=triggerHandler(event,phase),!options.triggerOnTouchEnd||options.triggerOnTouchLeave){var inBounds=!0;if(options.triggerOnTouchLeave){var bounds=getbounds(this);inBounds=isInBounds(currentFinger.end,bounds)}!options.triggerOnTouchEnd&&inBounds?phase=getNextPhase(PHASE_MOVE):options.triggerOnTouchLeave&&!inBounds&&(phase=getNextPhase(PHASE_END)),phase!=PHASE_CANCEL&&phase!=PHASE_END||triggerHandler(event,phase)}}else phase=PHASE_CANCEL,triggerHandler(event,phase);ret===!1&&(phase=PHASE_CANCEL,triggerHandler(event,phase))}}function touchEnd(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent,touches=event.touches;if(touches){if(touches.length&&!inMultiFingerRelease())return startMultiFingerRelease(event),!0;if(touches.length&&inMultiFingerRelease())return!0}return inMultiFingerRelease()&&(fingerCount=fingerCountAtRelease),endTime=getTimeStamp(),duration=calculateDuration(),didSwipeBackToCancel()||!validateSwipeDistance()?(phase=PHASE_CANCEL,triggerHandler(event,phase)):options.triggerOnTouchEnd||options.triggerOnTouchEnd===!1&&phase===PHASE_MOVE?(options.preventDefaultEvents!==!1&&jqEvent.preventDefault(),phase=PHASE_END,triggerHandler(event,phase)):!options.triggerOnTouchEnd&&hasTap()?(phase=PHASE_END,triggerHandlerForGesture(event,phase,TAP)):phase===PHASE_MOVE&&(phase=PHASE_CANCEL,triggerHandler(event,phase)),setTouchInProgress(!1),null}function touchCancel(){fingerCount=0,endTime=0,startTime=0,startTouchesDistance=0,endTouchesDistance=0,pinchZoom=1,cancelMultiFingerRelease(),setTouchInProgress(!1)}function touchLeave(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;options.triggerOnTouchLeave&&(phase=getNextPhase(PHASE_END),triggerHandler(event,phase))}function removeListeners(){$element.unbind(START_EV,touchStart),$element.unbind(CANCEL_EV,touchCancel),$element.unbind(MOVE_EV,touchMove),$element.unbind(END_EV,touchEnd),LEAVE_EV&&$element.unbind(LEAVE_EV,touchLeave),setTouchInProgress(!1)}function getNextPhase(currentPhase){var nextPhase=currentPhase,validTime=validateSwipeTime(),validDistance=validateSwipeDistance(),didCancel=didSwipeBackToCancel();return!validTime||didCancel?nextPhase=PHASE_CANCEL:!validDistance||currentPhase!=PHASE_MOVE||options.triggerOnTouchEnd&&!options.triggerOnTouchLeave?!validDistance&¤tPhase==PHASE_END&&options.triggerOnTouchLeave&&(nextPhase=PHASE_CANCEL):nextPhase=PHASE_END,nextPhase}function triggerHandler(event,phase){var ret,touches=event.touches;return(didSwipe()||hasSwipes())&&(ret=triggerHandlerForGesture(event,phase,SWIPE)),(didPinch()||hasPinches())&&ret!==!1&&(ret=triggerHandlerForGesture(event,phase,PINCH)),didDoubleTap()&&ret!==!1?ret=triggerHandlerForGesture(event,phase,DOUBLE_TAP):didLongTap()&&ret!==!1?ret=triggerHandlerForGesture(event,phase,LONG_TAP):didTap()&&ret!==!1&&(ret=triggerHandlerForGesture(event,phase,TAP)),phase===PHASE_CANCEL&&touchCancel(event),phase===PHASE_END&&(touches?touches.length||touchCancel(event):touchCancel(event)),ret}function triggerHandlerForGesture(event,phase,gesture){var ret;if(gesture==SWIPE){if($element.trigger("swipeStatus",[phase,direction||null,distance||0,duration||0,fingerCount,fingerData,currentDirection]),options.swipeStatus&&(ret=options.swipeStatus.call($element,event,phase,direction||null,distance||0,duration||0,fingerCount,fingerData,currentDirection),ret===!1))return!1;if(phase==PHASE_END&&validateSwipe()){if(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),$element.trigger("swipe",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipe&&(ret=options.swipe.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection),ret===!1))return!1;switch(direction){case LEFT:$element.trigger("swipeLeft",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeLeft&&(ret=options.swipeLeft.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case RIGHT:$element.trigger("swipeRight",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeRight&&(ret=options.swipeRight.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case UP:$element.trigger("swipeUp",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeUp&&(ret=options.swipeUp.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case DOWN:$element.trigger("swipeDown",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeDown&&(ret=options.swipeDown.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection))}}}if(gesture==PINCH){if($element.trigger("pinchStatus",[phase,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchStatus&&(ret=options.pinchStatus.call($element,event,phase,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData),ret===!1))return!1;if(phase==PHASE_END&&validatePinch())switch(pinchDirection){case IN:$element.trigger("pinchIn",[pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchIn&&(ret=options.pinchIn.call($element,event,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData));break;case OUT:$element.trigger("pinchOut",[pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchOut&&(ret=options.pinchOut.call($element,event,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData))}}return gesture==TAP?phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),hasDoubleTap()&&!inDoubleTap()?(doubleTapStartTime=getTimeStamp(),singleTapTimeout=setTimeout($.proxy(function(){doubleTapStartTime=null,$element.trigger("tap",[event.target]),options.tap&&(ret=options.tap.call($element,event,event.target))},this),options.doubleTapThreshold)):(doubleTapStartTime=null,$element.trigger("tap",[event.target]),options.tap&&(ret=options.tap.call($element,event,event.target)))):gesture==DOUBLE_TAP?phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),doubleTapStartTime=null,$element.trigger("doubletap",[event.target]),options.doubleTap&&(ret=options.doubleTap.call($element,event,event.target))):gesture==LONG_TAP&&(phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),doubleTapStartTime=null,$element.trigger("longtap",[event.target]),options.longTap&&(ret=options.longTap.call($element,event,event.target)))),ret}function validateSwipeDistance(){var valid=!0;return null!==options.threshold&&(valid=distance>=options.threshold),valid}function didSwipeBackToCancel(){var cancelled=!1;return null!==options.cancelThreshold&&null!==direction&&(cancelled=getMaxDistance(direction)-distance>=options.cancelThreshold),cancelled}function validatePinchDistance(){return null!==options.pinchThreshold?pinchDistance>=options.pinchThreshold:!0}function validateSwipeTime(){var result;return result=options.maxTimeThreshold?!(duration>=options.maxTimeThreshold):!0}function validateDefaultEvent(jqEvent,direction){if(options.preventDefaultEvents!==!1)if(options.allowPageScroll===NONE)jqEvent.preventDefault();else{var auto=options.allowPageScroll===AUTO;switch(direction){case LEFT:(options.swipeLeft&&auto||!auto&&options.allowPageScroll!=HORIZONTAL)&&jqEvent.preventDefault();break;case RIGHT:(options.swipeRight&&auto||!auto&&options.allowPageScroll!=HORIZONTAL)&&jqEvent.preventDefault();break;case UP:(options.swipeUp&&auto||!auto&&options.allowPageScroll!=VERTICAL)&&jqEvent.preventDefault();break;case DOWN:(options.swipeDown&&auto||!auto&&options.allowPageScroll!=VERTICAL)&&jqEvent.preventDefault();break;case NONE:}}}function validatePinch(){var hasCorrectFingerCount=validateFingers(),hasEndPoint=validateEndPoint(),hasCorrectDistance=validatePinchDistance();return hasCorrectFingerCount&&hasEndPoint&&hasCorrectDistance}function hasPinches(){return!!(options.pinchStatus||options.pinchIn||options.pinchOut)}function didPinch(){return!(!validatePinch()||!hasPinches())}function validateSwipe(){var hasValidTime=validateSwipeTime(),hasValidDistance=validateSwipeDistance(),hasCorrectFingerCount=validateFingers(),hasEndPoint=validateEndPoint(),didCancel=didSwipeBackToCancel(),valid=!didCancel&&hasEndPoint&&hasCorrectFingerCount&&hasValidDistance&&hasValidTime;return valid}function hasSwipes(){return!!(options.swipe||options.swipeStatus||options.swipeLeft||options.swipeRight||options.swipeUp||options.swipeDown)}function didSwipe(){return!(!validateSwipe()||!hasSwipes())}function validateFingers(){return fingerCount===options.fingers||options.fingers===ALL_FINGERS||!SUPPORTS_TOUCH}function validateEndPoint(){return 0!==fingerData[0].end.x}function hasTap(){return!!options.tap}function hasDoubleTap(){return!!options.doubleTap}function hasLongTap(){return!!options.longTap}function validateDoubleTap(){if(null==doubleTapStartTime)return!1;var now=getTimeStamp();return hasDoubleTap()&&now-doubleTapStartTime<=options.doubleTapThreshold}function inDoubleTap(){return validateDoubleTap()}function validateTap(){return(1===fingerCount||!SUPPORTS_TOUCH)&&(isNaN(distance)||distanceoptions.longTapThreshold&&DOUBLE_TAP_THRESHOLD>distance}function didTap(){return!(!validateTap()||!hasTap())}function didDoubleTap(){return!(!validateDoubleTap()||!hasDoubleTap())}function didLongTap(){return!(!validateLongTap()||!hasLongTap())}function startMultiFingerRelease(event){previousTouchEndTime=getTimeStamp(),fingerCountAtRelease=event.touches.length+1}function cancelMultiFingerRelease(){previousTouchEndTime=0,fingerCountAtRelease=0}function inMultiFingerRelease(){var withinThreshold=!1;if(previousTouchEndTime){var diff=getTimeStamp()-previousTouchEndTime;diff<=options.fingerReleaseThreshold&&(withinThreshold=!0)}return withinThreshold}function getTouchInProgress(){return!($element.data(PLUGIN_NS+"_intouch")!==!0)}function setTouchInProgress(val){$element&&(val===!0?($element.bind(MOVE_EV,touchMove),$element.bind(END_EV,touchEnd),LEAVE_EV&&$element.bind(LEAVE_EV,touchLeave)):($element.unbind(MOVE_EV,touchMove,!1),$element.unbind(END_EV,touchEnd,!1),LEAVE_EV&&$element.unbind(LEAVE_EV,touchLeave,!1)),$element.data(PLUGIN_NS+"_intouch",val===!0))}function createFingerData(id,evt){var f={start:{x:0,y:0},last:{x:0,y:0},end:{x:0,y:0}};return f.start.x=f.last.x=f.end.x=evt.pageX||evt.clientX,f.start.y=f.last.y=f.end.y=evt.pageY||evt.clientY,fingerData[id]=f,f}function updateFingerData(evt){var id=void 0!==evt.identifier?evt.identifier:0,f=getFingerData(id);return null===f&&(f=createFingerData(id,evt)),f.last.x=f.end.x,f.last.y=f.end.y,f.end.x=evt.pageX||evt.clientX,f.end.y=evt.pageY||evt.clientY,f}function getFingerData(id){return fingerData[id]||null}function setMaxDistance(direction,distance){direction!=NONE&&(distance=Math.max(distance,getMaxDistance(direction)),maximumsMap[direction].distance=distance)}function getMaxDistance(direction){return maximumsMap[direction]?maximumsMap[direction].distance:void 0}function createMaximumsData(){var maxData={};return maxData[LEFT]=createMaximumVO(LEFT),maxData[RIGHT]=createMaximumVO(RIGHT),maxData[UP]=createMaximumVO(UP),maxData[DOWN]=createMaximumVO(DOWN),maxData}function createMaximumVO(dir){return{direction:dir,distance:0}}function calculateDuration(){return endTime-startTime}function calculateTouchesDistance(startPoint,endPoint){var diffX=Math.abs(startPoint.x-endPoint.x),diffY=Math.abs(startPoint.y-endPoint.y);return Math.round(Math.sqrt(diffX*diffX+diffY*diffY))}function calculatePinchZoom(startDistance,endDistance){var percent=endDistance/startDistance*1;return percent.toFixed(2)}function calculatePinchDirection(){return 1>pinchZoom?OUT:IN}function calculateDistance(startPoint,endPoint){return Math.round(Math.sqrt(Math.pow(endPoint.x-startPoint.x,2)+Math.pow(endPoint.y-startPoint.y,2)))}function calculateAngle(startPoint,endPoint){var x=startPoint.x-endPoint.x,y=endPoint.y-startPoint.y,r=Math.atan2(y,x),angle=Math.round(180*r/Math.PI);return 0>angle&&(angle=360-Math.abs(angle)),angle}function calculateDirection(startPoint,endPoint){if(comparePoints(startPoint,endPoint))return NONE;var angle=calculateAngle(startPoint,endPoint);return 45>=angle&&angle>=0?LEFT:360>=angle&&angle>=315?LEFT:angle>=135&&225>=angle?RIGHT:angle>45&&135>angle?DOWN:UP}function getTimeStamp(){var now=new Date;return now.getTime()}function getbounds(el){el=$(el);var offset=el.offset(),bounds={left:offset.left,right:offset.left+el.outerWidth(),top:offset.top,bottom:offset.top+el.outerHeight()};return bounds}function isInBounds(point,bounds){return point.x>bounds.left&&point.xbounds.top&&point.y + + + + + org.wso2.carbon.devicemgt-plugins + extensions-feature + 4.0.88-SNAPSHOT + ../pom.xml + + 4.0.0 + + org.wso2.carbon.device.mgt.remote.session.feature + pom + 4.0.88-SNAPSHOT + WSO2 Carbon - Remote Session Manager + http://wso2.org + This feature contains the core bundles required iot Remote Sessions + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.extensions.remote.session + + + io.github.openfeign + feign-jaxrs + ${io.github.openfeign.version} + + + io.github.openfeign + feign-gson + ${io.github.openfeign.version} + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + package + + copy + + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.extensions.remote.session.endpoint + ${carbon.devicemgt.plugins.version} + war + true + + ${project.build.directory}/maven-shared-archive-resources/webapps/ + + remote#session.war + + + + + + + + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + p2-feature-generation + package + + p2-feature-gen + + + org.wso2.carbon.device.mgt.remote.session + ../../../features/etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + org.eclipse.equinox.p2.type.group:true + + + + + org.wso2.carbon.devicemgt-plugins:org.wso2.carbon.device.mgt.extensions.remote.session:${carbon.devicemgt.plugins.version} + + + + org.wso2.carbon.identity.inbound.auth.oauth2:org.wso2.carbon.identity.oauth.stub:${identity.inbound.auth.oauth.version} + + + io.github.openfeign:feign-core:${io.github.openfeign.version} + + + io.github.openfeign:feign-slf4j:${io.github.openfeign.version} + + + io.github.openfeign:feign-gson:${io.github.openfeign.version} + + + + + + + + + + \ No newline at end of file diff --git a/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.feature/src/main/resources/build.properties b/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.feature/src/main/resources/build.properties new file mode 100644 index 000000000..e0fe0c513 --- /dev/null +++ b/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.feature/src/main/resources/build.properties @@ -0,0 +1,19 @@ +# +# Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +# +# WSO2 Inc. licenses this file to you under the Apache License, +# Version 2.0 (the "License"); you may not use this file except +# in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +custom = true diff --git a/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.feature/src/main/resources/p2.inf b/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.feature/src/main/resources/p2.inf new file mode 100644 index 000000000..9efb7a994 --- /dev/null +++ b/features/extensions-feature/org.wso2.carbon.device.mgt.remote.session.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/org.wso2.carbon.device.mgt.remote.session_${feature.version}/webapps/,target:${installFolder}/../../deployment/server/webapps/,overwrite:true);\ \ No newline at end of file diff --git a/features/extensions-feature/pom.xml b/features/extensions-feature/pom.xml index ef722c1c1..bf53dceda 100644 --- a/features/extensions-feature/pom.xml +++ b/features/extensions-feature/pom.xml @@ -43,6 +43,7 @@ org.wso2.extension.siddhi.execution.json.feature org.wso2.carbon.device.mgt.notification.listener.feature org.wso2.gpl.siddhi.extension.geo.script.feature + org.wso2.carbon.device.mgt.remote.session.feature diff --git a/pom.xml b/pom.xml index 6aaca7f04..5e4b79335 100644 --- a/pom.xml +++ b/pom.xml @@ -483,6 +483,11 @@ ${carbon.devicemgt.plugins.version} war + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.extensions.remote.session + ${carbon.devicemgt.plugins.version} +