io.flowly.auth.manager.BaseManager.java Source code

Java tutorial

Introduction

Here is the source code for io.flowly.auth.manager.BaseManager.java

Source

/*
 * Copyright (c) 2015 The original author or authors.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Apache License v2.0 
 *  which accompanies this distribution.
 *
 *  The Apache License v2.0 is available at
 *  http://opensource.org/licenses/Apache-2.0
 *
 *  You may elect to redistribute this code under this license.
 */

package io.flowly.auth.manager;

import io.flowly.auth.graph.Schema;
import io.flowly.core.ObjectKeys;
import io.flowly.core.data.manager.GraphManager;
import io.flowly.core.security.Group;
import io.flowly.core.security.Permission;
import io.flowly.core.security.User;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public abstract class BaseManager extends GraphManager {
    public BaseManager(Graph graph) {
        super(graph);
    }

    /**
     * Create a vertex and add specified relationships in the auth graph.
     *
     * @param jsonObject holds the parameters used to create the vertex.
     * @return an empty list or a list of validation errors.
     */
    public abstract JsonArray create(JsonObject jsonObject);

    /**
     * Update a vertex and specified relationships in the auth graph.
     *
     * @param jsonObject holds the parameters used to update the vertex.
     * @return an empty list or a list of validation errors.
     */
    public abstract JsonArray update(JsonObject jsonObject);

    /**
     * Get the vertex based on the unique id assigned by the auth graph.
     *
     * @param id the vertex id in auth graph.
     * @return JSON object representing the vertex and its properties and relationships.
     */
    public abstract JsonObject get(Long id);

    /**
     * Get the vertex based on the user generated unique id.
     *
     * @param uniqueId string that uniquely identifies a vertex in the auth graph.
     * @return JSON object representing the vertex.
     */
    public abstract JsonObject get(String uniqueId);

    /**
     * Search for vertices based on provided criteria.
     *
     * @param pageNumber the page number used to retrieve vertices.
     * @param pageSize the number of vertices that fill a page.
     * @return a list of vertices.
     */
    public abstract JsonArray search(int pageNumber, int pageSize);

    public Handler<Message<JsonObject>> createHandler() {
        return message -> message.reply(create(message.body()));
    }

    public Handler<Message<JsonObject>> updateHandler() {
        return message -> message.reply(update(message.body()));
    }

    public Handler<Message<JsonObject>> searchHandler() {
        return message -> {
            JsonObject args = message.body();
            message.reply(search(args.getInteger(ObjectKeys.PAGE_NUMBER), args.getInteger(ObjectKeys.PAGE_SIZE)));
        };
    }

    public abstract Handler<Message<JsonObject>> getHandler();

    /**
     * Iterates the list of permission objects and grants them to the user
     * or group, if they are not previously granted.
     *
     * @param vertex the node that represents a user or group vertex.
     * @param permissions array of JSON objects each representing permissions on a given resource.
     * @param existingIds existing permissions granted to the user or group.
     */
    protected void grantPermissions(Vertex vertex, JsonArray permissions, Set<Long> existingIds) {
        if (permissions == null) {
            return;
        }

        for (Object prm : permissions) {
            Permission permission = new Permission((JsonObject) prm);

            if (existingIds == null || !existingIds.contains(permission.getResourceVertexId())) {
                Vertex resourceVertex = getVertex(permission.getResourceVertexId());
                vertex.addEdge(Schema.E_HAS_PERMISSION, resourceVertex, Schema.E_P_RWX, permission.getRWX());
            }
        }
    }

    /**
     * Revoke and grant permissions based on the specification.
     * Sequence - remove specified permissions, update specified permissions and then add specified permissions.
     *
     * @param vertex the node that represents a user or group vertex.
     * @param jsonObject JSON object representing the user or group permissions.
     */
    protected void redoPermissions(Vertex vertex, JsonObject jsonObject) {
        // Remove permissions.
        JsonArray permissionsToRemove = jsonObject.getJsonArray(Permission.PERMISSIONS_TO_REMOVE);
        if (permissionsToRemove != null) {
            graph.traversal().V(vertex).outE(Schema.E_HAS_PERMISSION).as("e").inV()
                    .has(T.id, P.within(permissionsToRemove.getList().toArray())).<Edge>select("e").drop().toList();
        }

        // Update permissions.
        JsonArray permissionsToUpdate = jsonObject.getJsonArray(Permission.PERMISSIONS_TO_UPDATE);
        if (permissionsToUpdate != null) {
            for (Object prm : permissionsToUpdate) {
                Permission permission = new Permission((JsonObject) prm);
                Long resourceVertexId = permission.getResourceVertexId();

                graph.traversal().V(vertex).outE(Schema.E_HAS_PERMISSION).as("e").inV().has(T.id, resourceVertexId)
                        .<Edge>select("e").property(Schema.E_P_RWX, permission.getRWX()).toList();
            }
        }

        // Add permissions.
        JsonArray permissionsToAdd = jsonObject.getJsonArray(Permission.PERMISSIONS_TO_ADD);
        if (permissionsToAdd != null) {
            Set<Long> existingIds = new HashSet<>();
            Long[] idsToAdd = new Long[permissionsToAdd.size()];

            for (int i = 0; i < permissionsToAdd.size(); i++) {
                idsToAdd[i] = permissionsToAdd.getJsonObject(i).getLong(Permission.RESOURCE_VERTEX_ID);
            }

            graph.traversal().V(vertex).outE(Schema.E_HAS_PERMISSION).inV().has(T.id, P.within(idsToAdd))
                    .sideEffect(s -> {
                        existingIds.add((Long) s.get().id());
                    }).toList();

            grantPermissions(vertex, permissionsToAdd, existingIds);
        }

    }

    /**
     * Iterates through the list of ids representing group or user vertices and
     * adds "member" and "memberOf" edges or vice-versa between the given vertex
     * and vertices based on whether the given vertex is the owner or not.
     *
     * @param vertex the node that represents a group or user vertex.
     * @param isOwner indicates if the given vertex is a owner or member.
     * @param ids array of ids representing group or user vertices.
     * @param existingIds set of vertices that hold an edge between them and the provided vertex.
     */
    protected void grantMemberships(Vertex vertex, boolean isOwner, JsonArray ids, Set<Long> existingIds) {
        if (ids == null) {
            return;
        }

        for (Object id : ids) {
            if (existingIds == null || !existingIds.contains(id)) {
                Vertex vertexById = getVertex(id);
                vertex.addEdge(isOwner ? Schema.E_MEMBER : Schema.E_MEMBER_OF, vertexById);
                vertexById.addEdge(isOwner ? Schema.E_MEMBER_OF : Schema.E_MEMBER, vertex);
            }
        }
    }

    /**
     * Add and remove users based on the specifications.
     * Sequence: remove and then add.
     *
     * @param vertex the node that represents a group or user vertex.
     * @param isOwner indicates if the given vertex is an owner or member.
     * @param idsToAdd list of ids representing user or group vertices to be added.
     * @param idsToRemove list of ids representing user or group vertices to be removed.
     */
    protected void redoMemberships(Vertex vertex, boolean isOwner, JsonArray idsToAdd, JsonArray idsToRemove) {
        if (idsToRemove != null) {
            graph.traversal().V(vertex).bothE(Schema.E_MEMBER, Schema.E_MEMBER_OF).as("e").otherV()
                    .has(T.id, P.within(idsToRemove.getList().toArray())).<Edge>select("e").drop().toList();
        }

        if (idsToAdd != null) {
            Set<Long> existingIds = new HashSet<>();

            graph.traversal().V(vertex).bothE(isOwner ? Schema.E_MEMBER : Schema.E_MEMBER_OF).otherV()
                    .has(T.id, P.within(idsToAdd.getList().toArray())).sideEffect(s -> {
                        existingIds.add((Long) s.get().id());
                    }).toList();

            grantMemberships(vertex, isOwner, idsToAdd, existingIds);
        }
    }

    /**
     * Retrieves direct and indirect memberships that a user or group holds.
     *
     * @param vertex vertex in the auth graph representing a user or group.
     * @param jsonObject JSON object representing the user or group to which retrieved memberships are added.
     * @param includeEffectiveMemberships indicates if all the user or group memberships are to be retrieved.
     * @param includeDirectMemberships indicates if the user's direct memberships are to be retrieved.
     * @param includePermissions indicates if the permissions granted to each group are to be retrieved.
     */
    protected void getMemberships(Vertex vertex, JsonObject jsonObject, boolean includeEffectiveMemberships,
            boolean includeDirectMemberships, boolean includePermissions) {
        boolean isUserVertex = jsonObject.containsKey(User.USER_ID);
        String uniqueId = isUserVertex ? jsonObject.getString(User.USER_ID) : jsonObject.getString(Group.GROUP_ID);

        if (includeEffectiveMemberships || includePermissions) {
            JsonArray effectiveMemberships = new JsonArray();
            jsonObject.put(User.EFFECTIVE_MEMBERSHIPS, effectiveMemberships);

            List<Vertex> groupVertices = graph.traversal().V(vertex).repeat(__.outE(Schema.E_MEMBER_OF).inV())
                    .emit().toList();
            getDistinctMemberships(groupVertices, effectiveMemberships, uniqueId, isUserVertex, includePermissions);
        }

        if (includeDirectMemberships) {
            JsonArray directMemberships = new JsonArray();
            jsonObject.put(User.DIRECT_MEMBERSHIPS, directMemberships);

            getDistinctMemberships(graph.traversal().V(vertex).outE(Schema.E_MEMBER_OF).inV().toList(),
                    directMemberships, null, false, false);
        }
    }

    /**
     * Add distinct groups to the memberships array.
     *
     * @param groupVertices vertices that represent groups in the auth graph.
     * @param memberships JSON array to which the group is to be added.
     * @param uniqueId unique id that identifies a user or group. Can be null.
     * @param idRepresentsUser indicates if the unique id argument represents a user or a group.
     * @param includePermissions indicates if the permissions granted to the group are to be retrieved.
     */
    private void getDistinctMemberships(List<Vertex> groupVertices, JsonArray memberships, String uniqueId,
            boolean idRepresentsUser, boolean includePermissions) {
        Set<Object> groupIds = new HashSet<>();

        for (Vertex groupVertex : groupVertices) {
            if (!groupIds.contains(groupVertex.id())) {
                JsonObject group = makeGroupObject(groupVertex, uniqueId, idRepresentsUser);
                memberships.add(group);

                if (includePermissions) {
                    getDirectPermissions(groupVertex, group);
                }

                groupIds.add(groupVertex.id());
            }
        }
    }

    /**
     * Get the permissions directly granted on the given user or group vertex.
     *
     * @param vertex represents a user or group vertex in the auth graph.
     * @param jsonObject JSON object representing the user or group.
     */
    protected void getDirectPermissions(Vertex vertex, JsonObject jsonObject) {
        jsonObject.put(Permission.DIRECT_PERMISSIONS, new JsonArray(graph.traversal().V(vertex)
                .outE(Schema.E_HAS_PERMISSION).map(m -> makePermissionObject(m.get())).toList()));
    }

    /**
     * Generate a JSON representation of the user vertex.
     *
     * @param userVertex the node that represents a user vertex.
     * @return JSON object representing the user attributes.
     */
    protected User makeUserObject(Vertex userVertex) {
        User user = new User();
        user.setId(userVertex.id());
        user.setUserId(getPropertyValue(userVertex, Schema.V_P_USER_ID));
        user.setFirstName(getPropertyValue(userVertex, Schema.V_P_FIRST_NAME));
        user.setLastName(getPropertyValue(userVertex, Schema.V_P_LAST_NAME));
        user.setMiddleName(getPropertyValue(userVertex, Schema.V_P_MIDDLE_NAME));
        user.setFullName(getPropertyValue(userVertex, Schema.V_P_NAME));
        user.setInternal(getPropertyValue(userVertex, Schema.V_P_IS_INTERNAL));

        return user;
    }

    /**
     * Generate a JSON representation of the group vertex.
     *
     * @param groupVertex vertex in the auth graph that represents a group.
     * @param uniqueId optional string that identifies a user or group.
     *                 If provided, checks if the group and userId/groupId have a direct edge.
     * @param idRepresentsUser indicates if the unique id argument represents a user or a group.
     * @return JSON object representing a group.
     */
    protected Group makeGroupObject(Vertex groupVertex, String uniqueId, boolean idRepresentsUser) {
        Group group = new Group();
        group.setId(groupVertex.id());
        group.setGroupId(getPropertyValue(groupVertex, Schema.V_P_GROUP_ID));
        group.setDescription(getPropertyValue(groupVertex, Schema.V_P_DESCRIPTION));

        if (uniqueId != null) {
            // Figure out if the user or group is a direct or indirect member of this group.
            String label = idRepresentsUser ? Schema.V_USER : Schema.V_GROUP;
            String key = idRepresentsUser ? Schema.V_P_USER_ID : Schema.V_P_GROUP_ID;

            group.setInherited(!graph.traversal().V(groupVertex).outE(Schema.E_MEMBER).inV()
                    .has(label, key, uniqueId).hasNext());
        }

        return group;
    }

    /**
     * Generate a JSON representation of a permission - read, write and execute privileges on a resource.
     *
     * @param permissionEdge edge in the auth graph that represents a permission.
     * @return JSON object representing access rights granted on a resource.
     */
    protected Permission makePermissionObject(Edge permissionEdge) {
        Vertex resource = permissionEdge.inVertex();

        Permission permission = new Permission();
        permission.setRWX(getPropertyValue(permissionEdge, Schema.E_P_RWX));
        permission.setResourceId(getPropertyValue(resource, Schema.V_P_RESOURCE_ID));
        permission.setResourceVertexId((Long) resource.id());

        return permission;
    }
}