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..df24036b5 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session.endpoint/pom.xml @@ -0,0 +1,68 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + remote-session-extension + 4.0.35-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..61c5dac64 --- /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,136 @@ +/* + * Copyright (c) 2015, 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.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionInvalidException; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +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 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); + private RemoteSessionConstants.CONNECTION_MODES connectionMode = RemoteSessionConstants.CONNECTION_MODES.CLIENT_MODE; + + /** + * Web socket onOpen - When client sends a message + * + * @param session - Users registered session.. + */ + @OnOpen + public void onOpen(Session session, @PathParam("deviceType") String deviceType, @PathParam("deviceId") String + deviceId) { + System.out.print("**************Open***************"); + if (log.isDebugEnabled()) { + log.debug("WebSocket opened, for RemoteSession id: " + session.getId()); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().initializeSession(session, deviceType, deviceId); + System.out.print("**************Opened***************"); + } catch (RemoteSessionInvalidException e) { + System.out.print(e.getMessage()); + 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); + } + } catch (RemoteSessionManagementException e) { + System.out.print(e.getMessage()); + if (log.isDebugEnabled()) { + log.error("Error occurred while initializing session ", e); + } + try { + session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Error occurred while adding operation")); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } + + } + + /** + * Web socket onMessage - When client sends a message + * + * @param session - Users registered session. + * @param message - Status code for web-socket close. + */ + @OnMessage + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onMessage - When client sends a message + * + * @param session - Users registered session. + * @param message - Message which needs to send to peer + */ + @OnMessage + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onClose - Remove the registered sessions + * + * @param session - Users registered session. + * @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 - Remove the registered sessions + * + * @param session - Users registered session. + * @param throwable - Status code for web-socket close. + */ + @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..3f2ae343a --- /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,137 @@ +/* + * Copyright (c) 2015, 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.constants.RemoteSessionConstants; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionInvalidException; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +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 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); + private RemoteSessionConstants.CONNECTION_MODES connectionMode = RemoteSessionConstants.CONNECTION_MODES.DEVICE_MODE; + + /** + * Web socket onOpen - When client sends a message + * + * @param session - Users registered session.. + */ + @OnOpen + public void onOpen(Session session, @PathParam("deviceType") String deviceType, @PathParam("deviceId") String + deviceId, @PathParam("operationId") String operationId) { + System.out.print("**************Open***************"+operationId); + if (log.isDebugEnabled()) { + log.debug("WebSocket opened, for RemoteSession id: " + session.getId()); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().initializeSession(session, deviceType, + deviceId, operationId); + System.out.print("**************Opened***************"); + } catch (RemoteSessionInvalidException e) { + System.out.print(e.getMessage()); + 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); + } + } catch (RemoteSessionManagementException e) { + System.out.print(e.getMessage()); + if (log.isDebugEnabled()) { + log.error("Error occurred while initializing session ", e); + } + try { + session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Error occurred while adding operation")); + } catch (IOException ex) { + if (log.isDebugEnabled()) { + log.error("Failed to disconnect the client.", ex); + } + } + } + + } + + /** + * Web socket onMessage - When client sends a message + * + * @param session - Users registered session. + * @param message - Status code for web-socket close. + */ + @OnMessage + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onMessage - When client sends a message + * + * @param session - Users registered session. + * @param message - Message which needs to send to peer + */ + @OnMessage + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + super.onMessage(session, message, deviceType, deviceId); + } + + /** + * Web socket onClose - Remove the registered sessions + * + * @param session - Users registered session. + * @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 - Remove the registered sessions + * + * @param session - Users registered session. + * @param throwable - Status code for web-socket close. + */ + @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..a6466d313 --- /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,144 @@ +/* + * Copyright (c) 2015, 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.exception.RemoteSessionInvalidException; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; +import org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.utils.ServiceHolder; + +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 - Users registered session. + * @param message - Status code for web-socket close. + */ + public void onMessage(Session session, String message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + System.out.print("______________" + session.getId()); + 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 (RemoteSessionInvalidException 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 - When client sends a message + * + * @param session - Users registered session. + * @param message - Message which needs to send to peer + */ + public void onMessage(Session session, byte[] message, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) throws RemoteSessionManagementException { + System.out.print("______________" + session.getId()); + 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 (RemoteSessionInvalidException 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 - Remove the registered sessions + * + * @param session - Users registered session. + * @param reason - Status code for web-socket close. + */ + public void onClose(Session session, CloseReason reason, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + if (log.isDebugEnabled()) { + log.debug("Closing a WebSocket due to " + reason.getReasonPhrase() + ", for session ID:" + session.getId + () + ", for request URI - " + session.getRequestURI() + " device type: " + deviceType + " device id: " + deviceId); + } + try { + ServiceHolder.getInstance().getRemoteSessionManagementService().endSession(session); + 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); + } + } + } + + /** + * Web socket onError - Remove the registered sessions + * + * @param session - Users registered session. + * @param throwable - Status code for web-socket close. + */ + public void onError(Session session, Throwable throwable, @PathParam("deviceType") String deviceType, @PathParam + ("deviceId") String deviceId) { + 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); + 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..9bf277389 --- /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,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. + * + */ + +package org.wso2.carbon.device.mgt.extensions.remote.session.endpoint.constants; + +public class Constants { + public static final String 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/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..a0088689b --- /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,36 @@ +/* + * + * Copyright (c) 2016, 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; + +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..c27ffb491 --- /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,53 @@ +/* + * Copyright (c) 2015, 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; + +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..9f7fd9910 --- /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..8545ae269 --- /dev/null +++ b/components/extensions/remote-session-extension/org.wso2.carbon.device.mgt.extensions.remote.session/pom.xml @@ -0,0 +1,128 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + remote-session-extension + 4.0.35-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 + + + io.github.openfeign + feign-core + + + io.github.openfeign + feign-jaxrs + + + io.github.openfeign + feign-gson + + + javax.ws.rs + jsr311-api + + + io.github.openfeign + feign-slf4j + + + 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..c370584ce --- /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,84 @@ +/* + * 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.RemoteSessionInvalidException; +import org.wso2.carbon.device.mgt.extensions.remote.session.exception.RemoteSessionManagementException; + +import javax.websocket.Session; + +/** + * Class @{@link RemoteSessionManagementService} use for managing remote sessions + */ +public interface RemoteSessionManagementService { + + /** + * Initialize session based on web socket request .this method use by the device to connect + * + * @param session Web socket RemoteSession + * @param deviceType Device Type + * @param deviceId Device Id + * @throws RemoteSessionInvalidException throws when session cannot be made due to invalid data + * @throws RemoteSessionManagementException throws when session has error with accessing device resources + */ + public void initializeSession(Session session, String deviceType, String deviceId) throws + RemoteSessionInvalidException, RemoteSessionManagementException; + + /** + * Initialize session based on web socket request .this method use 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 RemoteSessionInvalidException throws when session cannot be made due to invalid data + * @throws RemoteSessionManagementException throws when session has error with accessing device resources + */ + public void initializeSession(Session session, String deviceType, String deviceId, String operationId) throws + RemoteSessionInvalidException, RemoteSessionManagementException; + + /** + * Send message to connected remote device or client + * + * @param session Web socket RemoteSession + * @param message Message needs to send to peer connection client + * @throws RemoteSessionInvalidException + * @throws RemoteSessionManagementException + */ + public void sendMessageToPeer(Session session, String message) throws + RemoteSessionInvalidException, RemoteSessionManagementException; + + /** + * Send message to connected remote device or client + * + * @param session Web socket RemoteSession + * @param message Message needs to send to peer connection + * @throws RemoteSessionInvalidException + * @throws RemoteSessionManagementException + */ + public void sendMessageToPeer(Session session, byte[] message) throws + RemoteSessionInvalidException, RemoteSessionManagementException; + + /** + * Close the session + * + * @param session Web socket RemoteSession + */ + public void endSession(Session session); + +} 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..9fe986580 --- /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,274 @@ +/* + * 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.RemoteSessionInvalidException; +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.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class RemoteSessionManagementServiceImpl implements RemoteSessionManagementService { + + private static final Log log = LogFactory.getLog(RemoteSessionManagementServiceImpl.class); + private static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss"; + private static final int MAX_BUFFER_SIZE = 640 * 1024; + + + @Override + public void initializeSession(Session session, String deviceType, String deviceId, String operationId) throws + RemoteSessionInvalidException, RemoteSessionManagementException { + + + if (!RemoteSessionManagementDataHolder.getInstance().isEnabled()) { + throw new RemoteSessionManagementException("Remote session feature is disabled."); + } else if (RemoteSessionManagementDataHolder.getInstance().getServerUrl() == null) { + throw new RemoteSessionManagementException("Server url haven't been configured."); + } + + 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); + boolean userAuthorized = RemoteSessionManagementDataHolder.getInstance() + .getDeviceAccessAuthorizationService() + .isUserAuthorized(deviceIdentifier, authenticationInfo.getUsername()); + if (userAuthorized) { + log.info("Operation ID: " + operationId); + + if (operationId != null) { + Session pendingSession = RemoteSessionManagementDataHolder.getInstance() + .getDeviceRequestMap().get((authenticationInfo.getTenantDomain() + "/" + deviceType + + "/" + deviceId)); + + if (pendingSession != null) { + RemoteSession clientRemote = RemoteSessionManagementDataHolder.getInstance() + .getSessionMap().get(pendingSession.getId()); + if (clientRemote != null) { + + if (clientRemote.getOperationId().equals(operationId)) { + RemoteSession deviceRemote = new RemoteSession(authenticationInfo.getTenantDomain + (), deviceType, deviceId, operationId, RemoteSessionConstants + .CONNECTION_MODES.DEVICE_MODE); + deviceRemote.setPeerSession(pendingSession); + clientRemote.setPeerSession(session); + RemoteSessionManagementDataHolder.getInstance().getSessionMap().put(session + .getId(), deviceRemote); + RemoteSessionManagementDataHolder.getInstance().getDeviceRequestMap().remove( + (authenticationInfo.getTenantDomain() + "/" + deviceType + "/" + deviceId)); + // Send Remote connect response + JSONObject message = new JSONObject(); + message.put("code", RemoteSessionConstants.REMOTE_CONNECT); + message.put("operation_response", "connected"); + deviceRemote.sendMessageToPeer(message.toString()); + // set buffer sizes + session.setMaxBinaryMessageBufferSize(MAX_BUFFER_SIZE); + session.setMaxTextMessageBufferSize(MAX_BUFFER_SIZE); + } 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); + } + } else { + // Create new remote control operation to start the session + Session pendingSession = RemoteSessionManagementDataHolder.getInstance().getDeviceRequestMap().get( + (authenticationInfo.getTenantDomain() + "/" + deviceType + "/" + deviceId)); + if (pendingSession != null && pendingSession.isOpen()) { + throw new RemoteSessionManagementException("Another client session waiting on device to connect."); + } else { + Session lastSession = RemoteSessionManagementDataHolder.getInstance().getDeviceRequestMap().putIfAbsent( + (authenticationInfo.getTenantDomain() + "/" + deviceType + "/" + deviceId), + session); + + if (lastSession == null) { + Operation operation = new ConfigOperation(); + operation.setCode(RemoteSessionConstants.REMOTE_CONNECT); + operation.setEnabled(true); + JSONObject payload = new JSONObject(); + payload.put("serverUrl", RemoteSessionManagementDataHolder.getInstance().getServerUrl()); + operation.setPayLoad(payload.toString()); + String date = new SimpleDateFormat(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); + log.info("Activity id: " + activity.getActivityId()); + RemoteSession clientRemote = new RemoteSession(authenticationInfo.getTenantDomain(), + deviceType, deviceId, activity.getActivityId().replace(DeviceManagementConstants + .OperationAttributes.ACTIVITY, ""), RemoteSessionConstants + .CONNECTION_MODES.CLIENT_MODE); + RemoteSessionManagementDataHolder.getInstance().getSessionMap().put(session.getId(), clientRemote); + session.setMaxBinaryMessageBufferSize(MAX_BUFFER_SIZE); + session.setMaxTextMessageBufferSize(MAX_BUFFER_SIZE); + } + } + } + log.info("Current session count: " + RemoteSessionManagementDataHolder + .getInstance().getSessionMap().size()); + + } else { + throw new RemoteSessionInvalidException("Missing device Id or type ", new CloseReason + (CloseReason.CloseCodes.CANNOT_ACCEPT, "Missing device Id or device type ")); + } + } else { + throw new RemoteSessionInvalidException("Unauthorized Access for the device Type : " + deviceType + + " , deviceId : " + deviceId, new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, + "Unauthorized Access")); + } + } catch (OperationManagementException | InvalidDeviceException e) { + throw new RemoteSessionManagementException("Error occurred while adding initial operation for the device Type : " + + deviceType + " , deviceId : " + deviceId, e); + } catch (DeviceAccessAuthorizationException e) { + throw new RemoteSessionManagementException("Error occurred while device access authorization for the device Type : " + + deviceType + " , " + "deviceId : " + deviceId, e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + + } else { + throw new RemoteSessionInvalidException("Invalid token", new CloseReason(CloseReason.CloseCodes + .CANNOT_ACCEPT, "Invalid token")); + } + } + + @Override + public void initializeSession(Session session, String deviceType, String deviceId) throws RemoteSessionInvalidException, RemoteSessionManagementException { + initializeSession(session, deviceType, deviceId, null); + } + + /** + * Implements the behaviour of sending message to peer connection + * + * @param session Web socket RemoteSession + * @param message Message needs to send to peer connection + * @throws RemoteSessionInvalidException 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, + RemoteSessionInvalidException { + JSONObject jsonObject = new JSONObject(message); + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().get(session.getId()); + if (remoteSession != null) { + if (remoteSession.getConnection_mode().equals(RemoteSessionConstants.CONNECTION_MODES.CLIENT_MODE)) { + jsonObject.put("id", remoteSession.getOperationId()); + } + remoteSession.sendMessageToPeer(jsonObject.toString()); + } else { + throw new RemoteSessionInvalidException("Remote Session cannot be found ", new CloseReason(CloseReason + .CloseCodes.CANNOT_ACCEPT, "Invalid RemoteSession")); + } + } + + + @Override + public void sendMessageToPeer(Session session, byte[] message) throws RemoteSessionInvalidException, + RemoteSessionManagementException { + + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().get(session.getId()); + if (remoteSession != null) { + remoteSession.sendMessageToPeer(message); + } else { + throw new RemoteSessionInvalidException("Remote Session cannot be found ", new CloseReason(CloseReason + .CloseCodes.CANNOT_ACCEPT, "Invalid RemoteSession")); + } + } + + /** + * Closing the session and cleanup the resources + * + * @param session Web socket RemoteSession + */ + @Override + public void endSession(Session session) { + + RemoteSession remoteSession = RemoteSessionManagementDataHolder.getInstance().getSessionMap().remove(session.getId()); + if (remoteSession != null) { + String operationId = remoteSession.getOperationId(); + Session peerSession = remoteSession.getPeerSession(); + if (peerSession != null) { + RemoteSessionManagementDataHolder.getInstance().getSessionMap().remove(peerSession.getId()); + } + if (operationId != null) { + Session lastSession = RemoteSessionManagementDataHolder.getInstance().getDeviceRequestMap().get( + (remoteSession.getTenantDomain() + "/" + remoteSession.getDeviceType() + "/" + remoteSession + .getDeviceId())); + if (lastSession != null && lastSession.getId().equals(session.getId())) { + RemoteSessionManagementDataHolder.getInstance().getDeviceRequestMap().remove( + (remoteSession.getTenantDomain() + "/" + remoteSession.getDeviceType() + "/" + remoteSession + .getDeviceId())); + } + } + } + + } +} 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..9da426df3 --- /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) 2016, 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..ca51bc157 --- /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,33 @@ +/* + * Copyright (c) 2016, 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; + +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..ab19f5e08 --- /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) 2016, 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 final String COOKIE_KEY_VALUE_SEPERATOR = "="; + private static final String COOKIE_KEYPAIR_SEPERATOR = ";"; + private static final String COOKIE = "cookie"; + private static final String TOKEN_TYPE = "bearer"; + private static final String TOKEN_IDENTIFIER = "websocketToken"; + private static final String QUERY_STRING_SEPERATOR = "&"; + private static final String QUERY_KEY_VALUE_SEPERATOR = "="; + private static final String QUERY_STRING = "queryString"; + 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(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(COOKIE).get(0); + String[] properties = cookieString.split(COOKIE_KEYPAIR_SEPERATOR); + String token; + for (String keyValuePair: properties) { + if(TOKEN_IDENTIFIER.equals((keyValuePair.split(COOKIE_KEY_VALUE_SEPERATOR)[0]).trim())){ + token = (keyValuePair.split(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(QUERY_STRING).get(0); + if (queryString != null) { + String[] allQueryParamPairs = queryString.split(QUERY_STRING_SEPERATOR); + for (String keyValuePair : allQueryParamPairs) { + String[] queryParamPair = keyValuePair.split(QUERY_KEY_VALUE_SEPERATOR); + if (queryParamPair.length != 2) { + log.warn("Invalid query string [" + queryString + "] passed in."); + break; + } + if (queryParamPair[0].equals(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..8b02b920c --- /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,174 @@ +/* + * Copyright (c) 2016, 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 { + EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory(); + return easySSLPSFactory; + } 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..3e5715476 --- /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) 2016, 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..693d26d35 --- /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,49 @@ +/* + * Copyright (c) 2016, 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 this feature + */ +public class RemoteSessionConstants { + + public static final String SCOPE_IDENTIFIER = "scopes"; + public static final String MAXIMUM_TOTAL_HTTP_CONNECTION = "maximumTotalHttpConnection"; + public static final String MAXIMUM_HTTP_CONNECTION_PER_HOST = "maximumHttpConnectionPerHost"; + 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 DEVICE_ID = "deviceId"; + public static final String DEVICE_TYPE = "deviceType"; + public static final String REMOTE_CONNECT = "REMOTE_CONNECT"; + public static final String QUERY_STRING = "queryString"; + public static final String OPERATION_CODE = "operation_code"; + public static final String REMOTE_SESSION_DEVICE_ENDPOINT_CONTEXT = "/remote/session/devices"; + + // Constants for remote session Connection modes + + public enum CONNECTION_MODES { + CLIENT_MODE, DEVICE_MODE + } + + private RemoteSessionConstants() { + } + +} 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..7a351565b --- /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,125 @@ +/* + * 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.RemoteSessionInvalidException; + +import javax.websocket.CloseReason; +import javax.websocket.Session; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class RemoteSession { + + private static final Log log = LogFactory.getLog(RemoteSession.class); + private String tenantDomain, operationId, deviceType, deviceId; + private Session peerSession; + private RemoteSessionConstants.CONNECTION_MODES connection_mode; + private final Object writeLockObject = new Object(); + + + public RemoteSession(String tenantDomain, String deviceType, String deviceId, String operationId, RemoteSessionConstants.CONNECTION_MODES connection_mode) { + this.deviceType = deviceType; + this.deviceId = deviceId; + this.tenantDomain = tenantDomain; + this.operationId = operationId; + this.connection_mode = connection_mode; + } + + public void sendMessageToPeer(String message) throws RemoteSessionInvalidException { + if (peerSession != null && peerSession.isOpen()) { + synchronized (writeLockObject) { + try { + peerSession.getBasicRemote().sendText(message); + } catch (IOException e) { + log.warn("Send data to session failed due to ", e); + } + } + } else { + throw new RemoteSessionInvalidException("Peer Session already closed ", new CloseReason + (CloseReason.CloseCodes.CANNOT_ACCEPT, "Peer Session already closed ")); + } + } + + public void sendMessageToPeer(byte[] message) throws RemoteSessionInvalidException { + + if (peerSession != null && peerSession.isOpen()) { + synchronized (writeLockObject) { + try { + peerSession.getBasicRemote().sendBinary(ByteBuffer.wrap(message)); + } catch (IOException e) { + log.warn("Send data to session failed due to ", e); + } + } + } else { + throw new RemoteSessionInvalidException("Peer Session already closed ", new CloseReason + (CloseReason.CloseCodes.CANNOT_ACCEPT, "Peer Session already closed ")); + } + } + + public Session getPeerSession() { + return peerSession; + } + + public void setPeerSession(Session peerSession) { + this.peerSession = peerSession; + } + + public String getTenantDomain() { + return tenantDomain; + } + + public void setTenantDomain(String tenantDomain) { + this.tenantDomain = tenantDomain; + } + + public String getOperationId() { + return operationId; + } + + public void setOperationId(String operationId) { + this.operationId = operationId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public RemoteSessionConstants.CONNECTION_MODES getConnection_mode() { + return connection_mode; + } + + public void setConnection_mode(RemoteSessionConstants.CONNECTION_MODES connection_mode) { + this.connection_mode = connection_mode; + } +} 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/RemoteSessionInvalidException.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/RemoteSessionInvalidException.java new file mode 100644 index 000000000..139cc7434 --- /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/RemoteSessionInvalidException.java @@ -0,0 +1,52 @@ +/* + * 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 interference with Remote RemoteSession. + */ +public class RemoteSessionInvalidException extends Exception { + + CloseReason closeReason; + + public RemoteSessionInvalidException(String msg, CloseReason closeReason, Exception nestedEx) { + super(msg, nestedEx); + this.closeReason = closeReason; + } + + public RemoteSessionInvalidException(String message, CloseReason closeReason, Throwable cause) { + super(message, cause); + this.closeReason = closeReason; + } + + public RemoteSessionInvalidException(String msg, CloseReason closeReason) { + super(msg); + this.closeReason = closeReason; + } + + public CloseReason getCloseReason() { + return closeReason; + } + + public void setCloseReason(CloseReason closeReason) { + this.closeReason = 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/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..628fed35b --- /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,45 @@ +/* + * 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; + + +/** + * This Exception will be thrown, when there any interference with Remote RemoteSession. + */ +public class RemoteSessionManagementException extends Exception { + + public RemoteSessionManagementException(String msg, Exception nestedEx) { + super(msg, nestedEx); + } + + public RemoteSessionManagementException(String message, Throwable cause) { + super(message, cause); + } + + public RemoteSessionManagementException(String msg) { + super(msg); + } + + public RemoteSessionManagementException() { + super(); + } + + public RemoteSessionManagementException(Throwable cause) { + super(cause); + } +} 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..825ddc5cf --- /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,101 @@ +/* + * 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.config.DeviceConfigurationManager; +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 javax.websocket.Session; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class RemoteSessionManagementDataHolder { + + private static RemoteSessionManagementDataHolder thisInstance = new RemoteSessionManagementDataHolder(); + private DeviceManagementProviderService deviceManagementProviderService; + private DeviceAccessAuthorizationService deviceAccessAuthorizationService; + private boolean isEnabled; + private String serverUrl; + private OAuthAuthenticator oAuthAuthenticator; + private Map deviceRequestMap = 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 void setSessionMap(Map sessionMap) { + this.sessionMap = sessionMap; + } + + public Map getDeviceRequestMap() { + return deviceRequestMap; + } + + public void setDeviceRequestMap(Map deviceRequestMap) { + this.deviceRequestMap = deviceRequestMap; + } + + 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; + } +} 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..1bdb3e17a --- /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,91 @@ +/* + * 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); + + @SuppressWarnings("unused") + 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..97339b3b3 --- /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,73 @@ +/* + * 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.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 reciever gets activated after the server start up to avoid + * Bundle not loading issues. + */ +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 { + + RemoteSessionManagementDataHolder.getInstance().setEnabled(DeviceConfigurationManager.getInstance() + .getDeviceManagementConfig().getRemoteSessionConfiguration().getIsEnabled()); + RemoteSessionManagementDataHolder.getInstance().setServerUrl(DeviceConfigurationManager.getInstance() + .getDeviceManagementConfig().getRemoteSessionConfiguration().getRemoteSessionServerUrl()); + Map configProperties = new HashMap<>(); + configProperties.put(RemoteSessionConstants.TOKEN_VALIDATION_ENDPOINT_URL, "https://localhost:9443"); + configProperties.put(RemoteSessionConstants.USERNAME,"admin"); + configProperties.put(RemoteSessionConstants.PASSWORD,"admin"); + configProperties.put(RemoteSessionConstants.MAXIMUM_HTTP_CONNECTION_PER_HOST,"2"); + configProperties.put(RemoteSessionConstants.MAXIMUM_TOTAL_HTTP_CONNECTION,"100"); + 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..b8e46ad41 --- /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,45 @@ +/* +* Copyright (c) 2016, 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; + +public class PropertyUtils { + + //This method is only used if the mb features are within DAS. + 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..50cc9cdbf --- /dev/null +++ b/components/extensions/remote-session-extension/pom.xml @@ -0,0 +1,59 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + extensions + 4.0.35-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 836505a2d..6b1d0cef5 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}} @@ -82,6 +84,7 @@
  • Applications
  • Policy Compliance
  • +
  • Remote Session
  • {{/zone}} {{#zone "device-view-tab-injected-conents"}} @@ -113,6 +116,86 @@
    +
    +
    + + +
    +
    + +