package io.lumify.securegraph.model.workspace; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.inject.Inject; import com.google.inject.Singleton; import io.lumify.core.exception.LumifyAccessDeniedException; import io.lumify.core.exception.LumifyResourceNotFoundException; import io.lumify.core.formula.FormulaEvaluator; import io.lumify.core.model.lock.LockRepository; import io.lumify.core.model.ontology.OntologyRepository; import io.lumify.core.model.properties.LumifyProperties; import io.lumify.core.model.user.AuthorizationRepository; import io.lumify.core.model.user.UserRepository; import io.lumify.core.model.workspace.*; import io.lumify.core.model.workspace.diff.WorkspaceDiffHelper; import io.lumify.core.security.LumifyVisibility; import io.lumify.core.user.SystemUser; import io.lumify.core.user.User; import io.lumify.core.util.LumifyLogger; import io.lumify.core.util.LumifyLoggerFactory; import io.lumify.securegraph.model.user.SecureGraphUserRepository; import io.lumify.web.clientapi.model.ClientApiWorkspaceDiff; import io.lumify.web.clientapi.model.GraphPosition; import io.lumify.web.clientapi.model.WorkspaceAccess; import org.elasticsearch.common.base.Function; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.collect.Maps; import org.securegraph.*; import org.securegraph.mutation.ElementMutation; import org.securegraph.mutation.ExistingEdgeMutation; import org.securegraph.util.ConvertingIterable; import org.securegraph.util.FilterIterable; import org.securegraph.util.VerticesToEdgeIdsIterable; import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; import static org.securegraph.util.IterableUtils.toList; import static org.securegraph.util.IterableUtils.toSet; @Singleton public class SecureGraphWorkspaceRepository extends WorkspaceRepository { private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(SecureGraphWorkspaceRepository.class); private UserRepository userRepository; private AuthorizationRepository authorizationRepository; private WorkspaceDiffHelper workspaceDiff; private final LockRepository lockRepository; private Cache<String, Boolean> usersWithReadAccessCache = CacheBuilder.newBuilder() .expireAfterWrite(15, TimeUnit.SECONDS) .build(); private Cache<String, Boolean> usersWithCommentAccessCache = CacheBuilder.newBuilder() .expireAfterWrite(15, TimeUnit.SECONDS) .build(); private Cache<String, Boolean> usersWithWriteAccessCache = CacheBuilder.newBuilder() .expireAfterWrite(15, TimeUnit.SECONDS) .build(); private Cache<String, List<WorkspaceUser>> usersWithAccessCache = CacheBuilder.newBuilder() .expireAfterWrite(15, TimeUnit.SECONDS) .build(); private Cache<String, Vertex> userWorkspaceVertexCache = CacheBuilder.newBuilder() .expireAfterWrite(15, TimeUnit.SECONDS) .build(); public void clearCache() { usersWithReadAccessCache.invalidateAll(); usersWithCommentAccessCache.invalidateAll(); usersWithWriteAccessCache.invalidateAll(); usersWithAccessCache.invalidateAll(); userWorkspaceVertexCache.invalidateAll(); } @Inject public SecureGraphWorkspaceRepository( OntologyRepository ontologyRepository, Graph graph, UserRepository userRepository, AuthorizationRepository authorizationRepository, WorkspaceDiffHelper workspaceDiff, LockRepository lockRepository) { super(graph); this.userRepository = userRepository; this.authorizationRepository = authorizationRepository; this.workspaceDiff = workspaceDiff; this.lockRepository = lockRepository; authorizationRepository.addAuthorizationToGraph(VISIBILITY_STRING); authorizationRepository.addAuthorizationToGraph(LumifyVisibility.SUPER_USER_VISIBILITY_STRING); } @Override public void delete(final Workspace workspace, final User user) { if (!hasWritePermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } lockRepository.lock(getLockName(workspace), new Runnable() { @Override public void run() { Authorizations authorizations = userRepository.getAuthorizations(user, UserRepository.VISIBILITY_STRING, LumifyVisibility.SUPER_USER_VISIBILITY_STRING, workspace.getWorkspaceId()); Vertex workspaceVertex = getVertexFromWorkspace(workspace, true, authorizations); getGraph().removeVertex(workspaceVertex, authorizations); getGraph().flush(); authorizationRepository.removeAuthorizationFromGraph(workspace.getWorkspaceId()); } }); } private String getLockName(Workspace workspace) { return getLockName(workspace.getWorkspaceId()); } private String getLockName(String workspaceId) { return "WORKSPACE_" + workspaceId; } public Vertex getVertex(String workspaceId, User user) { String cacheKey = getUserWorkspaceVertexCacheKey(workspaceId, user); Vertex workspaceVertex = userWorkspaceVertexCache.getIfPresent(cacheKey); if (workspaceVertex != null) { return workspaceVertex; } Authorizations authorizations = userRepository.getAuthorizations(user, UserRepository.VISIBILITY_STRING, LumifyVisibility.SUPER_USER_VISIBILITY_STRING, workspaceId); workspaceVertex = getGraph().getVertex(workspaceId, authorizations); userWorkspaceVertexCache.put(cacheKey, workspaceVertex); return workspaceVertex; } public String getUserWorkspaceVertexCacheKey(String workspaceId, User user) { return workspaceId + user.getUserId(); } private Vertex getVertexFromWorkspace(Workspace workspace, boolean includeHidden, Authorizations authorizations) { if (workspace instanceof SecureGraphWorkspace) { return ((SecureGraphWorkspace) workspace).getVertex(getGraph(), includeHidden, authorizations); } return getGraph().getVertex(workspace.getWorkspaceId(), includeHidden ? FetchHint.ALL_INCLUDING_HIDDEN : FetchHint.ALL, authorizations); } @Override public Workspace findById(String workspaceId, boolean includeHidden, User user) { LOGGER.debug("findById(workspaceId: %s, userId: %s)", workspaceId, user.getUserId()); Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspaceId); Vertex workspaceVertex = getGraph().getVertex(workspaceId, includeHidden ? FetchHint.ALL_INCLUDING_HIDDEN : FetchHint.ALL, authorizations); if (workspaceVertex == null) { return null; } if (!hasReadPermissions(workspaceId, user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have read access to workspace " + workspaceId, user, workspaceId); } return new SecureGraphWorkspace(workspaceVertex); } @Override public Workspace add(String workspaceId, String title, User user) { authorizationRepository.addAuthorizationToGraph(workspaceId); Authorizations authorizations = userRepository.getAuthorizations(user, UserRepository.VISIBILITY_STRING, VISIBILITY_STRING, workspaceId); Vertex userVertex = getGraph().getVertex(user.getUserId(), authorizations); checkNotNull(userVertex, "Could not find user: " + user.getUserId()); VertexBuilder workspaceVertexBuilder = getGraph().prepareVertex(workspaceId, VISIBILITY.getVisibility()); LumifyProperties.CONCEPT_TYPE.setProperty(workspaceVertexBuilder, WORKSPACE_CONCEPT_IRI, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.TITLE.setProperty(workspaceVertexBuilder, title, VISIBILITY.getVisibility()); Vertex workspaceVertex = workspaceVertexBuilder.save(authorizations); addWorkspaceToUser(workspaceVertex, userVertex, authorizations); getGraph().flush(); return new SecureGraphWorkspace(workspaceVertex); } public void addWorkspaceToUser(Vertex workspaceVertex, Vertex userVertex, Authorizations authorizations) { EdgeBuilder edgeBuilder = getGraph().prepareEdge(workspaceVertex, userVertex, WORKSPACE_TO_USER_RELATIONSHIP_IRI, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_USER_IS_CREATOR.setProperty(edgeBuilder, true, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_USER_ACCESS.setProperty(edgeBuilder, WorkspaceAccess.WRITE.toString(), VISIBILITY.getVisibility()); edgeBuilder.save(authorizations); } @Override public Iterable<Workspace> findAllForUser(final User user) { checkNotNull(user, "User is required"); Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, UserRepository.VISIBILITY_STRING); Vertex userVertex = getGraph().getVertex(user.getUserId(), authorizations); checkNotNull(userVertex, "Could not find user vertex with id " + user.getUserId()); return toList(new ConvertingIterable<Vertex, Workspace>(userVertex.getVertices(Direction.IN, WORKSPACE_TO_USER_RELATIONSHIP_IRI, authorizations)) { @Override protected Workspace convert(Vertex workspaceVertex) { String cacheKey = getUserWorkspaceVertexCacheKey(workspaceVertex.getId(), user); userWorkspaceVertexCache.put(cacheKey, workspaceVertex); return new SecureGraphWorkspace(workspaceVertex); } }); } @Override public void setTitle(Workspace workspace, String title, User user) { if (!hasWritePermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } Authorizations authorizations = userRepository.getAuthorizations(user); Vertex workspaceVertex = getVertexFromWorkspace(workspace, false, authorizations); WorkspaceLumifyProperties.TITLE.setProperty(workspaceVertex, title, VISIBILITY.getVisibility(), authorizations); getGraph().flush(); } @Override public List<WorkspaceUser> findUsersWithAccess(final String workspaceId, final User user) { String cacheKey = workspaceId + user.getUserId(); List<WorkspaceUser> usersWithAccess = this.usersWithAccessCache.getIfPresent(cacheKey); if (usersWithAccess != null) { return usersWithAccess; } Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspaceId); Vertex workspaceVertex = getVertex(workspaceId, user); Iterable<Edge> userEdges = workspaceVertex.getEdges(Direction.BOTH, WORKSPACE_TO_USER_RELATIONSHIP_IRI, authorizations); usersWithAccess = toList(new ConvertingIterable<Edge, WorkspaceUser>(userEdges) { @Override protected WorkspaceUser convert(Edge edge) { String userId = edge.getOtherVertexId(workspaceId); String accessString = WorkspaceLumifyProperties.WORKSPACE_TO_USER_ACCESS.getPropertyValue(edge); WorkspaceAccess workspaceAccess = WorkspaceAccess.NONE; if (accessString != null && accessString.length() > 0) { workspaceAccess = WorkspaceAccess.valueOf(accessString); } boolean isCreator = WorkspaceLumifyProperties.WORKSPACE_TO_USER_IS_CREATOR.getPropertyValue(edge, false); return new WorkspaceUser(userId, workspaceAccess, isCreator); } }); this.usersWithAccessCache.put(cacheKey, usersWithAccess); return usersWithAccess; } @Override public List<WorkspaceEntity> findEntities(final Workspace workspace, final User user) { LOGGER.debug("findEntities(workspaceId: %s, userId: %s)", workspace.getWorkspaceId(), user.getUserId()); if (!hasReadPermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have read access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } return lockRepository.lock(getLockName(workspace), new Callable<List<WorkspaceEntity>>() { @Override public List<WorkspaceEntity> call() throws Exception { return findEntitiesNoLock(workspace, false, user); } }); } public List<WorkspaceEntity> findEntitiesNoLock(final Workspace workspace, boolean includeHidden, User user) { Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspace.getWorkspaceId()); Vertex workspaceVertex = getVertexFromWorkspace(workspace, includeHidden, authorizations); Iterable<Edge> entityEdges = workspaceVertex.getEdges(Direction.BOTH, WORKSPACE_TO_ENTITY_RELATIONSHIP_IRI, authorizations); return toList(new ConvertingIterable<Edge, WorkspaceEntity>(entityEdges) { @Override protected WorkspaceEntity convert(Edge edge) { String entityVertexId = edge.getOtherVertexId(workspace.getWorkspaceId()); Integer graphPositionX = WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_X.getPropertyValue(edge); Integer graphPositionY = WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_Y.getPropertyValue(edge); String graphLayoutJson = WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_LAYOUT_JSON.getPropertyValue(edge); boolean visible = WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_VISIBLE.getPropertyValue(edge, false); return new WorkspaceEntity(entityVertexId, visible, graphPositionX, graphPositionY, graphLayoutJson); } }); } private Iterable<Edge> findEdges(final Workspace workspace, List<WorkspaceEntity> workspaceEntities, boolean includeHidden, User user) { Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspace.getWorkspaceId()); Iterable<Vertex> vertices = WorkspaceEntity.toVertices(getGraph(), workspaceEntities, includeHidden, authorizations); Iterable<String> edgeIds = toSet(new VerticesToEdgeIdsIterable(vertices, authorizations)); return getGraph().getEdges(edgeIds, includeHidden ? FetchHint.ALL_INCLUDING_HIDDEN : FetchHint.ALL, authorizations); } @Override public Workspace copyTo(Workspace workspace, User destinationUser, User user) { Workspace newWorkspace = super.copyTo(workspace, destinationUser, user); getGraph().flush(); return newWorkspace; } @Override public void softDeleteEntitiesFromWorkspace(Workspace workspace, List<String> entityIdsToDelete, User user) { if (!hasWritePermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspace.getWorkspaceId()); final Vertex workspaceVertex = getVertexFromWorkspace(workspace, true, authorizations); List<Edge> allEdges = toList(workspaceVertex.getEdges(Direction.BOTH, authorizations)); for (final String vertexId : entityIdsToDelete) { LOGGER.debug("workspace delete (%s): %s", workspace.getWorkspaceId(), vertexId); Iterable<Edge> edges = new FilterIterable<Edge>(allEdges) { @Override protected boolean isIncluded(Edge o) { String entityVertexId = o.getOtherVertexId(workspaceVertex.getId()); return entityVertexId.equalsIgnoreCase(vertexId); } }; for (Edge edge : edges) { ExistingEdgeMutation m = edge.prepareMutation(); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_VISIBLE.setProperty(m, false, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_LAYOUT_JSON.removeProperty(m, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_X.removeProperty(m, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_Y.removeProperty(m, VISIBILITY.getVisibility()); m.save(authorizations); } } getGraph().flush(); } @Override public void updateEntitiesOnWorkspace(final Workspace workspace, final Iterable<Update> updates, final User user) { if (!hasCommentPermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } lockRepository.lock(getLockName(workspace.getWorkspaceId()), new Runnable() { @Override public void run() { Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspace.getWorkspaceId()); Vertex workspaceVertex = getVertexFromWorkspace(workspace, true, authorizations); if (workspaceVertex == null) { throw new LumifyResourceNotFoundException("Could not find workspace vertex: " + workspace.getWorkspaceId(), workspace.getWorkspaceId()); } Iterable<String> vertexIds = new ConvertingIterable<Update, String>(updates) { @Override protected String convert(Update o) { return o.getVertexId(); } }; Iterable<Vertex> vertices = getGraph().getVertices(vertexIds, authorizations); ImmutableMap<String, Vertex> verticesMap = Maps.uniqueIndex(vertices, new Function<Vertex, String>() { @Override public String apply(Vertex vertex) { return vertex.getId(); } }); for (Update update : updates) { Vertex otherVertex = verticesMap.get(update.getVertexId()); checkNotNull(otherVertex, "Could not find vertex with id: " + update.getVertexId()); createEdge(workspaceVertex, otherVertex, update.getGraphPosition(), update.getGraphLayoutJson(), update.getVisible(), authorizations); } getGraph().flush(); } }); } private void createEdge(Vertex workspaceVertex, Vertex otherVertex, GraphPosition graphPosition, String graphLayoutJson, Boolean visible, Authorizations authorizations) { List<Edge> existingEdges = toList(workspaceVertex.getEdges(otherVertex, Direction.BOTH, authorizations)); if (existingEdges.size() > 0) { for (Edge existingEdge : existingEdges) { ElementMutation<Edge> m = existingEdge.prepareMutation(); if (graphPosition != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_X.setProperty(m, graphPosition.getX(), VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_Y.setProperty(m, graphPosition.getY(), VISIBILITY.getVisibility()); } if (graphLayoutJson != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_LAYOUT_JSON.setProperty(m, graphLayoutJson, VISIBILITY.getVisibility()); } if (visible != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_VISIBLE.setProperty(m, visible, VISIBILITY.getVisibility()); } m.save(authorizations); } } else { EdgeBuilder edgeBuilder = getGraph().prepareEdge(workspaceVertex, otherVertex, WORKSPACE_TO_ENTITY_RELATIONSHIP_IRI, VISIBILITY.getVisibility()); if (graphPosition != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_X.setProperty(edgeBuilder, graphPosition.getX(), VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_POSITION_Y.setProperty(edgeBuilder, graphPosition.getY(), VISIBILITY.getVisibility()); } if (graphLayoutJson != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_GRAPH_LAYOUT_JSON.setProperty(edgeBuilder, graphLayoutJson, VISIBILITY.getVisibility()); } if (visible != null) { WorkspaceLumifyProperties.WORKSPACE_TO_ENTITY_VISIBLE.setProperty(edgeBuilder, visible, VISIBILITY.getVisibility()); } edgeBuilder.save(authorizations); } } @Override public void deleteUserFromWorkspace(final Workspace workspace, final String userId, final User user) { if (!hasWritePermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } lockRepository.lock(getLockName(workspace), new Runnable() { @Override public void run() { Authorizations authorizations = userRepository.getAuthorizations(user, UserRepository.VISIBILITY_STRING, VISIBILITY_STRING, workspace.getWorkspaceId()); Vertex userVertex = getGraph().getVertex(userId, authorizations); if (userVertex == null) { throw new LumifyResourceNotFoundException("Could not find user: " + userId, userId); } Vertex workspaceVertex = getVertexFromWorkspace(workspace, true, authorizations); List<Edge> edges = toList(workspaceVertex.getEdges(userVertex, Direction.BOTH, WORKSPACE_TO_USER_RELATIONSHIP_IRI, authorizations)); for (Edge edge : edges) { getGraph().removeEdge(edge, authorizations); } getGraph().flush(); clearCache(); } }); } @Override public boolean hasCommentPermissions(String workspaceId, User user) { if (user instanceof SystemUser) { return true; } String cacheKey = workspaceId + user.getUserId(); Boolean hasCommentAccess = usersWithCommentAccessCache.getIfPresent(cacheKey); if (hasCommentAccess != null && hasCommentAccess) { return true; } List<WorkspaceUser> usersWithAccess = findUsersWithAccess(workspaceId, user); for (WorkspaceUser userWithAccess : usersWithAccess) { if (userWithAccess.getUserId().equals(user.getUserId()) && ( userWithAccess.getWorkspaceAccess() == WorkspaceAccess.WRITE || userWithAccess.getWorkspaceAccess() == WorkspaceAccess.COMMENT )) { usersWithCommentAccessCache.put(cacheKey, true); return true; } } return false; } @Override public boolean hasWritePermissions(String workspaceId, User user) { if (user instanceof SystemUser) { return true; } String cacheKey = workspaceId + user.getUserId(); Boolean hasWriteAccess = usersWithWriteAccessCache.getIfPresent(cacheKey); if (hasWriteAccess != null && hasWriteAccess) { return true; } List<WorkspaceUser> usersWithAccess = findUsersWithAccess(workspaceId, user); for (WorkspaceUser userWithAccess : usersWithAccess) { if (userWithAccess.getUserId().equals(user.getUserId()) && userWithAccess.getWorkspaceAccess() == WorkspaceAccess.WRITE) { usersWithWriteAccessCache.put(cacheKey, true); return true; } } return false; } @Override public boolean hasReadPermissions(String workspaceId, User user) { if (user instanceof SystemUser) { return true; } String cacheKey = workspaceId + user.getUserId(); Boolean hasReadAccess = usersWithReadAccessCache.getIfPresent(cacheKey); if (hasReadAccess != null && hasReadAccess) { return true; } List<WorkspaceUser> usersWithAccess = findUsersWithAccess(workspaceId, user); for (WorkspaceUser userWithAccess : usersWithAccess) { if (userWithAccess.getUserId().equals(user.getUserId()) && (userWithAccess.getWorkspaceAccess() == WorkspaceAccess.WRITE || userWithAccess.getWorkspaceAccess() == WorkspaceAccess.READ || userWithAccess.getWorkspaceAccess() == WorkspaceAccess.COMMENT)) { return true; } } return false; } @Override public void updateUserOnWorkspace(final Workspace workspace, final String userId, final WorkspaceAccess workspaceAccess, final User user) { if (!hasWritePermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } lockRepository.lock(getLockName(workspace), new Runnable() { @Override public void run() { Authorizations authorizations = userRepository.getAuthorizations(user, VISIBILITY_STRING, workspace.getWorkspaceId()); Vertex otherUserVertex; if (userRepository instanceof SecureGraphUserRepository) { otherUserVertex = ((SecureGraphUserRepository) userRepository).findByIdUserVertex(userId); } else { otherUserVertex = getGraph().getVertex(userId, authorizations); } if (otherUserVertex == null) { throw new LumifyResourceNotFoundException("Could not find user: " + userId, userId); } Vertex workspaceVertex = getVertexFromWorkspace(workspace, true, authorizations); if (workspaceVertex == null) { throw new LumifyResourceNotFoundException("Could not find workspace vertex: " + workspace.getWorkspaceId(), workspace.getWorkspaceId()); } List<Edge> existingEdges = toList(workspaceVertex.getEdges(otherUserVertex, Direction.OUT, WORKSPACE_TO_USER_RELATIONSHIP_IRI, authorizations)); if (existingEdges.size() > 0) { for (Edge existingEdge : existingEdges) { WorkspaceLumifyProperties.WORKSPACE_TO_USER_ACCESS.setProperty(existingEdge, workspaceAccess.toString(), VISIBILITY.getVisibility(), authorizations); } } else { EdgeBuilder edgeBuilder = getGraph().prepareEdge(workspaceVertex, otherUserVertex, WORKSPACE_TO_USER_RELATIONSHIP_IRI, VISIBILITY.getVisibility()); WorkspaceLumifyProperties.WORKSPACE_TO_USER_ACCESS.setProperty(edgeBuilder, workspaceAccess.toString(), VISIBILITY.getVisibility()); edgeBuilder.save(authorizations); } getGraph().flush(); clearCache(); } }); } @Override public ClientApiWorkspaceDiff getDiff(final Workspace workspace, final User user, final Locale locale, final String timeZone) { if (!hasReadPermissions(workspace.getWorkspaceId(), user)) { throw new LumifyAccessDeniedException("user " + user.getUserId() + " does not have write access to workspace " + workspace.getWorkspaceId(), user, workspace.getWorkspaceId()); } return lockRepository.lock(getLockName(workspace), new Callable<ClientApiWorkspaceDiff>() { @Override public ClientApiWorkspaceDiff call() throws Exception { List<WorkspaceEntity> workspaceEntities = findEntitiesNoLock(workspace, true, user); List<Edge> workspaceEdges = toList(findEdges(workspace, workspaceEntities, true, user)); FormulaEvaluator.UserContext userContext = new FormulaEvaluator.UserContext(locale, timeZone, workspace.getWorkspaceId()); return workspaceDiff.diff(workspace, workspaceEntities, workspaceEdges, userContext, user); } }); } }