org.entcore.session.AuthManager.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.session.AuthManager.java

Source

/* Copyright  "Open Digital Education", 2014
 *
 * This program is published by "Open Digital Education".
 * You must indicate the name of the software and the company in any production /contribution
 * using the software and indicate on the home page of the software industry in question,
 * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/.
 *
 * This program is free software, licensed under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, version 3 of the License.
 *
 * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License.
 * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications.
 *
 * You should have received a copy of the GNU Affero General Public License along with the software.
 * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives.
    
 *
 */

package org.entcore.session;

import fr.wseduc.mongodb.MongoDb;
import fr.wseduc.webutils.eventbus.ResultMessage;
import io.vertx.core.shareddata.LocalMap;
import org.entcore.common.neo4j.Neo4j;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.Message;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.spi.cluster.ClusterManager;
import org.entcore.common.utils.StringUtils;
import org.vertx.java.busmods.BusModBase;

import java.io.Serializable;
import java.util.*;

import static fr.wseduc.webutils.Utils.getOrElse;

public class AuthManager extends BusModBase implements Handler<Message<JsonObject>> {

    private static final long LAST_ACTIVITY_DELAY = 30000l;
    protected Map<String, String> sessions;
    protected Map<String, List<LoginInfo>> logins;
    protected Map<String, Long> inactivity;

    private static final long DEFAULT_SESSION_TIMEOUT = 30 * 60 * 1000;
    private static final String SESSIONS_COLLECTION = "sessions";

    private long sessionTimeout;
    private MongoDb mongo;
    private Neo4j neo4j;

    private static final class LoginInfo implements Serializable {
        final long timerId;
        final String sessionId;

        private LoginInfo(long timerId, String sessionId) {
            this.timerId = timerId;
            this.sessionId = sessionId;
        }
    }

    public void start() {
        super.start();
        LocalMap<Object, Object> server = vertx.sharedData().getLocalMap("server");
        String neo4jConfig = (String) server.get("neo4jConfig");
        neo4j = Neo4j.getInstance();
        neo4j.init(vertx, new JsonObject(neo4jConfig));
        Boolean cluster = (Boolean) server.get("cluster");
        String node = (String) server.get("node");
        mongo = MongoDb.getInstance();
        mongo.init(vertx.eventBus(), node + config.getString("mongo-address", "wse.mongodb.persistor"));
        if (Boolean.TRUE.equals(cluster)) {
            ClusterManager cm = ((VertxInternal) vertx).getClusterManager();
            sessions = cm.getSyncMap("sessions");
            logins = cm.getSyncMap("logins");
            if (getOrElse(config.getBoolean("inactivy"), false)) {
                inactivity = cm.getSyncMap("inactivity");
                logger.info("inactivity ha map : " + inactivity.getClass().getName());
            }
            logger.info("Initialize session cluster maps.");
        } else {
            sessions = new HashMap<>();
            logins = new HashMap<>();
            if (getOrElse(config.getBoolean("inactivy"), false)) {
                inactivity = new HashMap<>();
            }
            logger.info("Initialize session hash maps.");
        }
        final String address = getOptionalStringConfig("address", "wse.session");
        Object timeout = config.getValue("session_timeout");
        if (timeout != null) {
            if (timeout instanceof Long) {
                this.sessionTimeout = (Long) timeout;
            } else if (timeout instanceof Integer) {
                this.sessionTimeout = (Integer) timeout;
            }
        } else {
            this.sessionTimeout = DEFAULT_SESSION_TIMEOUT;
        }

        eb.localConsumer(address, this);
    }

    @Override
    public void handle(Message<JsonObject> message) {
        String action = message.body().getString("action");

        if (action == null) {
            sendError(message, "action must be specified");
            return;
        }

        switch (action) {
        case "find":
            doFind(message);
            break;
        case "findByUserId":
            doFindByUserId(message);
            break;
        case "create":
            doCreate(message);
            break;
        case "drop":
            doDrop(message);
            break;
        case "dropCacheSession":
            doDropCacheSession(message);
            break;
        case "dropPermanentSessions":
            doDropPermanentSessions(message);
            break;
        case "addAttribute":
            doAddAttribute(message);
            break;
        case "removeAttribute":
            doRemoveAttribute(message);
            break;
        default:
            sendError(message, "Invalid action: " + action);
        }
    }

    private void doDropCacheSession(Message<JsonObject> message) {
        final String userId = message.body().getString("userId");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[doDropCacheSession] Invalid userId : " + message.body().encode());
            return;
        }
        final List<LoginInfo> loginInfos = logins.get(userId);
        if (loginInfos != null) {
            final List<String> sessionIds = new ArrayList<>();
            for (LoginInfo loginInfo : loginInfos) {
                sessionIds.add(loginInfo.sessionId);
            }
            for (String sessionId : sessionIds) {
                dropSession(null, sessionId, null);
            }
        }
        sendOK(message);
    }

    private void doDropPermanentSessions(final Message<JsonObject> message) {
        String userId = message.body().getString("userId");
        String currentSessionId = message.body().getString("currentSessionId");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[doDropPermanentSessions] Invalid userId : " + message.body().encode());
            return;
        }

        JsonObject query = new JsonObject().put("userId", userId);
        if (currentSessionId != null) {
            query.put("_id", new JsonObject().put("$ne", currentSessionId));
        }
        mongo.delete(SESSIONS_COLLECTION, query, new Handler<Message<JsonObject>>() {
            @Override
            public void handle(Message<JsonObject> event) {
                if ("ok".equals(event.body().getString("status"))) {
                    sendOK(message);
                } else {
                    sendError(message, event.body().getString("message"));
                }
            }
        });
    }

    private void doFindByUserId(final Message<JsonObject> message) {
        final String userId = message.body().getString("userId");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[doFindByUserId] Invalid userId : " + message.body().encode());
            return;
        }

        LoginInfo info = getLoginInfo(userId);
        if (info == null && !getOrElse(message.body().getBoolean("allowDisconnectedUser"), false)) {
            sendError(message, "[doFindByUserId] info is null - Invalid userId : " + message.body().encode());
            return;
        } else if (info == null) {
            generateSessionInfos(userId, new Handler<JsonObject>() {

                @Override
                public void handle(JsonObject infos) {
                    if (infos != null) {
                        sendOK(message, new JsonObject().put("status", "ok").put("session", infos));
                    } else {
                        sendError(message, "Invalid userId : " + userId);
                    }
                }
            });
            return;
        }
        JsonObject session = null;
        try {
            session = unmarshal(sessions.get(info.sessionId));
        } catch (Exception e) {
            logger.error("Error in deserializing hazelcast session " + info.sessionId, e);
        }
        if (session == null) {
            sendError(message, "Session not found. 6");
            return;
        }
        sendOK(message, new JsonObject().put("status", "ok").put("session", session));
    }

    private LoginInfo getLoginInfo(String userId) {
        List<LoginInfo> loginInfos = logins.get(userId);
        if (loginInfos != null && !loginInfos.isEmpty()) {
            return loginInfos.get(loginInfos.size() - 1);
        }
        return null;
    }

    private JsonObject unmarshal(String s) {
        if (s != null) {
            return new JsonObject(s);
        }
        return null;
    }

    private void doFind(final Message<JsonObject> message) {
        final String sessionId = message.body().getString("sessionId");
        if (sessionId == null || sessionId.trim().isEmpty()) {
            sendError(message, "Invalid sessionId.");
            return;
        }

        JsonObject session = null;
        try {
            session = unmarshal(sessions.get(sessionId));
        } catch (Exception e) {
            logger.warn("Error in deserializing hazelcast session " + sessionId);
            try {
                //            if (sessions instanceof BaseMap) {
                //               ((BaseMap) sessions).delete(sessionId);
                //            } else {
                sessions.remove(sessionId);
                //            }
            } catch (Exception e1) {
                logger.warn("Error getting object after removing hazelcast session " + sessionId);
            }
        }

        if (session == null) {
            final JsonObject query = new JsonObject().put("_id", sessionId);
            mongo.findOne(SESSIONS_COLLECTION, query, new Handler<Message<JsonObject>>() {
                @Override
                public void handle(Message<JsonObject> event) {
                    JsonObject res = event.body().getJsonObject("result");
                    String userId;
                    if ("ok".equals(event.body().getString("status")) && res != null
                            && (userId = res.getString("userId")) != null && !userId.trim().isEmpty()) {
                        final String uId = userId;
                        createSession(userId, sessionId, res.getString("SessionIndex"), res.getString("NameID"),
                                new Handler<String>() {
                                    @Override
                                    public void handle(String sId) {
                                        if (sId != null) {
                                            try {
                                                JsonObject s = unmarshal(sessions.get(sId));
                                                if (s != null) {
                                                    JsonObject sessionResponse = new JsonObject()
                                                            .put("status", "ok").put("session", s);
                                                    sendOK(message, sessionResponse);
                                                } else {
                                                    sendError(message, "Session not found. 1");
                                                }
                                            } catch (Exception e) {
                                                logger.warn("Error in deserializing new hazelcast session " + sId);
                                                generateSessionInfos(uId, new Handler<JsonObject>() {

                                                    @Override
                                                    public void handle(JsonObject event) {
                                                        if (event != null) {
                                                            logger.info("Session with hazelcast problem : "
                                                                    + event.encode());
                                                            sendOK(message, new JsonObject().put("status", "ok")
                                                                    .put("session", event));
                                                        } else {
                                                            sendError(message, "Session not found. 2");
                                                        }
                                                    }
                                                });
                                            }
                                        } else {
                                            sendError(message, "Session not found. 3");
                                        }
                                    }
                                });
                    } else {
                        sendError(message, "Session not found. 4");
                    }
                }
            });
        } else {
            sendOK(message, new JsonObject().put("status", "ok").put("session", session));
            if (inactivity != null) {
                Long lastActivity = inactivity.get(sessionId);
                String userId = sessions.get(sessionId);
                if (userId != null && (lastActivity == null
                        || (lastActivity + LAST_ACTIVITY_DELAY) < System.currentTimeMillis())) {
                    inactivity.put(sessionId, System.currentTimeMillis());
                }
            }
        }
    }

    private void doCreate(final Message<JsonObject> message) {
        final String userId = message.body().getString("userId");
        final String sessionIndex = message.body().getString("SessionIndex");
        final String nameID = message.body().getString("NameID");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[doCreate] Invalid userId : " + message.body().encode());
            return;
        }

        createSession(userId, null, sessionIndex, nameID, new Handler<String>() {
            @Override
            public void handle(String sessionId) {
                if (sessionId != null) {
                    sendOK(message, new JsonObject().put("status", "ok").put("sessionId", sessionId));
                } else {
                    sendError(message, "Invalid userId : " + userId);
                }
            }
        });
    }

    private void createSession(final String userId, final String sId, final String sessionIndex,
            final String nameId, final Handler<String> handler) {
        final String sessionId = (sId != null) ? sId : UUID.randomUUID().toString();
        generateSessionInfos(userId, new Handler<JsonObject>() {

            @Override
            public void handle(JsonObject infos) {
                if (infos != null) {
                    long timerId = setTimer(userId, sessionId);

                    try {
                        sessions.put(sessionId, infos.encode());
                        addLoginInfo(userId, timerId, sessionId);
                    } catch (Exception e) {
                        logger.error("Error putting session in hazelcast map");
                        //                  try {
                        //                     if (sessions instanceof IMap) {
                        //                        ((IMap) sessions).putAsync(sessionId, infos.encode());
                        //                     }
                        //                     addLoginInfo(userId, timerId, sessionId);
                        //                  } catch (Exception e1) {
                        //                     logger.error("Error putting async session in hazelcast map", e1);
                        //                  }
                    }
                    final JsonObject now = MongoDb.now();
                    if (sId == null) {
                        JsonObject json = new JsonObject().put("_id", sessionId).put("userId", userId)
                                .put("created", now).put("lastUsed", now);
                        if (sessionIndex != null && nameId != null) {
                            json.put("SessionIndex", sessionIndex).put("NameID", nameId);
                        }
                        mongo.save(SESSIONS_COLLECTION, json);
                    } else {
                        mongo.update(SESSIONS_COLLECTION, new JsonObject().put("_id", sessionId),
                                new JsonObject().put("$set", new JsonObject().put("lastUsed", now)));
                    }
                    handler.handle(sessionId);
                } else {
                    handler.handle(null);
                }
            }
        });
    }

    protected long setTimer(final String userId, final String sessionId) {
        if (inactivity != null) {
            inactivity.put(sessionId, System.currentTimeMillis());
        }
        return setTimer(userId, sessionId, sessionTimeout);
    }

    protected long setTimer(final String userId, final String sessionId, final long sessionTimeout) {
        return vertx.setTimer(sessionTimeout, new Handler<Long>() {
            public void handle(Long timerId) {
                if (inactivity != null) {
                    final Long lastActivity = inactivity.get(sessionId);
                    if (lastActivity != null) {
                        final long timeoutTimestamp = lastActivity + AuthManager.this.sessionTimeout;
                        final long now = System.currentTimeMillis();
                        if (timeoutTimestamp > now) {
                            setTimer(userId, sessionId, (timeoutTimestamp - now));
                        } else {
                            dropSession(new ResultMessage(), sessionId, null);
                        }
                    } else {
                        dropSession(new ResultMessage(), sessionId, null);
                    }
                } else {
                    logins.remove(userId);
                    sessions.remove(sessionId);
                }
            }
        });
    }

    private void addLoginInfo(String userId, long timerId, String sessionId) {
        List<LoginInfo> loginInfos = logins.get(userId);
        if (loginInfos == null) {
            loginInfos = new ArrayList<>();
        }
        loginInfos.add(new LoginInfo(timerId, sessionId));
        logins.put(userId, loginInfos);
    }

    private void doDrop(final Message<JsonObject> message) {
        final String sessionId = message.body().getString("sessionId");
        boolean sessionMeta = getOrElse(message.body().getBoolean("sessionMetadata"), false);
        if (sessionId == null || sessionId.trim().isEmpty()) {
            sendError(message, "Invalid sessionId.");
            return;
        }

        if (sessionMeta) {
            final JsonObject query = new JsonObject().put("_id", sessionId);
            mongo.findOne(SESSIONS_COLLECTION, query, new Handler<Message<JsonObject>>() {
                @Override
                public void handle(Message<JsonObject> event) {
                    JsonObject res = event.body().getJsonObject("result");
                    dropSession(message, sessionId, res);
                }
            });
        } else {
            dropSession(message, sessionId, null);
        }
    }

    private void dropSession(Message<JsonObject> message, String sessionId, JsonObject meta) {
        mongo.delete(SESSIONS_COLLECTION, new JsonObject().put("_id", sessionId));
        JsonObject session = null;
        try {
            session = unmarshal(sessions.get(sessionId));
        } catch (Exception e) {
            try {
                //            if (sessions instanceof BaseMap) {
                //               ((BaseMap) sessions).delete(sessionId);
                //            } else {
                sessions.remove(sessionId);
                //}
            } catch (Exception e1) {
                logger.error("In doDrop - Error getting object after removing hazelcast session " + sessionId, e);
            }
        }
        if (session != null) {
            JsonObject s = unmarshal(sessions.remove(sessionId));
            if (s != null) {
                final String userId = s.getString("userId");
                LoginInfo info = removeLoginInfo(sessionId, userId);
                if (getOrElse(config.getBoolean("slo"), false)) {
                    eb.send("cas", new JsonObject().put("action", "logout").put("userId", userId));
                }
                if (info != null) {
                    vertx.cancelTimer(info.timerId);
                }
            }
        }
        if (inactivity != null) {
            inactivity.remove(sessionId);
        }
        JsonObject res = new JsonObject().put("status", "ok");
        if (meta != null) {
            res.put("sessionMetadata", meta);
        }
        if (message != null) {
            sendOK(message, res);
        }
    }

    private LoginInfo removeLoginInfo(String sessionId, String userId) {
        List<LoginInfo> loginInfos = logins.get(userId);
        LoginInfo loginInfo = null;
        if (loginInfos != null && sessionId != null) {
            boolean found = false;
            int idx = 0;
            for (LoginInfo i : loginInfos) {
                if (sessionId.equals(i.sessionId)) {
                    found = true;
                    break;
                }
                idx++;
            }
            if (found) {
                loginInfo = loginInfos.remove(idx);
                if (loginInfos.isEmpty()) {
                    logins.remove(userId);
                } else {
                    logins.put(userId, loginInfos);
                }
            }
        }
        return loginInfo;
    }

    private void doAddAttribute(Message<JsonObject> message) {
        JsonObject session = getSessionByUserId(message);
        if (session == null) {
            return;
        }

        String key = message.body().getString("key");
        if (key == null || key.trim().isEmpty()) {
            sendError(message, "Invalid key.");
            return;
        }

        Object value = message.body().getValue("value");
        if (value == null) {
            sendError(message, "Invalid value.");
            return;
        }

        session.getJsonObject("cache").put(key, value);

        updateSessionByUserId(message, session);
        sendOK(message);
    }

    private JsonObject getSessionByUserId(Message<JsonObject> message) {
        final String userId = message.body().getString("userId");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[getSessionByUserId] Invalid userId : " + message.body().encode());
            return null;
        }

        LoginInfo info = getLoginInfo(userId);
        if (info == null) { // disconnected user : ignore action
            sendOK(message);
            return null;
        }
        JsonObject session = null;
        try {
            session = unmarshal(sessions.get(info.sessionId));
        } catch (Exception e) {
            logger.error("Error in deserializing hazelcast session " + info.sessionId, e);
        }
        if (session == null) {
            sendError(message, "Session not found. 7");
            return null;
        }
        return session;
    }

    private void updateSessionByUserId(Message<JsonObject> message, JsonObject session) {
        final String userId = message.body().getString("userId");
        if (userId == null || userId.trim().isEmpty()) {
            sendError(message, "[updateSessionByUserId] Invalid userId : " + message.body().encode());
            return;
        }

        List<LoginInfo> infos = logins.get(userId);
        if (infos == null || infos.isEmpty()) {
            sendError(message,
                    "[updateSessionByUserId] info is null - Invalid userId : " + message.body().encode());
            return;
        }
        for (LoginInfo info : infos) {
            try {
                sessions.put(info.sessionId, session.encode());
            } catch (Exception e) {
                logger.error("Error putting session in hazelcast map : " + info.sessionId, e);
            }
        }
    }

    private void doRemoveAttribute(Message<JsonObject> message) {
        JsonObject session = getSessionByUserId(message);
        if (session == null) {
            return;
        }

        String key = message.body().getString("key");
        if (key == null || key.trim().isEmpty()) {
            sendError(message, "Invalid key.");
            return;
        }

        session.getJsonObject("cache").remove(key);
        updateSessionByUserId(message, session);
        sendOK(message);
    }

    private void generateSessionInfos(final String userId, final Handler<JsonObject> handler) {
        final String query = "MATCH (n:User {id : {id}}) " + "WHERE HAS(n.login) "
                + "OPTIONAL MATCH n-[:IN]->(gp:Group) " + "OPTIONAL MATCH gp-[:DEPENDS]->(s:Structure) "
                + "OPTIONAL MATCH gp-[:DEPENDS]->(c:Class) "
                + "OPTIONAL MATCH n-[rf:HAS_FUNCTION]->fg-[:CONTAINS_FUNCTION*0..1]->(f:Function) "
                + "OPTIONAL MATCH n<-[:RELATED]-(child:User) " + "RETURN distinct "
                + "n.classes as classNames, n.level as level, n.login as login, COLLECT(distinct [c.id, c.name]) as classes, "
                + "n.lastName as lastName, n.firstName as firstName, n.externalId as externalId, n.federated as federated, "
                + "n.birthDate as birthDate, "
                + "n.displayName as username, HEAD(n.profiles) as type, COLLECT(distinct [child.id, child.lastName, child.firstName]) as childrenInfo, "
                + "COLLECT(distinct [s.id, s.name]) as structures, COLLECT(distinct [f.externalId, rf.scope]) as functions, "
                + "COLLECT(distinct s.UAI) as uai, "
                + "COLLECT(distinct gp.id) as groupsIds, n.federatedIDP as federatedIDP, n.functions as aafFunctions";
        final String query2 = "MATCH (n:User {id : {id}})-[:IN]->()-[:AUTHORIZED]->(:Role)-[:AUTHORIZE]->(a:Action)"
                + "<-[:PROVIDE]-(app:Application) " + "WHERE HAS(n.login) "
                + "RETURN DISTINCT COLLECT(distinct [a.name,a.displayName,a.type]) as authorizedActions, "
                + "COLLECT(distinct [app.name,app.address,app.icon,app.target,app.displayName,app.display,app.prefix]) as apps";
        final String query3 = "MATCH (u:User {id: {id}})-[:IN]->(g:Group)-[auth:AUTHORIZED]->(w:Widget) "
                + "WHERE HAS(u.login) "
                + "AND ( NOT(w<-[:HAS_WIDGET]-(:Application)-[:PROVIDE]->(:WorkflowAction)) "
                + "XOR w<-[:HAS_WIDGET]-(:Application)-[:PROVIDE]->(:WorkflowAction)<-[:AUTHORIZE]-(:Role)<-[:AUTHORIZED]-g )  "
                + "OPTIONAL MATCH (w)<-[:HAS_WIDGET]-(app:Application) "
                + "WITH w, app, collect(auth) as authorizations " + "RETURN DISTINCT COLLECT({"
                + "id: w.id, name: w.name, " + "path: coalesce(app.address, '') + w.path, "
                + "js: coalesce(app.address, '') + w.js, " + "i18n: coalesce(app.address, '') + w.i18n, "
                + "application: app.name, "
                + "mandatory: ANY(a IN authorizations WHERE HAS(a.mandatory) AND a.mandatory = true)"
                + "}) as widgets";
        final String query4 = "MATCH (s:Structure) return s.id as id, s.externalId as externalId";
        final String query5 = "MATCH (u:User {id: {id}})-[:PREFERS]->(uac:UserAppConf) RETURN uac AS preferences";
        JsonObject params = new JsonObject();
        params.put("id", userId);
        JsonArray statements = new fr.wseduc.webutils.collections.JsonArray()
                .add(new JsonObject().put("statement", query).put("parameters", params))
                .add(new JsonObject().put("statement", query2).put("parameters", params))
                .add(new JsonObject().put("statement", query3).put("parameters", params))
                .add(new JsonObject().put("statement", query4))
                .add(new JsonObject().put("statement", query5).put("parameters", params));
        neo4j.executeTransaction(statements, null, true, new Handler<Message<JsonObject>>() {

            @Override
            public void handle(Message<JsonObject> message) {
                JsonArray results = message.body().getJsonArray("results");
                if ("ok".equals(message.body().getString("status")) && results != null && results.size() == 5
                        && results.getJsonArray(0).size() > 0 && results.getJsonArray(1).size() > 0) {
                    JsonObject j = results.getJsonArray(0).getJsonObject(0);
                    JsonObject j2 = results.getJsonArray(1).getJsonObject(0);
                    JsonObject j3 = results.getJsonArray(2).getJsonObject(0);
                    JsonObject structureMapping = new JsonObject();
                    for (Object o : results.getJsonArray(3)) {
                        if (!(o instanceof JsonObject))
                            continue;
                        JsonObject jsonObject = (JsonObject) o;
                        structureMapping.put(jsonObject.getString("externalId"), jsonObject.getString("id"));
                    }
                    j.put("userId", userId);
                    JsonObject functions = new JsonObject();
                    JsonArray actions = new fr.wseduc.webutils.collections.JsonArray();
                    JsonArray apps = new fr.wseduc.webutils.collections.JsonArray();
                    for (Object o : getOrElse(j2.getJsonArray("authorizedActions"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        JsonArray a = (JsonArray) o;
                        actions.add(new JsonObject().put("name", a.getString(0)).put("displayName", a.getString(1))
                                .put("type", a.getString(2)));
                    }
                    for (Object o : getOrElse(j2.getJsonArray("apps"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        JsonArray a = (JsonArray) o;
                        apps.add(new JsonObject().put("name", (String) a.getString(0))
                                .put("address", (String) a.getString(1)).put("icon", (String) a.getString(2))
                                .put("target", (String) a.getString(3)).put("displayName", (String) a.getString(4))
                                .put("display", ((a.getValue(5) == null) || a.getBoolean(5)))
                                .put("prefix", (String) a.getString(6)));
                    }
                    for (Object o : getOrElse(j.getJsonArray("aafFunctions"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (o == null)
                            continue;
                        String[] sf = o.toString().split("\\$");
                        if (sf.length == 5) {
                            JsonObject jo = functions.getJsonObject(sf[1]);
                            if (jo == null) {
                                jo = new JsonObject().put("code", sf[1]).put("functionName", sf[2])
                                        .put("scope", new fr.wseduc.webutils.collections.JsonArray())
                                        .put("structureExternalIds", new fr.wseduc.webutils.collections.JsonArray())
                                        .put("subjects", new JsonObject());
                                functions.put(sf[1], jo);
                            }
                            JsonObject subject = jo.getJsonObject("subjects").getJsonObject(sf[3]);
                            if (subject == null) {
                                subject = new JsonObject().put("subjectCode", sf[3]).put("subjectName", sf[4])
                                        .put("scope", new fr.wseduc.webutils.collections.JsonArray())
                                        .put("structureExternalIds",
                                                new fr.wseduc.webutils.collections.JsonArray());
                                jo.getJsonObject("subjects").put(sf[3], subject);
                            }
                            jo.getJsonArray("structureExternalIds").add(sf[0]);
                            subject.getJsonArray("structureExternalIds").add(sf[0]);
                            String sid = structureMapping.getString(sf[0]);
                            if (sid != null) {
                                jo.getJsonArray("scope").add(sid);
                                subject.getJsonArray("scope").add(sid);
                            }
                        }
                    }
                    j.remove("aafFunctions");
                    for (Object o : getOrElse(j.getJsonArray("functions"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        JsonArray a = (JsonArray) o;
                        String code = a.getString(0);
                        if (code != null) {
                            functions.put(code, new JsonObject().put("code", code).put("scope", a.getJsonArray(1)));
                        }
                    }
                    final JsonObject children = new JsonObject();
                    final List<String> childrenIds = new ArrayList<String>();
                    for (Object o : getOrElse(j.getJsonArray("childrenInfo"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        final JsonArray a = (JsonArray) o;
                        final String childId = a.getString(0);
                        if (childId != null) {
                            childrenIds.add(childId);
                            JsonObject jo = children.getJsonObject(childId);
                            if (jo == null) {
                                jo = new JsonObject().put("lastName", a.getString(1)).put("firstName",
                                        a.getString(2));
                                children.put(childId, jo);
                            }
                        }
                    }
                    j.remove("childrenInfo");
                    final List<String> classesIds = new ArrayList<String>();
                    final List<String> classesNames = new ArrayList<String>();
                    for (Object o : getOrElse(j.getJsonArray("classes"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        final JsonArray c = (JsonArray) o;
                        if (c.getString(0) != null) {
                            classesIds.add(c.getString(0));
                            classesNames.add(c.getString(1));
                        }
                    }
                    j.remove("classes");
                    final List<String> structureIds = new ArrayList<String>();
                    final List<String> structureNames = new ArrayList<String>();
                    for (Object o : getOrElse(j.getJsonArray("structures"),
                            new fr.wseduc.webutils.collections.JsonArray())) {
                        if (!(o instanceof JsonArray))
                            continue;
                        final JsonArray s = (JsonArray) o;
                        if (s.getString(0) != null) {
                            structureIds.add(s.getString(0));
                            structureNames.add(StringUtils.trimToBlank(s.getString(1)));
                        }
                    }
                    j.remove("structures");
                    j.put("structures", new fr.wseduc.webutils.collections.JsonArray(structureIds));
                    j.put("structureNames", new fr.wseduc.webutils.collections.JsonArray(structureNames));
                    j.put("classes", new fr.wseduc.webutils.collections.JsonArray(classesIds));
                    j.put("realClassesNames", new fr.wseduc.webutils.collections.JsonArray(classesNames));
                    j.put("functions", functions);
                    j.put("authorizedActions", actions);
                    j.put("apps", apps);
                    j.put("childrenIds", new fr.wseduc.webutils.collections.JsonArray(childrenIds));
                    j.put("children", children);
                    final JsonObject cache = (results.getJsonArray(4) != null && results.getJsonArray(4).size() > 0
                            && results.getJsonArray(4).getJsonObject(0) != null)
                                    ? results.getJsonArray(4).getJsonObject(0)
                                    : new JsonObject();
                    j.put("cache", cache);
                    j.put("widgets",
                            getOrElse(j3.getJsonArray("widgets"), new fr.wseduc.webutils.collections.JsonArray()));
                    handler.handle(j);
                } else {
                    handler.handle(null);
                }
            }
        });
    }

}