/*******************************************************************************
  * Copyright (c) 2017 DocDoku.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  *    DocDoku - initial API and implementation
  *******************************************************************************/

package org.polarsys.eplmp.server.ws;

import org.polarsys.eplmp.core.security.UserGroupMapping;
import org.polarsys.eplmp.core.services.IUserManagerLocal;
import org.polarsys.eplmp.server.auth.AuthConfig;
import org.polarsys.eplmp.server.auth.jwt.JWTokenFactory;
import org.polarsys.eplmp.server.auth.jwt.JWTokenUserGroupMapping;

import javax.ejb.Stateless;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * This class expose a web socket end point on path {contextPath}/ws
 * <p>
 * It maintains the list of current users sockets, receive, process and broadcast messages.
 * Authentication is made by passing a jwt token on first message.
 * <p>
 */
@Stateless
@ServerEndpoint(
        value = "/ws",
        decoders = {
                WebSocketMessageDecoder.class
        },
        encoders = {
                WebSocketMessageEncoder.class
        }
)
public class WebSocketApplication {

    private static final Logger LOGGER = Logger.getLogger(WebSocketApplication.class.getName());

    @Inject
    @Any
    private Instance<WebSocketModule> webSocketModules;

    @Inject
    private IUserManagerLocal userManager;

    @Inject
    private WebSocketSessionsManager webSocketSessionsManager;

    @Inject
    private AuthConfig authConfig;

    private List<Session> unAuthenticatedSessions = new ArrayList<>();

    public WebSocketApplication() {
    }

    private static final String AUTH = "AUTH";

    @OnError
    public void error(Session session, Throwable error) {
        LOGGER.log(Level.SEVERE, "WebSocket error", error);
        unTrackSession(session);
    }

    @OnClose
    public void close(Session session, CloseReason reason) {
        LOGGER.log(Level.FINE, "WebSocket closed with message '" +
                reason.getReasonPhrase() + "' and code " + reason.getCloseCode());
        unTrackSession(session);
    }

    @OnOpen
    public void open(Session session) {
        unAuthenticatedSessions.add(session);
    }

    @OnMessage
    public void message(Session session, WebSocketMessage message) {
        if (unAuthenticatedSessions.contains(session)) {
            authenticateOrClose(session, message);
            return;
        }

        // Modules are responsible for :
        // Exit if caller is not allowed to reach callee (business)
        // Prevent caller to join himself

        WebSocketModule selectedModule = selectModule(message);

        if (null != selectedModule) {
            selectedModule.process(session, message);
        } else {
            LOGGER.log(Level.WARNING, "No modules for type " + message.getType());
        }


    }

    private void authenticateOrClose(Session session, WebSocketMessage message) {

        String type = message.getType();

        if (AUTH.equals(type)) {
            String jwt = message.getString("jwt");

            JWTokenUserGroupMapping jwTokenUserGroupMapping = JWTokenFactory.validateAuthToken(authConfig.getJWTKey(), jwt);

            if (null != jwTokenUserGroupMapping) {
                UserGroupMapping userGroupMapping = jwTokenUserGroupMapping.getUserGroupMapping();
                String login = userGroupMapping.getLogin();
                if (login != null) {
                    unAuthenticatedSessions.remove(session);
                    webSocketSessionsManager.addSession(login, session);
                    return;
                }
            }
        }

        // Authentication failed, close socket
        closeSession(session);
        unTrackSession(session);

    }

    private WebSocketModule selectModule(WebSocketMessage webSocketMessage) {
        for (WebSocketModule webSocketModule : webSocketModules) {
            if (webSocketModule.canDecode(webSocketMessage)) {
                return webSocketModule;
            }
        }
        return null;
    }

    private void closeSession(Session session) {
        try {
            session.close();
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, null, e);
        }
    }

    private void unTrackSession(Session session) {
        if (unAuthenticatedSessions.contains(session)) {
            unAuthenticatedSessions.remove(session);
        } else {
            webSocketSessionsManager.removeSession(session);
        }
    }


}

@@ -846,36 +846,8 @@
 g;%0A%0A
-import javax.ejb.Stateless;%0A
 impo
@@ -1051,59 +1051,8 @@
 on;%0A
-import java.util.ArrayList;%0Aimport java.util.List;%0A
 impo
@@ -1368,19 +1368,8 @@
  */%0A
-@Stateless%0A
 @Ser
@@ -1692,24 +1692,72 @@
 etName());%0A%0A
+    private static final String AUTH = %22AUTH%22;%0A%0A
     @Inject%0A
@@ -2011,164 +2011,44 @@
    p
-rivate List%3CSession%3E unAuthenticatedSessions = new ArrayList%3C%3E();%0A%0A    public WebSocketApplication() %7B%0A    %7D%0A%0A    private static final String AUTH = %22AUTH%22;
+ublic WebSocketApplication() %7B%0A    %7D
 %0A%0A  
@@ -2544,43 +2544,50 @@
     
-unAuthenticatedSessions.add(session
+session.getUserProperties().put(AUTH, null
 );%0A 
@@ -2692,48 +2692,52 @@
 if (
-unAuthenticatedSessions.contains(session
+null == session.getUserProperties().get(AUTH
 )) %7B
@@ -3239,17 +3239,16 @@
      %7D%0A%0A
-%0A
     %7D%0A%0A 
@@ -3853,46 +3853,49 @@
     
-unAuthenticatedSessions.remove(session
+session.getUserProperties().put(AUTH, jwt
 );%0A 
@@ -4713,120 +4713,54 @@
 if (
-unAuthenticatedSessions.contains(session)) %7B%0A            unAuthenticatedSessions.remove(session);%0A        %7D else
+null != session.getUserProperties().get(AUTH))
  %7B%0A 
@@ -4836,11 +4836,10 @@
 %0A    %7D%0A%0A
-%0A
 %7D%0A

Use of Session.getUserProperties + close docdoku/docdoku-plm/issues/1130