OS-1 : insecure UI websocket.
- notes on authentication of UI web socket connection.
- new classes: UiSessionToken, UiTokenService.
- UiExtensionManager now implements UiTokenService.
- UiWebSocket now expects an authentication event from the client
- websocket.js now sends authentication event as first event
- (fix websocket Jasmine test)
Change-Id: I4303c67f57fc618e911be244091f00bcc2823c91
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 8bb6244..6c4fc1e 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -29,6 +29,8 @@
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiSessionToken;
+import org.onosproject.ui.UiTokenService;
import org.onosproject.ui.UiTopo2OverlayFactory;
import org.onosproject.ui.UiTopoLayoutService;
import org.onosproject.ui.UiTopoOverlayFactory;
@@ -57,6 +59,9 @@
private static final String EVENT = "event";
private static final String PAYLOAD = "payload";
private static final String UNKNOWN = "unknown";
+ private static final String AUTHENTICATION = "authentication";
+ private static final String TOKEN = "token";
+ private static final String ERROR = "error";
private static final String ID = "id";
private static final String IP = "ip";
@@ -87,6 +92,9 @@
private TopoOverlayCache overlayCache;
private Topo2OverlayCache overlay2Cache;
+ private UiSessionToken sessionToken;
+
+
/**
* Creates a new web-socket for serving data to the Web UI.
*
@@ -102,8 +110,8 @@
UiTopoLayoutService layoutService = directory.get(UiTopoLayoutService.class);
sharedModel.injectJsonifier(t2json);
-
topoSession = new UiTopoSession(this, t2json, sharedModel, layoutService);
+ sessionToken = null;
}
@Override
@@ -192,6 +200,11 @@
@Override
public synchronized void onClose(int closeCode, String message) {
+ tokenService().revokeToken(sessionToken);
+ log.info("Session token revoked");
+
+ sessionToken = null;
+
topoSession.destroy();
destroyHandlersAndOverlays();
log.info("GUI client disconnected [close-code={}, message={}]",
@@ -210,13 +223,20 @@
try {
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
String type = message.path(EVENT).asText(UNKNOWN);
- UiMessageHandler handler = handlers.get(type);
- if (handler != null) {
- log.debug("RX message: {}", message);
- handler.process(message);
+
+ if (sessionToken == null) {
+ authenticate(type, message);
+
} else {
- log.warn("No GUI message handler for type {}", type);
+ UiMessageHandler handler = handlers.get(type);
+ if (handler != null) {
+ log.debug("RX message: {}", message);
+ handler.process(message);
+ } else {
+ log.warn("No GUI message handler for type {}", type);
+ }
}
+
} catch (Exception e) {
log.warn("Unable to parse GUI message {} due to {}", data, e);
log.debug("Boom!!!", e);
@@ -277,6 +297,31 @@
log.debug("#handlers = {}, #overlays = {}", handlers.size(), overlayCache.size());
}
+ private void authenticate(String type, ObjectNode message) {
+ if (!AUTHENTICATION.equals(type)) {
+ log.warn("Non-Authenticated Web Socket: {}", message);
+ return;
+ }
+
+ String tokstr = message.path(PAYLOAD).path(TOKEN).asText(UNKNOWN);
+ UiSessionToken token = new UiSessionToken(tokstr);
+
+ if (tokenService().isTokenValid(token)) {
+ sessionToken = token;
+ log.info("Session token authenticated");
+ log.debug("WebSocket authenticated: {}", message);
+ } else {
+ log.warn("Invalid Authentication Token: {}", message);
+ sendMessage(ERROR, notAuthorized(token));
+ }
+ }
+
+ private ObjectNode notAuthorized(UiSessionToken token) {
+ return mapper.createObjectNode()
+ .put("message", "invalid authentication token")
+ .put("badToken", token.toString());
+ }
+
private void registerOverlays(UiExtension ext) {
UiTopoOverlayFactory overlayFactory = ext.topoOverlayFactory();
if (overlayFactory != null) {
@@ -350,4 +395,11 @@
sendMessage(BOOTSTRAP, payload);
}
+ private UiTokenService tokenService() {
+ UiTokenService service = directory.get(UiTokenService.class);
+ if (service == null) {
+ log.error("Unable to reference UiTokenService");
+ }
+ return service;
+ }
}