org.entcore.directory.controllers.DirectoryController.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.directory.controllers.DirectoryController.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.directory.controllers;

import fr.wseduc.bus.BusAddress;
import fr.wseduc.rs.Get;
import fr.wseduc.rs.Post;
import fr.wseduc.security.ActionType;
import fr.wseduc.security.SecuredAction;
import fr.wseduc.webutils.Either;
import fr.wseduc.webutils.http.BaseController;
import fr.wseduc.webutils.security.BCrypt;
import io.vertx.core.eventbus.DeliveryOptions;
import org.entcore.common.appregistry.ApplicationUtils;
import org.entcore.common.bus.BusResponseHandler;
import org.entcore.common.http.filter.AdminFilter;
import org.entcore.common.http.filter.IgnoreCsrf;
import org.entcore.common.http.filter.ResourceFilter;
import org.entcore.common.neo4j.Neo;
import org.entcore.directory.security.AdmlOfStructuresByExternalId;
import org.entcore.directory.services.ClassService;
import org.entcore.directory.services.GroupService;
import org.entcore.directory.services.SchoolService;
import org.entcore.directory.services.UserService;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.vertx.java.core.http.RouteMatcher;

import java.util.*;

import static fr.wseduc.webutils.Utils.getOrElse;
import static fr.wseduc.webutils.Utils.handlerToAsyncHandler;
import static fr.wseduc.webutils.request.RequestUtils.bodyToJson;
import static org.entcore.common.bus.BusResponseHandler.busArrayHandler;
import static org.entcore.common.http.response.DefaultResponseHandler.*;

public class DirectoryController extends BaseController {

    private Neo neo;
    private JsonObject config;
    private JsonObject admin;
    private SchoolService schoolService;
    private ClassService classService;
    private UserService userService;
    private GroupService groupService;

    public void init(Vertx vertx, JsonObject config, RouteMatcher rm,
            Map<String, fr.wseduc.webutils.security.SecuredAction> securedActions) {
        super.init(vertx, config, rm, securedActions);
        this.neo = new Neo(vertx, eb, log);
        this.config = config;
        this.admin = new JsonObject(vertx.fileSystem().readFileBlocking("super-admin.json").toString());
    }

    @Get("/admin-console")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(AdminFilter.class)
    public void adminConsole(HttpServerRequest request) {
        renderView(request, new JsonObject());
    }

    @Get("/class-admin")
    @SecuredAction(value = "userbook.authent", type = ActionType.AUTHENTICATED)
    public void classAdmin(HttpServerRequest request) {
        renderView(request);
    }

    @Post("/import")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(AdmlOfStructuresByExternalId.class)
    @IgnoreCsrf
    public void launchImport(final HttpServerRequest request) {
        final JsonObject json = new JsonObject().put("action", "import")
                .put("feeder", request.params().get("feeder")).put("profile", request.params().get("profile"))
                .put("charset", request.params().get("charset"))
                .put("structureExternalId", request.params().get("structureExternalId"));

        eb.send("entcore.feeder", json);
        request.response().end();
    }

    @Post("/transition")
    @SecuredAction("directory.transition")
    @IgnoreCsrf
    public void launchTransition(final HttpServerRequest request) {
        JsonObject t = new JsonObject().put("action", "transition");
        String structureId = request.params().get("structureExternalId");
        if (structureId != null) {
            t.put("structureExternalId", structureId);
        }
        eb.send("entcore.feeder", t,
                new DeliveryOptions().setSendTimeout(getOrElse(config.getLong("transitionTimeout"), 300000l)),
                handlerToAsyncHandler(new Handler<Message<JsonObject>>() {

                    @Override
                    public void handle(Message<JsonObject> event) {
                        renderJson(request, event.body());
                    }
                }));
    }

    @Post("/duplicates/mark")
    @SecuredAction("directory.duplicates.mark")
    @IgnoreCsrf
    public void markDuplicates(final HttpServerRequest request) {
        eb.send("entcore.feeder", new JsonObject().put("action", "mark-duplicates"),
                new DeliveryOptions().setSendTimeout(getOrElse(config.getLong("markDuplicatesTimeout"), 300000l)),
                handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                    @Override
                    public void handle(Message<JsonObject> event) {
                        renderJson(request, event.body());
                    }
                }));
    }

    @Post("/export")
    @SecuredAction("directory.export")
    @IgnoreCsrf
    public void launchExport(HttpServerRequest request) {
        eb.send("entcore.feeder", new JsonObject().put("action", "export"));
        request.response().end();
    }

    @Post("/reinitLogins")
    @SecuredAction("directory.reinit.login")
    @IgnoreCsrf
    public void reinitLogins(HttpServerRequest request) {
        eb.send("entcore.feeder", new JsonObject().put("action", "reinit-logins"));
        request.response().end();
    }

    @Get("/annuaire")
    @SecuredAction(value = "directory.search.view", type = ActionType.AUTHENTICATED)
    public void annuaire(HttpServerRequest request) {
        renderView(request, null, "annuaire.html", null);
    }

    @Get("/schools")
    @SecuredAction(value = "directory.schools", type = ActionType.AUTHENTICATED)
    public void schools(HttpServerRequest request) {
        renderView(request);
    }

    @Get("/api/ecole")
    @SecuredAction("directory.authent")
    public void school(HttpServerRequest request) {
        neo.send("MATCH (n:Structure) RETURN distinct n.name as name, n.id as id", request.response());
    }

    @Post("/school")
    @SecuredAction("directory.school.create")
    @IgnoreCsrf
    public void createSchool(final HttpServerRequest request) {
        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject school) {
                schoolService.create(school, new Handler<Either<String, JsonObject>>() {
                    @Override
                    public void handle(final Either<String, JsonObject> r) {
                        if (r.isRight()) {
                            if (r.right().getValue() != null && r.right().getValue().size() > 0) {
                                JsonObject j = new JsonObject().put("action", "initDefaultCommunicationRules")
                                        .put("schoolIds", new fr.wseduc.webutils.collections.JsonArray()
                                                .add(r.right().getValue().getString("id")));
                                eb.send("wse.communication", j,
                                        handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                                            @Override
                                            public void handle(Message<JsonObject> message) {
                                                renderJson(request, r.right().getValue(), 201);
                                            }
                                        }));
                            } else {
                                request.response().setStatusCode(404).end();
                            }
                        } else {
                            leftToResponse(request, r.left());
                        }
                    }
                });
            }
        });
    }

    @Get("/school/:id")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    public void getSchool(final HttpServerRequest request) {
        String schoolId = request.params().get("id");
        schoolService.get(schoolId, notEmptyResponseHandler(request));
    }

    @Post("/class/:schoolId")
    @SecuredAction("directory.class.create")
    @IgnoreCsrf
    public void createClass(final HttpServerRequest request) {
        final String schoolId = request.params().get("schoolId");
        if (schoolId == null || schoolId.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject c) {
                classService.create(schoolId, c, new Handler<Either<String, JsonObject>>() {
                    @Override
                    public void handle(final Either<String, JsonObject> event) {
                        if (event.isRight()) {
                            if (event.right().getValue() != null && event.right().getValue().size() > 0) {
                                JsonObject j = new JsonObject().put("action", "initDefaultCommunicationRules").put(
                                        "schoolIds", new fr.wseduc.webutils.collections.JsonArray().add(schoolId));
                                eb.send("wse.communication", j);
                                String classId = event.right().getValue().getString("id");
                                if (classId != null && !classId.trim().isEmpty()
                                        && request.params().contains("setDefaultRoles")
                                        && config.getBoolean("classDefaultRoles", false)) {
                                    ApplicationUtils.setDefaultClassRoles(eb, classId,
                                            handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                                                @Override
                                                public void handle(Message<JsonObject> message) {
                                                    renderJson(request, event.right().getValue(), 201);
                                                }
                                            }));
                                } else {
                                    renderJson(request, event.right().getValue(), 201);
                                }
                            } else {
                                request.response().setStatusCode(404).end();
                            }
                        } else {
                            JsonObject error = new JsonObject().put("error", event.left().getValue());
                            renderJson(request, error, 400);
                        }
                    }
                });
            }
        });
    }

    @Get("/api/classes")
    @SecuredAction("directory.classes")
    public void classes(HttpServerRequest request) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", request.params().get("id"));
        neo.send(
                "MATCH (n:Structure)<-[:BELONGS]-(m:Class) " + "WHERE n.id = {id} "
                        + "RETURN distinct m.name as name, m.id as classId, n.id as schoolId",
                params, request.response());
    }

    @Get("/api/personnes")
    @SecuredAction("directory.authent")
    public void people(HttpServerRequest request) {
        List<String> expectedTypes = request.params().getAll("type");
        JsonObject params = new JsonObject();
        params.put("classId", request.params().get("id"));
        String types = "";
        if (expectedTypes != null && !expectedTypes.isEmpty()) {
            types = "AND p.name IN {expectedTypes} ";
            params.put("expectedTypes", new fr.wseduc.webutils.collections.JsonArray(expectedTypes));
        }
        neo.send(
                "MATCH (n:Class)<-[:DEPENDS]-(g:ProfileGroup)<-[:IN]-(m:User), "
                        + "g-[:DEPENDS]->(pg:ProfileGroup)-[:HAS_PROFILE]->(p:Profile) " + "WHERE n.id = {classId} "
                        + types + "RETURN distinct m.id as userId, p.name as type, "
                        + "m.activationCode as code, m.firstName as firstName,"
                        + "m.lastName as lastName, n.id as classId " + "ORDER BY type DESC ",
                params.getMap(), request.response());
    }

    @Get("/users")
    @SecuredAction("directory.list.users")
    public void users(HttpServerRequest request) {
        String structureId = request.params().get("structureId");
        String classId = request.params().get("classId");
        List<String> profiles = request.params().getAll("profile");
        userService.list(structureId, classId, new fr.wseduc.webutils.collections.JsonArray(profiles),
                arrayResponseHandler(request));
    }

    @Get("/api/details")
    @SecuredAction("directory.authent")
    public void details(HttpServerRequest request) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", request.params().get("id"));
        neo.send("MATCH (n:User) " + "WHERE n.id = {id} "
                + "RETURN distinct n.id as id, n.login as login, n.address as address, n.activationCode as code;",
                params, request.response());
    }

    @Post("/api/user")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @IgnoreCsrf
    public void createUser(final HttpServerRequest request) {
        request.setExpectMultipart(true);
        request.endHandler(new Handler<Void>() {

            @Override
            public void handle(Void v) {
                final String classId = request.formAttributes().get("classId");
                final String structureId = request.formAttributes().get("structureId");
                if ((classId == null || classId.trim().isEmpty())
                        && (structureId == null || structureId.trim().isEmpty())) {
                    badRequest(request);
                    return;
                }
                JsonObject user = new JsonObject().put("firstName", request.formAttributes().get("firstname"))
                        .put("lastName", request.formAttributes().get("lastname"))
                        .put("type", request.formAttributes().get("type"));
                String birthDate = request.formAttributes().get("birthDate");
                if (birthDate != null && !birthDate.trim().isEmpty()) {
                    user.put("birthDate", birthDate);
                }
                List<String> childrenIds = request.formAttributes().getAll("childrenIds");
                user.put("childrenIds", new fr.wseduc.webutils.collections.JsonArray(childrenIds));
                if (classId != null && !classId.trim().isEmpty()) {
                    userService.createInClass(classId, user, new Handler<Either<String, JsonObject>>() {
                        @Override
                        public void handle(Either<String, JsonObject> res) {
                            if (res.isRight() && res.right().getValue().size() > 0) {
                                JsonObject r = res.right().getValue();
                                JsonArray a = new fr.wseduc.webutils.collections.JsonArray().add(r.getString("id"));
                                ApplicationUtils.sendModifiedUserGroup(eb, a,
                                        handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                                            @Override
                                            public void handle(Message<JsonObject> message) {
                                                schoolService.getByClassId(classId,
                                                        new Handler<Either<String, JsonObject>>() {
                                                            @Override
                                                            public void handle(Either<String, JsonObject> s) {
                                                                if (s.isRight()) {
                                                                    JsonObject j = new JsonObject()
                                                                            .put("action",
                                                                                    "setDefaultCommunicationRules")
                                                                            .put("schoolId", s.right().getValue()
                                                                                    .getString("id"));
                                                                    eb.send("wse.communication", j);
                                                                }
                                                            }
                                                        });
                                            }
                                        }));
                                renderJson(request, r);
                            } else {
                                leftToResponse(request, res.left());
                            }
                        }
                    });
                } else {
                    userService.createInStructure(structureId, user, new Handler<Either<String, JsonObject>>() {
                        @Override
                        public void handle(Either<String, JsonObject> res) {
                            if (res.isRight() && res.right().getValue().size() > 0) {
                                JsonObject r = res.right().getValue();
                                JsonArray a = new fr.wseduc.webutils.collections.JsonArray().add(r.getString("id"));
                                ApplicationUtils.sendModifiedUserGroup(eb, a,
                                        handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                                            @Override
                                            public void handle(Message<JsonObject> message) {
                                                JsonObject j = new JsonObject()
                                                        .put("action", "setDefaultCommunicationRules")
                                                        .put("schoolId", structureId);
                                                eb.send("wse.communication", j);
                                            }
                                        }));
                                renderJson(request, r);
                            } else {
                                leftToResponse(request, res.left());
                            }
                        }
                    });
                }
            }
        });
    }

    @Get("/api/export")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    public void export(final HttpServerRequest request) {
        String neoRequest = "";
        Map<String, Object> params = new HashMap<>();
        if (request.params().get("id").equals("all")) {
            neoRequest = "MATCH (m:User)-[:IN]->g " + "WHERE NOT(m.activationCode IS NULL) "
                    + "OPTIONAL MATCH g-[:DEPENDS*0..1]->(pg:ProfileGroup)-[:HAS_PROFILE]->(p:Profile) "
                    + "RETURN distinct m.lastName as lastName, m.firstName as firstName, "
                    + "m.login as login, m.activationCode as activationCode, " + "p.name as type "
                    + "ORDER BY type, login ";
        } else if (request.params().get("id") != null) {
            neoRequest = "MATCH (m:User)-[:IN]->g-[:DEPENDS]->n "
                    + "WHERE (n:Structure OR n:Class) AND n.id = {id} AND NOT(m.activationCode IS NULL) "
                    + "OPTIONAL MATCH g-[:DEPENDS*0..1]->(pg:ProfileGroup)-[:HAS_PROFILE]->(p:Profile) "
                    + "RETURN distinct m.lastName as lastName, m.firstName as firstName, "
                    + "m.login as login, m.activationCode as activationCode, " + "p.name as type "
                    + "ORDER BY type, login ";
            params.put("id", request.params().get("id"));
        } else {
            notFound(request);
        }
        neo.send(neoRequest, params, new Handler<Message<JsonObject>>() {

            @Override
            public void handle(Message<JsonObject> res) {
                if ("ok".equals(res.body().getString("status"))) {
                    JsonArray r = Neo.resultToJsonArray(res.body().getJsonObject("result"));
                    processTemplate(request, "text/export.txt", new JsonObject().put("list", r),
                            new Handler<String>() {
                                @Override
                                public void handle(String export) {
                                    if (export != null) {
                                        request.response().putHeader("Content-Type", "application/csv");
                                        request.response().putHeader("Content-Disposition",
                                                "attachment; filename=activation_de_comptes.csv");
                                        request.response().end(export);
                                    } else {
                                        renderError(request);
                                    }
                                }
                            });
                } else {
                    renderError(request);
                }
            }
        });
    }

    public void createSuperAdmin() {
        log.info("Create super admin");
        neo.send("MATCH (n:User)-[:HAS_FUNCTION]->(f:Function { externalId : 'SUPER_ADMIN'}) " + "WHERE n.id = '"
                + admin.getString("id") + "' " + "WITH count(*) AS exists " + "WHERE exists=0 "
                + "CREATE (m:User {id:'" + admin.getString("id") + "', " + "externalId:'"
                + UUID.randomUUID().toString() + "', " + "manual:true, " + "lastName:'"
                + admin.getString("firstname") + "', " + "firstName:'" + admin.getString("lastname") + "', "
                + "login:'" + admin.getString("login") + "', " + "displayName:'" + admin.getString("firstname")
                + " " + admin.getString("lastname") + "', " + "password:'"
                + BCrypt.hashpw(admin.getString("password"), BCrypt.gensalt()) + "'})-[:HAS_FUNCTION]->"
                + "(f:Function { externalId : 'SUPER_ADMIN', name : 'SuperAdmin' })", event -> {
                    if (!"ok".equals(event.body().getString("status"))) {
                        log.error("Create default super admin : " + event.body().getString("message"));
                    }
                });
        neo.execute("MERGE (u:User {id:'OAuthSystemUser'}) ON CREATE SET u.manual = true", (JsonObject) null,
                event -> {
                    if (!"ok".equals(event.body().getString("status"))) {
                        log.error("Create OAuthSystemUser : " + event.body().getString("message"));
                    }
                });
    }

    @BusAddress("directory")
    public void directoryHandler(final Message<JsonObject> message) {
        String action = message.body().getString("action", "");
        String userId = message.body().getString("userId");
        switch (action) {
        case "usersInProfilGroup":
            boolean itSelf2 = message.body().getBoolean("itself", false);
            String excludeUserId = message.body().getString("excludeUserId");
            userService.list(userId, itSelf2, excludeUserId, responseHandler(message));
            break;
        case "getUser":
            userService.get(userId, false, BusResponseHandler.busResponseHandler(message));
            break;
        case "getUserInfos":
            userService.getInfos(userId, BusResponseHandler.busResponseHandler(message));
            break;
        case "list-users":
            JsonArray userIds = message.body().getJsonArray("userIds",
                    new fr.wseduc.webutils.collections.JsonArray());
            JsonArray groupIds = message.body().getJsonArray("groupIds",
                    new fr.wseduc.webutils.collections.JsonArray());
            boolean itSelf = message.body().getBoolean("itself", false);
            String excludeId = message.body().getString("excludeUserId");
            userService.list(groupIds, userIds, itSelf, excludeId, busArrayHandler(message));
            break;
        case "list-structures":
            schoolService.list(message.body().getJsonArray("fields"), busArrayHandler(message));
            break;
        case "list-groups":
            String structureId = message.body().getString("structureId");
            String type = message.body().getString("type");
            boolean subGroups = message.body().getBoolean("subGroups", false);
            groupService.list(structureId, type, subGroups, busArrayHandler(message));
            break;
        case "list-adml":
            String sId = message.body().getString("structureId");
            userService.listAdml(sId, responseHandler(message));
            break;
        default:
            message.reply(new JsonObject().put("status", "error").put("message", "Invalid action."));
        }
    }

    public void setSchoolService(SchoolService schoolService) {
        this.schoolService = schoolService;
    }

    public void setClassService(ClassService classService) {
        this.classService = classService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    private Handler<Either<String, JsonArray>> responseHandler(final Message<JsonObject> message) {
        return new Handler<Either<String, JsonArray>>() {

            @Override
            public void handle(Either<String, JsonArray> res) {
                JsonArray j;
                if (res.isRight()) {
                    j = res.right().getValue();
                } else {
                    log.warn(res.left().getValue());
                    j = new fr.wseduc.webutils.collections.JsonArray();
                }
                message.reply(j);
            }
        };
    }

    public void setGroupService(GroupService groupService) {
        this.groupService = groupService;
    }
}