Java tutorial
/* Copyright (C) 2013-2015 Computer Sciences Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ezbake.groups.service; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.annotation.Nullable; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.inject.Inject; import ezbake.base.thrift.EzSecurityToken; import ezbake.base.thrift.EzSecurityTokenException; import ezbake.base.thrift.TokenType; import ezbake.base.thrift.metrics.MetricRegistryThrift; import ezbake.groups.graph.GroupsGraph; import ezbake.groups.graph.UserGroupPermissionsWrapper; import ezbake.groups.graph.exception.AccessDeniedException; import ezbake.groups.graph.exception.IndexUnavailableException; import ezbake.groups.graph.exception.InvalidGroupNameException; import ezbake.groups.graph.exception.InvalidVertexTypeException; import ezbake.groups.graph.exception.UserNotFoundException; import ezbake.groups.graph.exception.VertexExistsException; import ezbake.groups.graph.exception.VertexNotFoundException; import ezbake.groups.graph.frames.edge.BaseEdge; import ezbake.groups.graph.frames.vertex.BaseVertex; import ezbake.groups.graph.frames.vertex.Group; import ezbake.groups.graph.frames.vertex.User; import ezbake.groups.thrift.AllGroupMembers; import ezbake.groups.thrift.AuthorizationError; import ezbake.groups.thrift.AuthorizationException; import ezbake.groups.thrift.EzGroupOperationException; import ezbake.groups.thrift.EzGroupsConstants; import ezbake.groups.thrift.GroupInheritancePermissions; import ezbake.groups.thrift.GroupQueryError; import ezbake.groups.thrift.GroupQueryException; import ezbake.groups.thrift.GroupsRequest; import ezbake.groups.thrift.GroupsRequestResponse; import ezbake.groups.thrift.OperationError; import ezbake.groups.thrift.UserGroup; import ezbake.groups.thrift.UserGroupPermissions; import ezbake.groups.thrift.UserGroupsRequest; import ezbake.groups.thrift.UserType; import ezbake.security.client.EzSecurityTokenWrapper; import ezbake.security.client.EzbakeSecurityClient; import ezbake.security.common.core.EzSecurityTokenUtils; import ezbake.security.common.core.SecurityID; import ezbake.thrift.authentication.EzX509; import ezbake.util.AuditEvent; import ezbake.util.AuditEventType; import ezbake.util.AuditLogger; /** * This is the implementation of the EzGroups thrift service. It implements the EzBakeBaseThriftService, and can be run * with thrift runner if built into a jar with dependencies. */ public class EzGroupsService extends BaseGroupsService { /** * Value used to populate the map returned by getGroupNamesByIndices when a name cannot be returned. */ public static final String UNABLE_TO_RETRIEVE_GROUP_NAME = "<NOT_FOUND>"; private static final Logger logger = LoggerFactory.getLogger(EzGroupsService.class); private static final AuditLogger auditLogger = AuditLogger.getAuditLogger(EzGroupsService.class); /** * Predicate for filtering out the root group. */ private static final Predicate<Group> IS_ROOT_GROUP = new Predicate<Group>() { @Override public boolean apply(@Nullable Group input) { return input != null && input.getGroupName().equals(EzGroupsConstants.ROOT); } }; /* Instance variables */ private final GroupsGraph graph; @Inject public EzGroupsService(Properties configuration, GroupsGraph graph, EzbakeSecurityClient ezbakeSecurityClient) { super(configuration, ezbakeSecurityClient); this.graph = graph; } public GroupsGraph getGraph() { return graph; } @Override public boolean ping() { logger.info("received ping. hello??"); return true; } @Override public MetricRegistryThrift getMetricRegistryThrift() { return new MetricRegistryThrift(); } @Override public long createGroup(EzSecurityToken ezSecurityToken, String parent, String name, GroupInheritancePermissions inheritance) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { return createGroupWithInclusion(ezSecurityToken, parent, name, inheritance, true, false); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public ezbake.groups.thrift.Group createAndGetGroup(EzSecurityToken ezSecurityToken, String parent, String name, GroupInheritancePermissions inheritance) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { return createAndGetGroupWithInclusion(ezSecurityToken, parent, name, inheritance, true, false); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public long createGroupWithInclusion(EzSecurityToken ezSecurityToken, String parent, String name, GroupInheritancePermissions inheritance, boolean includeOnlyRequiresUser, boolean includeOnlyRequiresApp) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { return createAndGetGroupWithInclusion(ezSecurityToken, parent, name, inheritance, includeOnlyRequiresUser, includeOnlyRequiresApp).getId(); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public ezbake.groups.thrift.Group createAndGetGroupWithInclusion(EzSecurityToken ezSecurityToken, String parent, String name, GroupInheritancePermissions inheritance, boolean includeOnlyRequiresUser, boolean includeOnlyRequiresApp) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { logger.debug( "createGroup request. Initiator: {}, Parent Group: {}, Group Name: {}, Inheritance options: {}", ezSecurityToken.getValidity().getIssuedTo(), parent, name, inheritance); validateToken(ezSecurityToken); // Information about the user creating the group final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtGroupRoleAdd, ezSecurityToken) .arg("Parent group", parent).arg("New group", name).arg("Group inheritance", inheritance) .arg("always include group if user has access", includeOnlyRequiresUser) .arg("always include group if app has access", includeOnlyRequiresApp); // Always add root to the parent groups if (Strings.isNullOrEmpty(parent) || parent.trim().isEmpty()) { parent = EzGroupsConstants.ROOT; } else { parent = Joiner.on(EzGroupsConstants.GROUP_NAME_SEP).join(EzGroupsConstants.ROOT, parent); } final String parentWithoutRoot = nameHelper.removeRootGroupPrefix(parent); try { // Create the group final Group group = graph.addGroup(userTypeFromToken(ezSecurityToken), principal, name, parent, inheritance, UserGroupPermissionsWrapper.ownerPermissions(), includeOnlyRequiresUser, includeOnlyRequiresApp); event.arg("Assigned group index", group.getIndex()); // Create a thrift group from our group-vertex and return it final ezbake.groups.thrift.Group tGroup = internalGroupToThriftGroup(group, inheritance); graph.commitTransaction(); return tGroup; } catch (final VertexNotFoundException e) { event.failed(); logger.error("Cannot create group, the parent vertex doesn't exist! parent name: {}, group: {}", parentWithoutRoot, name); throw new EzGroupOperationException( String.format("Parent group (%s) can not be found, unable to create child " + "group", parentWithoutRoot), OperationError.PARENT_GROUP_NOT_FOUND); } catch (final VertexExistsException e) { event.failed(); logger.error("Cannot create group, the vertex already exists! group name: {}", name); throw new EzGroupOperationException("Group already exists", OperationError.GROUP_EXISTS); } catch (final UserNotFoundException e) { event.failed(); logger.error("Cannot create group, the owner could not be found! owner name: {}", principal); throw new AuthorizationException( "Group owner does not exist. The user must be added to EzGroups first", AuthorizationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.failed(); logger.error( "Cannot create group, the owner does not have permission to create a child group! owner " + "name: {}", principal); throw new AuthorizationException( "User does not have permissions to create groups from parent group: " + parentWithoutRoot, AuthorizationError.ACCESS_DENIED); } catch (final IndexUnavailableException e) { event.failed(); logger.error("Cannot get an index for the group"); throw new EzGroupOperationException("Unable to create group at this time. No group ID available", OperationError.INDEX_UNAVAILABLE); } catch (final InvalidGroupNameException e) { event.failed(); logger.error("Create group with invalid group name: {}", e.getMessage()); throw new EzGroupOperationException("Invalid group name passed to createGroup: " + e.getMessage(), OperationError.UNRECOVERABLE_ERROR); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public long createUser(EzSecurityToken ezSecurityToken, String principal, String name) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken, true); logger.debug("createUser request. Initiator: {}, UserId: {}, UserName: {}", ezSecurityToken.getValidity().getIssuedTo(), principal, name); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtAdd, ezSecurityToken) .arg("Create user id", principal).arg("user name", name); long userId; try { final User user = graph.addUser(BaseVertex.VertexType.USER, principal, name); userId = user.getIndex(); graph.commitTransaction(); event.arg("Assigned group index to user", userId); } catch (final VertexExistsException e) { event.arg("User already exists", true); event.failed(); logger.error("Cannot create user, the vertex already exists! user name: {}", principal); throw new EzGroupOperationException("User with principal: " + principal + " already exists", OperationError.USER_EXISTS); } catch (InvalidVertexTypeException | InvalidGroupNameException e) { event.failed(); logger.error("Unexpected exception.", e); throw new EzGroupOperationException("Unable to create users at this time", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { event.failed(); logger.error("User not found: {}?", principal); throw new EzGroupOperationException("User not found creating app group for user: " + principal + "the app " + "group will need to be created manually", OperationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.arg("Creating user does have permission to create groups", true); event.failed(); logger.error("User does not have permission to create groups"); throw new AuthorizationException("User does not have permissions for creating groups. This usually " + "means that an app group cannot be created", AuthorizationError.ACCESS_DENIED); } catch (final IndexUnavailableException e) { event.arg("Failed setting group index", true); event.failed(); logger.error("Cannot get an index for the user"); throw new EzGroupOperationException("Unable to create users at this time. No ID available", OperationError.INDEX_UNAVAILABLE); } finally { auditLogger.logEvent(event); } return userId; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void modifyUser(EzSecurityToken ezSecurityToken, String principal, String newPrincipal) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("modifyUser request. Initiator: {}, Old Id: {}, New Id: {}", ezSecurityToken.getValidity().getIssuedTo(), principal, newPrincipal); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("change user principal", principal).arg("to new principal", newPrincipal); try { modifyUserOfType(BaseVertex.VertexType.USER, principal, newPrincipal, null); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void deactivateUser(EzSecurityToken ezSecurityToken, String principal) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("deactivateUser request. Initiator: {}, User Id: {}", ezSecurityToken.getValidity().getIssuedTo(), principal); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("deactivate user", principal); try { deactivateUserOfType(BaseVertex.VertexType.USER, principal); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void activateUser(EzSecurityToken ezSecurityToken, String principal) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("activateUser request. Initiator: {}, User Id: {}", ezSecurityToken.getValidity().getIssuedTo(), principal); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("activate user", principal); try { activateUserOfType(BaseVertex.VertexType.USER, principal); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void deleteUser(EzSecurityToken ezSecurityToken, String principal) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("deleteUser request. Initiator: {}, User Id: {}", ezSecurityToken.getValidity().getIssuedTo(), principal); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtDelete, ezSecurityToken) .arg("delete user", principal); try { deleteUserOfType(BaseVertex.VertexType.USER, principal); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public long createAppUser(EzSecurityToken ezSecurityToken, String securityID, String name) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken, true); logger.debug("createAppUser request. Initiator: {}, Security Id: {}, App Name: {}", ezSecurityToken.getValidity().getIssuedTo(), securityID, name); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtAdd, ezSecurityToken) .arg("Create app user id", securityID).arg("app name", name); try { // Create the app user. root.app.name and root.appaccess.name will be created final User appUser = graph.addUser(BaseVertex.VertexType.APP_USER, securityID, name); final String appGroup = nameHelper.getNamespacedAppGroup(name, null); // Also create root.app.name.ezbAudits, root.app.name.ezbMetrics, root.app.name.ezbDiagnostics graph.addGroup(BaseVertex.VertexType.APP_USER, securityID, EzGroupsConstants.AUDIT_GROUP, appGroup, new GroupInheritancePermissions(false, false, false, false, false), new UserGroupPermissionsWrapper(false, true, true, true, true), true, false); graph.addGroup(BaseVertex.VertexType.APP_USER, securityID, EzGroupsConstants.METRICS_GROUP, appGroup, new GroupInheritancePermissions(false, false, false, false, false), UserGroupPermissionsWrapper.ownerPermissions(), true, false); graph.addGroup(BaseVertex.VertexType.APP_USER, securityID, EzGroupsConstants.DIAGNOSTICS_GROUP, appGroup, new GroupInheritancePermissions(false, false, false, false, false), UserGroupPermissionsWrapper.ownerPermissions(), true, false); event.arg("Assigned App Group Index", appUser.getIndex()); return appUser.getIndex(); } catch (final VertexExistsException e) { event.arg("Already exists", true); event.failed(); logger.error("Cannot create user, the vertex already exists! user name: {}. Original Exception: {}", securityID, e.getMessage()); throw new EzGroupOperationException("User with principal: " + securityID + " already exists", OperationError.USER_EXISTS); } catch (final InvalidVertexTypeException e) { event.failed(); logger.error("Invalid vertex type, this should not happen"); throw new EzGroupOperationException("Unable to create users at this time", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { event.failed(); logger.error("User not found? Most likely when creating app group: {}", securityID); throw new EzGroupOperationException("User not found creating app group for user: " + securityID, OperationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.arg("User does not have permission to create groups", true); event.failed(); logger.error("User does not have permission to create groups"); throw new AuthorizationException("User does not have permissions for creating groups. This usually " + "means the app group cannot be created", AuthorizationError.ACCESS_DENIED); } catch (final VertexNotFoundException e) { event.arg("Unable to create groups for app user because parent group was not found", true); event.failed(); logger.error("Unable to create groups for app user because parent group was not found"); throw new EzGroupOperationException( "Unable to create groups for app user (" + securityID + ") : " + e.getMessage(), OperationError.PARENT_GROUP_NOT_FOUND); } catch (final IndexUnavailableException e) { event.arg("Failed setting group index", true); event.failed(); logger.error("Cannot get an index for the user"); throw new EzGroupOperationException("Unable to create users at this time. No ID available", OperationError.INDEX_UNAVAILABLE); } catch (final InvalidGroupNameException e) { event.arg("Unable to create app groups with app name", name); event.failed(); throw new EzGroupOperationException("Unable to create app users with no name", OperationError.UNRECOVERABLE_ERROR); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void modifyAppUser(EzSecurityToken ezSecurityToken, String securityId, String newSecurityID, String newName) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { validateToken(ezSecurityToken); logger.debug( "modifyAppUser request. Initiator: {}, Old Security Id: {}, New Security Id: {}, New Name: {}", ezSecurityToken.getValidity().getIssuedTo(), securityId, newSecurityID, newName); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("change user principal", securityId).arg("to new principal", newSecurityID) .arg("New name", newName); try { // If not an admin, must have admin manage access to the app group if (!EzSecurityTokenUtils.isEzAdmin(ezSecurityToken)) { // Query for app_user to determine app name final User app; try { app = getUser(BaseVertex.VertexType.APP_USER, securityId); } catch (final UserNotFoundException e) { throw new EzGroupOperationException("No app found with security id: " + securityId, OperationError.USER_NOT_FOUND); } final String appGroup = nameHelper.getNamespacedAppGroup(app.getName()); ensureUserHasAdminGroup(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal(), appGroup, BaseEdge.EdgeType.A_MANAGE, false); } modifyUserOfType(BaseVertex.VertexType.APP_USER, securityId, newSecurityID, newName); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void deactivateAppUser(EzSecurityToken ezSecurityToken, String securityId) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("deactivateAppUser request. Initiator: {}, Security Id: {}", ezSecurityToken.getValidity().getIssuedTo(), securityId); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("deactivate app user", securityId); try { deactivateUserOfType(BaseVertex.VertexType.APP_USER, securityId); } catch (final EzGroupOperationException e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void activateAppUser(EzSecurityToken ezSecurityToken, String securityId) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken, true); logger.debug("activateAppUser request. Initiator: {}, Security Id: {}", ezSecurityToken.getValidity().getIssuedTo(), securityId); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("activate app user", securityId); try { activateUserOfType(BaseVertex.VertexType.APP_USER, securityId); } catch (final EzGroupOperationException e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void deleteAppUser(EzSecurityToken ezSecurityToken, String securityId) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { validateToken(ezSecurityToken); logger.debug("deleteAppUser request. Initiator: {}, Security Id: {}", ezSecurityToken.getValidity().getIssuedTo(), securityId); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtDelete, ezSecurityToken) .arg("delete app user", securityId); try { // First rename the app user, which will rename all of the app groups that were created for it modifyAppUser(ezSecurityToken, securityId, securityId, "_DELETED_APP_" + securityId); // Now delete the user deleteUserOfType(BaseVertex.VertexType.APP_USER, securityId); } catch (final Exception e) { event.arg("error", e.getMessage()); event.failed(); throw e; } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get a set of all users who belong to a particular group * * @param ezSecurityToken token representing the user * @param groupName name of the group to query * @param explicitMembers only return members who belong to a group directly * @return a set of user external IDs */ @Override public AllGroupMembers getGroupMembers(EzSecurityToken ezSecurityToken, String groupName, boolean explicitMembers) throws EzSecurityTokenException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getGroupMembers request. Initiator: {}, groupName: {}, onlyExplicitMembers: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, explicitMembers); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); return getGroupMembers(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal(), realGroupName, true, true, explicitMembers); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get a set of all application users who belong to a particular group * * @param ezSecurityToken token representing the user * @param groupName name of the group to query * @param explicitMembers only return members who belong to a group directly * @return a set of user external IDs */ @Override public Set<String> getGroupApps(EzSecurityToken ezSecurityToken, String groupName, boolean explicitMembers) throws EzSecurityTokenException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getGroupApps request. Initiator: {}, groupName: {}, onlyExplicitMembers: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, explicitMembers); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); return getGroupMembers(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal(), realGroupName, false, true, explicitMembers) .getApps(); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get a set of all users who belong to a particular group * * @param ezSecurityToken token representing the user * @param groupName name of the group to query * @param explicitMembers only return members who belong to a group directly * @return a set of user external IDs */ @Override public Set<String> getGroupUsers(EzSecurityToken ezSecurityToken, String groupName, boolean explicitMembers) throws EzSecurityTokenException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getGroupUsers request. Initiator: {}, groupName: {}, onlyExplicitMembers: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, explicitMembers); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); return getGroupMembers(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal(), realGroupName, true, false, explicitMembers) .getUsers(); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public Set<Long> getAuthorizations(EzSecurityToken ezSecurityToken) throws EzSecurityTokenException, AuthorizationException, GroupQueryException { try { validateToken(ezSecurityToken); return graph.getAuthorizations(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal(), requestChainFromToken(ezSecurityToken)); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Returns all groups an user will have data access to when making a request with the given token. The app the token * was issued to is taken into consideration when determining the returned groups. * * @param token security token used to determine the groups to which an user has data access when making requests * through the app this token was issued to. * @param explicitGroups currently not implemented, this flag would restrict the return values to only those groups * to which the user has direct data access * @return a set of UserGroups containing information, including ID and name, of the groups to which an user has * data access * @throws EzSecurityTokenException if EzGroups is unable to validate the token * @throws AuthorizationException may be thrown if authorization is denied to the user for this operation * @throws GroupQueryException may be thrown if there is problem executing the query */ @Override public Set<UserGroup> getUserGroups(EzSecurityToken token, boolean explicitGroups) throws GroupQueryException, EzSecurityTokenException { try { logger.debug("getUserGroups request. Initiator: {}, User: {}", token.getValidity().getIssuedTo(), token.getTokenPrincipal().getPrincipal()); validateToken(token); final String userId = token.getTokenPrincipal().getPrincipal(); final String requestApp = token.getValidity().getIssuedTo(); // First get all user and apps groups final Set<Group> userGroups = getUserGroups(BaseVertex.VertexType.USER, userId, true); final Set<Group> appGroups = getUserGroups(BaseVertex.VertexType.APP_USER, requestApp, true); // Split groups into 2 sets - those that users always have (even if app doesn't) and those that users // only have if app has too final Set<Group> groupsUserHasRegardless = Sets.newHashSet(); Set<Group> groupsDependingOnApp = Sets.newHashSet(); for (final Group g : userGroups) { if (g.isRequireOnlyUser()) { groupsUserHasRegardless.add(g); } else { groupsDependingOnApp.add(g); } } // Get app groups that go in regardless final Set<Group> appAlwaysGetsGroups = Sets.newHashSet(); for (final Group g : appGroups) { if (g.isRequireOnlyApp()) { appAlwaysGetsGroups.add(g); } } // Filter the groups that depend on the app if (!groupsDependingOnApp.isEmpty()) { groupsDependingOnApp = Sets.intersection(groupsDependingOnApp, appGroups); } // Now union the sets to get the users final list Set<Group> groups = Sets.union(groupsUserHasRegardless, groupsDependingOnApp); if (!appAlwaysGetsGroups.isEmpty()) { groups = Sets.union(groups, appAlwaysGetsGroups); } // Transform final Set<UserGroup> ugs = getUserGroupsInfo(userId, groups); graph.commitTransaction(); return ugs; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Gets permissions on a set of groups for a particular user and packages them in an UserGroup object with the * corresponding group. * * @param userId ID of the user to get group permissions for * @param groups groups for which to get user permissions * @return a set of UserGroup; UserGroup includes a group packaged with an users permissions on that group. */ private Set<UserGroup> getUserGroupsInfo(String userId, Set<Group> groups) { final Set<UserGroup> ugs = new HashSet<>(); for (final Group g : groups) { GroupInheritancePermissions inheritancePermissions = null; try { inheritancePermissions = graph.getGroupInheritancePermissions(g.getGroupName()); } catch (final VertexNotFoundException e) { logger.error(g.getGroupName(), e); } final ezbake.groups.thrift.Group group = internalGroupToThriftGroup(g, inheritancePermissions); Set<BaseEdge.EdgeType> edges = null; try { edges = graph.userPermissionsOnGroup(BaseVertex.VertexType.USER, userId, g.getGroupName()); } catch (UserNotFoundException | VertexNotFoundException e) { // ignore. We just won't have permissions } final UserGroupPermissions ugp = convertEdgesToUserGroupPermissions(edges); ugs.add(new UserGroup(group, ugp)); } return ugs; } /** * Takes a set of edges and converts their labels to an UserGroupPermissions object. Certain edge labels correspond * to certain group permissions. If those labels are detected in the set of edges then the field for that permission * on the returned UserGroupPermissions object will be set to true. It is expected that this set of edges will be * built from the set of edges from {@link GroupsGraph#userPermissionsOnGroup (BaseVertex.VertexType, String, * String)}. * * @param edges Set of edges to convert an an UserGroupPermissions object * @return an UserGroupsPermissions object built from the set of given edges */ private static UserGroupPermissions convertEdgesToUserGroupPermissions(Set<BaseEdge.EdgeType> edges) { final UserGroupPermissions userGroupPermissions = new UserGroupPermissions(); for (final BaseEdge.EdgeType edge : edges) { switch (edge) { case DATA_ACCESS: userGroupPermissions.setDataAccess(true); break; case A_CREATE_CHILD: userGroupPermissions.setAdminCreateChild(true); break; case A_MANAGE: userGroupPermissions.setAdminManage(true); break; case A_READ: userGroupPermissions.setAdminRead(true); break; case A_WRITE: userGroupPermissions.setAdminWrite(true); break; } } return userGroupPermissions; } /** * Request groups to which an user has data access. Requester must be an EzBake Admin and all groups to which an * user has data access will be returned. * * @param token EzSecurityToken user to determine if requester is authorized to perform this action * @param userGroupsRequest information needed to carry about the request for the users groups * @return a set of UserGroups containing information, including ID and name, of the groups to which an user has * data access * @throws EzSecurityTokenException thrown if EzGroups is unable to validate the token * @throws AuthorizationException thrown if the requester is not an EzBake Admin or authorization is denied * @throws GroupQueryException may be thrown if there is problem executing the query */ @Override public Set<UserGroup> requestUserGroups(EzSecurityToken token, UserGroupsRequest userGroupsRequest) throws EzSecurityTokenException, AuthorizationException, GroupQueryException, TException { try { validateToken(token); if (!EzSecurityTokenUtils.isEzAdmin(token)) { final String errMsg = String.format( "User '%s' not an EzBake Admin. Must be EzBake Admin to request groups for an user!", token.getTokenPrincipal().getPrincipal()); logger.error(errMsg); throw new AuthorizationException(errMsg, AuthorizationError.ACCESS_DENIED); } final String id = userGroupsRequest.getIdentifier(); final Set<Group> groupsUserBelongsTo = getUserGroups(BaseVertex.VertexType.USER, id, true); // root group should not be returned to the user Iterables.removeIf(groupsUserBelongsTo, IS_ROOT_GROUP); final Set<UserGroup> groups = getUserGroupsInfo(id, groupsUserBelongsTo); graph.commitTransaction(); return groups; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get the app names for which the user given in the EzSecurity token is a diagnostic user * * @param ezSecurityToken a USER token issued to the user of interest * @return a set of the application names the user has diagnostic access to */ @Override public Set<String> getUserDiagnosticApps(EzSecurityToken ezSecurityToken) throws EzSecurityTokenException, AuthorizationException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getUserDiagnosticApps request. Initiator: {}, User: {}", ezSecurityToken.getValidity().getIssuedTo(), ezSecurityToken.getTokenPrincipal().getPrincipal()); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); return querySpecialGroupForApps(userTypeFromToken(ezSecurityToken), principal, EzGroupsConstants.DIAGNOSTICS_GROUP); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get the app names for which the user given in the EzSecurity token is a metrics user * * @param ezSecurityToken a USER token issued to the user of interest * @return a set of the application names the user has metrics access to */ @Override public Set<String> getUserMetricsApps(EzSecurityToken ezSecurityToken) throws EzSecurityTokenException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getUserMetricsApps request. Initiator: {}, User: {}", ezSecurityToken.getValidity().getIssuedTo(), ezSecurityToken.getTokenPrincipal().getPrincipal()); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); return querySpecialGroupForApps(userTypeFromToken(ezSecurityToken), principal, EzGroupsConstants.METRICS_GROUP); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get the app names for which the user given in the EzSecurity token is an audit user * * @param ezSecurityToken a USER token issued to the user of interest * @return a set of the application names the user has auditor access to */ @Override public Set<String> getUserAuditorApps(EzSecurityToken ezSecurityToken) throws EzSecurityTokenException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("getUserAuditorApps request. Initiator: {}, User: {}", ezSecurityToken.getValidity().getIssuedTo(), ezSecurityToken.getTokenPrincipal().getPrincipal()); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); return querySpecialGroupForApps(userTypeFromToken(ezSecurityToken), principal, EzGroupsConstants.AUDIT_GROUP); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public boolean checkUserAccessToGroup(EzSecurityToken ezSecurityToken, String groupName) throws EzSecurityTokenException, AuthorizationException, GroupQueryException { try { validateToken(ezSecurityToken); logger.debug("checkUserAccsesToGroup request. Initiator: {}, User: {}, Group: {}", ezSecurityToken.getValidity().getIssuedTo(), ezSecurityToken.getTokenPrincipal().getPrincipal(), groupName); final EzSecurityTokenWrapper wrapper = new EzSecurityTokenWrapper(ezSecurityToken); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); // First get the user and app user vertex final User user; final User appUser; try { user = graph.getUser(userTypeFromToken(ezSecurityToken), ezSecurityToken.getTokenPrincipal().getPrincipal()); appUser = graph.getUser(BaseVertex.VertexType.APP_USER, wrapper.getSecurityId()); } catch (InvalidVertexTypeException | UserNotFoundException e) { logger.info("Unable to get user: {}", e.getMessage()); graph.commitTransaction(true); throw new GroupQueryException("Unable to get user: " + e.getMessage(), GroupQueryError.USER_NOT_FOUND); } // Now get the group and check both accesses final Group group; try { group = graph.getGroup(realGroupName); } catch (final VertexNotFoundException e) { logger.info("Group does not exist to check access: {}", groupName); graph.commitTransaction(true); throw new GroupQueryException("Group not found: " + groupName, GroupQueryError.GROUP_NOT_FOUND); } // Determine user access. User must have access, and if not require only user check app, // give unconditionally // if require only app boolean userAccess = graph.pathExists(user.asVertex(), group.asVertex(), BaseEdge.EdgeType.DATA_ACCESS.toString()); if (!group.isRequireOnlyUser() || group.isRequireOnlyApp()) { final boolean appAccess = graph.pathExists(appUser.asVertex(), group.asVertex(), BaseEdge.EdgeType.DATA_ACCESS.toString()); if (group.isRequireOnlyApp()) { userAccess = appAccess; } else if (!group.isRequireOnlyUser()) { userAccess = userAccess && appAccess; } } graph.commitTransaction(); return userAccess; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Return a set of UserGroups, which include group information and the user's group permissions * * @param token a token granting access to EzGroups * @param explicitGroups whether or not to fetch all user groups, or just the ones they have direct membership to * @return a set of all the apps groups */ @Override public Set<UserGroup> getAppUserGroups(EzSecurityToken token, boolean explicitGroups) throws EzSecurityTokenException, GroupQueryException { try { validateToken(token); logger.debug("getAppUserGroups request. Initiator: {}, App User: {}, onlyExplicitGroups: {}", token.getValidity().getIssuedTo(), token.getTokenPrincipal().getPrincipal(), explicitGroups); final String securityId = token.getTokenPrincipal().getPrincipal(); final Set<UserGroup> ugs = new HashSet<>(); final Set<Group> groups = getUserGroups(BaseVertex.VertexType.APP_USER, securityId, true); for (final Group g : groups) { final ezbake.groups.thrift.Group group = new ezbake.groups.thrift.Group(g.getIndex(), g.getGroupName()); try { group.setInheritancePermissions(graph.getGroupInheritancePermissions(g.getGroupName())); } catch (final VertexNotFoundException e) { logger.error(g.getGroupName(), e); } group.setRequireOnlyUser(g.isRequireOnlyUser()); group.setRequireOnlyAPP(g.isRequireOnlyApp()); group.setGroupName(nameHelper.removeRootGroupPrefix(g.getGroupName())); // Figure out what the users permissions are final UserGroupPermissions ugp = new UserGroupPermissions(); try { final Set<BaseEdge.EdgeType> edges = graph .userPermissionsOnGroup(BaseVertex.VertexType.APP_USER, securityId, g.getGroupName()); for (final BaseEdge.EdgeType edge : edges) { switch (edge) { case DATA_ACCESS: ugp.setDataAccess(true); break; case A_CREATE_CHILD: ugp.setAdminCreateChild(true); break; case A_MANAGE: ugp.setAdminManage(true); break; case A_READ: ugp.setAdminRead(true); break; case A_WRITE: ugp.setAdminWrite(true); break; } } } catch (UserNotFoundException | VertexNotFoundException e) { // ignore. We just won't have permissions } ugs.add(new UserGroup(group, ugp)); } graph.commitTransaction(); return ugs; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * List the child groups from the specified parent. If null, all groups a user has access to will be returned * * @param ezSecurityToken a token granting access to EzGroups * @param groupName the group name from where the query should begin * @param recurse whether or not to return all children, or just direct child groups * @return a set of the child groups */ @Override public Set<ezbake.groups.thrift.Group> getChildGroups(EzSecurityToken ezSecurityToken, String groupName, boolean recurse) throws EzSecurityTokenException, GroupQueryException, AuthorizationException { try { logger.debug("getChildGroups request. Initiator: {}, Parent Group: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName); validateToken(ezSecurityToken); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final Set<ezbake.groups.thrift.Group> groupNames = Sets.newHashSet(); try { final Set<Group> groups = graph.getGroupChildren(userTypeFromToken(ezSecurityToken), principal, realGroupName, recurse); for (final Group g : groups) { final ezbake.groups.thrift.Group group = new ezbake.groups.thrift.Group(g.getIndex(), g.getGroupName()); try { group.setInheritancePermissions(graph.getGroupInheritancePermissions(g.getGroupName())); } catch (final VertexNotFoundException e) { logger.error(g.getGroupName(), e); } group.setRequireOnlyUser(g.isRequireOnlyUser()); group.setRequireOnlyAPP(g.isRequireOnlyApp()); group.setIsActive(g.isActive()); group.setGroupName(nameHelper.removeRootGroupPrefix(g.getGroupName())); groupNames.add(group); } graph.commitTransaction(); } catch (final VertexNotFoundException e) { logger.error("No group found getting children of {}", groupName); throw new GroupQueryException("No group found for name: " + groupName, GroupQueryError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { logger.error("No user found getting children of {}, user id: {}", groupName, principal); throw new GroupQueryException("No user found for id: " + principal, GroupQueryError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { logger.error("User did not have required permissions for accessing group: {}", groupName); throw new AuthorizationException("User does not have access to group: " + groupName, AuthorizationError.ACCESS_DENIED); } return groupNames; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Add a user to a group, assigning them the specified direct permissions. Other permissions may still be inherited * * @param ezSecurityToken a token granting access to EzGroups * @param groupName the group to add the user to * @param securityId the app user's security Id * @param permissions the direct permissions the user should have */ @Override public void addAppUserToGroup(EzSecurityToken ezSecurityToken, String groupName, String securityId, UserGroupPermissions permissions) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken); logger.debug( "addAppUserToGroup request. Initiator: {}, Security Id: {}, GroupName: {}, Permissions: {}", ezSecurityToken.getValidity().getIssuedTo(), securityId, groupName, permissions); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtAdd, ezSecurityToken) .arg("add app user", securityId).arg("to group", groupName) .arg("with permissions", permissions); try { // First need to check the actors permissions on the group final boolean privileged = EzSecurityTokenUtils.isEzAdmin(ezSecurityToken) && isPrivilegedPeer(new EzX509(), SecurityID.ReservedSecurityId.INS_REG); ensureUserHasAdminGroup(userTypeFromToken(ezSecurityToken), principal, realGroupName, BaseEdge.EdgeType.A_WRITE, privileged); graph.addUserToGroup(BaseVertex.VertexType.APP_USER, securityId, realGroupName, permissions.isDataAccess(), permissions.isAdminRead(), permissions.isAdminWrite(), permissions.isAdminManage(), permissions.isAdminCreateChild()); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot add user to {}", groupName); throw new EzGroupOperationException("No group found for name: " + groupName, OperationError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { event.arg("User does not exist", securityId); event.failed(); logger.error("No user found. Cannot add user {} to {}", securityId, groupName); throw new EzGroupOperationException( "No user found, " + securityId + " will not be added to the group", OperationError.USER_NOT_FOUND); } catch (final InvalidVertexTypeException e) { event.failed(); logger.error("Invalid vertex type, this should not happen"); throw new EzGroupOperationException("Unable to add users to groups at this time", OperationError.UNRECOVERABLE_ERROR); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Add a user to a group, assigning them the specified direct permissions. Other permissions may still be inherited * * @param ezSecurityToken a token granting access to EzGroups * @param groupName the group to add the user to * @param userId the user's external id * @param permissions the direct permissions the user should have */ @Override public void addUserToGroup(EzSecurityToken ezSecurityToken, String groupName, String userId, UserGroupPermissions permissions) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken); logger.debug("addUserToGroup request. token: {}, group: {}, user: {}, permissions: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, userId, permissions); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtAdd, ezSecurityToken) .arg("add user", userId).arg("to group", groupName).arg("with permissions", permissions); try { // First need to check the actors permissions on the group final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final boolean privileged = EzSecurityTokenUtils.isEzAdmin(ezSecurityToken) || isPrivilegedPeer(new EzX509(), SecurityID.ReservedSecurityId.INS_REG); ensureUserHasAdminGroup(userTypeFromToken(ezSecurityToken), principal, realGroupName, BaseEdge.EdgeType.A_WRITE, privileged); graph.addUserToGroup(BaseVertex.VertexType.USER, userId, realGroupName, permissions.isDataAccess(), permissions.isAdminRead(), permissions.isAdminWrite(), permissions.isAdminManage(), permissions.isAdminCreateChild()); } catch (final VertexNotFoundException e) { event.arg("Group not found", groupName); event.failed(); logger.error("No group found. Cannot add user to {}", groupName); throw new EzGroupOperationException("No group found for name: " + groupName, OperationError.GROUP_NOT_FOUND); } catch (final InvalidVertexTypeException e) { event.arg("Invalid vertex type", userTypeFromToken(ezSecurityToken)); event.failed(); logger.error("Invalid vertex type, this should not happen"); throw new EzGroupOperationException("Unable to add users to groupsat this time", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { // Create the user now, and then add them logger.info("User being added does not exist. Creating the user, and then reattempting to add"); final AuditEvent addUserEvent = new AuditEvent(AuditEventType.UserGroupMgmtAdd, ezSecurityToken) .arg("Adding user", userId); try { graph.addUser(BaseVertex.VertexType.USER, userId, ""); graph.addUserToGroup(BaseVertex.VertexType.USER, userId, realGroupName, permissions.isDataAccess(), permissions.isAdminRead(), permissions.isAdminWrite(), permissions.isAdminManage(), permissions.isAdminCreateChild()); } catch (final AccessDeniedException e1) { addUserEvent.failed(); logger.error( "Access denied creating user. This should not have happened because it only happens for " + "APP_USERS, not USERS"); throw new EzGroupOperationException( "Unable to create user: " + userId + " reason: " + e1.getMessage(), OperationError.UNRECOVERABLE_ERROR); } catch (InvalidVertexTypeException | UserNotFoundException | VertexExistsException | InvalidGroupNameException e1) { addUserEvent.arg("Unable to create user", e1.getMessage()); addUserEvent.failed(); logger.error("Unable to create a user: {}", e1.getMessage()); throw new EzGroupOperationException( "Unable to create user: " + userId + " reason: " + e1.getMessage(), OperationError.UNRECOVERABLE_ERROR); } catch (final IndexUnavailableException e1) { addUserEvent.arg("Failed setting group index", true); addUserEvent.failed(); logger.error("Cannot get an index for the user"); throw new EzGroupOperationException("Unable to create users at this time. No ID available", OperationError.INDEX_UNAVAILABLE); } catch (final VertexNotFoundException e1) { addUserEvent.arg("Group not found", groupName); addUserEvent.failed(); logger.error("No group found. Cannot add user to {}", groupName); throw new EzGroupOperationException("No group found for name: " + groupName, OperationError.GROUP_NOT_FOUND); } finally { auditLogger.logEvent(addUserEvent); } } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void deactivateGroup(EzSecurityToken ezSecurityToken, String groupName, boolean andChildren) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken); logger.debug("deactivateGroup request. Initiator: {}, Group: {}, Recursive: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, andChildren); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("Deactivate group", groupName).arg("Deactivate child groups, too", andChildren); try { graph.setGroupActiveOrNot(userTypeFromToken(ezSecurityToken), principal, nameHelper.addRootGroupPrefix(groupName), false, andChildren); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot deactivate group {}", groupName); throw new EzGroupOperationException("Unable to deactivate group:" + groupName, OperationError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { event.arg("User does not exist", groupName); event.failed(); logger.error("User not found. Cannot deactivate group {}", groupName); throw new EzGroupOperationException("Unable to deactivate group:" + groupName, OperationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.arg("User does not have permission to deactivate", groupName); event.failed(); logger.error("User does not have admin manage permissions on {}", groupName); throw new AuthorizationException("User does not have required permissions on group: " + groupName, AuthorizationError.ACCESS_DENIED); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void activateGroup(EzSecurityToken ezSecurityToken, String groupName, boolean andChildren) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken); logger.debug("activateGroup request. Initiator: {}, Group: {}, Recursive: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, andChildren); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("Activate group", groupName); try { graph.setGroupActiveOrNot(userTypeFromToken(ezSecurityToken), principal, nameHelper.addRootGroupPrefix(groupName), true, andChildren); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot activate group {}", groupName); throw new EzGroupOperationException("Unable to activate group:" + groupName, OperationError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { event.arg("User does not exist", groupName); event.failed(); logger.error("User not found. Cannot activate group {}", groupName); throw new EzGroupOperationException("Unable to activate group:" + groupName, OperationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.arg("User does not have permission to activate", groupName); event.failed(); logger.error("User does not have admin manage permissions on {}", groupName); throw new AuthorizationException("User does not have required permissions on group: " + groupName, AuthorizationError.ACCESS_DENIED); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void removeAppUserFromGroup(EzSecurityToken ezSecurityToken, String groupName, String securityId) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { validateToken(ezSecurityToken); logger.debug("removeAppUserFromGroup request. Initiator: {}, Group: {}, Security Id: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, securityId); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtDelete, ezSecurityToken) .arg("remove app user", securityId).arg("from group", groupName); try { // First need to check the actors permissions on the group final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final BaseVertex.VertexType ownerType = vertexTypeFromTokenType(ezSecurityToken.getType()); if (!graph.userPermissionsOnGroup(ownerType, principal, realGroupName) .contains(BaseEdge.EdgeType.A_WRITE)) { event.arg("permissions missing", BaseEdge.EdgeType.A_WRITE.toString()); event.failed(); logger.error( "Bad access. User: {} tried removing {} to group {}, " + "but is lacking admin manage permissions", principal, securityId, realGroupName); throw new AuthorizationException("This user cannot add other users to the group", AuthorizationError.ACCESS_DENIED); } graph.removeUserFromGroup(BaseVertex.VertexType.APP_USER, securityId, realGroupName); } catch (final UserNotFoundException e) { event.arg("User does not exist", securityId); event.failed(); logger.error("No user found. Cannot remove user {} from {}", securityId, realGroupName); throw new EzGroupOperationException("No app user found for id: " + securityId, OperationError.USER_NOT_FOUND); } catch (final InvalidVertexTypeException e) { event.failed(); logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException("Unable to modify users at this time", OperationError.UNRECOVERABLE_ERROR); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot remove user {} from {}", securityId, groupName); throw new EzGroupOperationException("No group found for name: " + groupName, OperationError.GROUP_NOT_FOUND); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Gets groups matching the parameters in the given GroupsRequest or all groups if no parameters are given. * * @param token EzSecurityToken used to determine the returned groups. Currently all groups can be returned to an * EzBake Admin, but an AuthorizationException will be thrown for any other requester. * @param requestInfo criteria for the returned results - currently unused * @return the requested groups * @throws EzSecurityTokenException if EzGroups is unable to validate the token * @throws AuthorizationException may be thrown if authorization is denied to the user for this operation * @throws GroupQueryException may be thrown if there is problem executing the query */ @Override public GroupsRequestResponse getGroups(EzSecurityToken token, GroupsRequest requestInfo) throws EzSecurityTokenException, AuthorizationException, GroupQueryException { try { validateToken(token); final GroupsRequestResponse response = new GroupsRequestResponse(); response.setRetrievedGroups(internalGroupsToThriftGroups(graph.getGroups())); return response; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void removeUserFromGroup(EzSecurityToken ezSecurityToken, String groupName, String userId) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { validateToken(ezSecurityToken); logger.debug("removeUserFromGroup request. Initiator: {}, Group: {}, User Id: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, userId); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtDelete, ezSecurityToken) .arg("remove user", userId).arg("from group", groupName); try { // First need to check the actors permissions on the group final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final BaseVertex.VertexType ownerType = vertexTypeFromTokenType(ezSecurityToken.getType()); if (!graph.userPermissionsOnGroup(ownerType, principal, realGroupName) .contains(BaseEdge.EdgeType.A_WRITE)) { event.arg("permissions missing", BaseEdge.EdgeType.A_WRITE.toString()); event.failed(); logger.error("Bad access. User: {} tried removing {} from group {}, " + "but is lacking admin manage permissions", principal, userId, realGroupName); throw new AuthorizationException("This user cannot add other users to the group", AuthorizationError.ACCESS_DENIED); } graph.removeUserFromGroup(BaseVertex.VertexType.USER, userId, realGroupName); } catch (final UserNotFoundException e) { event.arg("User does not exist", userId); event.failed(); logger.error("No user found. Cannot remove user {} from {}", userId, realGroupName); throw new EzGroupOperationException("No user found for id: " + userId, OperationError.USER_NOT_FOUND); } catch (final InvalidVertexTypeException e) { event.failed(); logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException("Unable to modify users at this time", OperationError.UNRECOVERABLE_ERROR); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot remove user {} from {}", userId, groupName); throw new EzGroupOperationException("No group found for name: " + groupName, OperationError.GROUP_NOT_FOUND); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void changeGroupInheritance(EzSecurityToken ezSecurityToken, String groupName, GroupInheritancePermissions inheritance) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException { try { validateToken(ezSecurityToken); logger.debug("changeGroupInheritance request. Initiator: {}, Group: {}, New inheritance: {}", ezSecurityToken.getValidity().getIssuedTo(), groupName, inheritance); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtGroupRoleModify, ezSecurityToken) .arg("Group name", groupName).arg("Set Group Ineritance", inheritance); try { // First need to check the actors permissions on the group final BaseVertex.VertexType ownerType = vertexTypeFromTokenType(ezSecurityToken.getType()); if (!graph.userPermissionsOnGroup(ownerType, principal, realGroupName) .contains(BaseEdge.EdgeType.A_MANAGE)) { event.arg("permissions missing", BaseEdge.EdgeType.A_MANAGE.toString()); event.failed(); logger.error( "Bad access. User: {} tried removing {} from group {}, " + "but is lacking admin manage permissions", principal, principal, realGroupName); throw new AuthorizationException("This user cannot add other users to the group", AuthorizationError.ACCESS_DENIED); } graph.setGroupInheritance(realGroupName, inheritance.isDataAccess(), inheritance.isAdminRead(), inheritance.isAdminWrite(), inheritance.isAdminManage(), inheritance.isAdminCreateChild()); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", groupName); event.failed(); logger.error("No group found. Cannot manage inheritance! group {}", groupName); throw new EzGroupOperationException("Unable to manage group inheritance for " + groupName, OperationError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { event.arg("User does not exist", principal); event.failed(); logger.error("No user found: {}", principal); throw new EzGroupOperationException("No user found: " + principal, OperationError.USER_NOT_FOUND); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public void changeGroupName(EzSecurityToken ezSecurityToken, String oldFullyQualifiedName, String newFriendlyName) throws EzSecurityTokenException, EzGroupOperationException, AuthorizationException { try { validateToken(ezSecurityToken); logger.debug("changeGroupName request. Initiator: {}, Old Name: {}, New Name: {}", ezSecurityToken.getValidity().getIssuedTo(), oldFullyQualifiedName, newFriendlyName); final String realGroupName = nameHelper.addRootGroupPrefix(oldFullyQualifiedName); final String principal = ezSecurityToken.getTokenPrincipal().getPrincipal(); final AuditEvent event = new AuditEvent(AuditEventType.UserGroupMgmtModify, ezSecurityToken) .arg("Group name", oldFullyQualifiedName).arg("new friendly group name", newFriendlyName); final BaseVertex.VertexType ownerType = vertexTypeFromTokenType(ezSecurityToken.getType()); try { // First need to check the actors permissions on the group, fail fast if they don't have access if (!graph.userPermissionsOnGroup(ownerType, principal, realGroupName) .contains(BaseEdge.EdgeType.A_MANAGE)) { event.arg("permissions missing", BaseEdge.EdgeType.A_MANAGE.toString()); event.failed(); final String errMsg = String.format("Bad access. User: %s tried changing group name for %s, " + "but is lacking admin manage permissions", principal, realGroupName); logger.error(errMsg); throw new AuthorizationException(errMsg, AuthorizationError.ACCESS_DENIED); } Map<String, String> changedGroupNames = null; try { changedGroupNames = graph.changeGroupName(ownerType, principal, realGroupName, newFriendlyName); } catch (final VertexExistsException e) { final String errMsg = String.format( "Unable to update group '%s' with new friendly name '%s', group already exists!", oldFullyQualifiedName, newFriendlyName); logger.error(errMsg, e); throw new EzGroupOperationException(errMsg, OperationError.GROUP_EXISTS); } event.arg("changed group names", changedGroupNames); } catch (final VertexNotFoundException e) { event.arg("Group does not exist", realGroupName); event.failed(); logger.error("No group found. Cannot manage inheritance! group {}", realGroupName); throw new EzGroupOperationException("Unable to manage group name for " + realGroupName, OperationError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { event.arg("User does not exist", principal); event.failed(); logger.error("No user found: {}", principal, e); throw new EzGroupOperationException("No user found: " + principal, OperationError.USER_NOT_FOUND); } catch (final AccessDeniedException e) { event.arg("permissions missing", BaseEdge.EdgeType.A_MANAGE.toString()); event.failed(); final String errMsg = String.format( "Bad access. User: %s tried changing group %s, but is lacking admin manage permissions to one" + " or " + "more of its child group(s).", principal, realGroupName); logger.error(errMsg, e); throw new AuthorizationException(errMsg, AuthorizationError.ACCESS_DENIED); } finally { auditLogger.logEvent(event); } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public Set<Long> getGroupsMask(EzSecurityToken ezSecurityToken, Set<String> groupNames, Set<String> ezbakeUserIds, Set<String> ezbakeAppIds) throws EzSecurityTokenException, EzGroupOperationException { try { validateToken(ezSecurityToken); // Get all the groups groupNames = nameHelper.addRootGroupPrefix(groupNames.toArray(new String[groupNames.size()])); final Set<Group> groups = graph.getGroups(groupNames); final Set<User> users = Sets.newHashSet(); if (ezbakeUserIds != null) { users.addAll(graph.getUsers(BaseVertex.VertexType.USER, ezbakeUserIds)); } if (ezbakeAppIds != null) { users.addAll(graph.getUsers(BaseVertex.VertexType.APP_USER, ezbakeAppIds)); } final Set<Long> mask = Sets.newTreeSet(); for (final Group group : groups) { final Long index = group.getIndex(); if (index != null) { mask.add(index); } } for (final User user : users) { final Long index = user.getIndex(); if (index != null) { mask.add(index); } } graph.commitTransaction(); return mask; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public Set<Long> createUserAndGetAuthorizations(EzSecurityToken token, List<String> chain, String id, String name) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException, GroupQueryException { logger.debug( "createUserAndGetAuthorizations - requesting app: {}, request chain: {}, user id: {}, user name: {}", token.getValidity().getIssuedTo(), chain, id, name); try { validatePrivilegedPeer(token, new EzX509()); try { return getUserAuthorizations(token, TokenType.USER, id, chain); } catch (final GroupQueryException e) { if (e.getErrorType() == GroupQueryError.USER_NOT_FOUND) { logger.info("User did not exist. Attempting to create"); createUser(token, id, name); return getUserAuthorizations(token, TokenType.USER, id, chain); } else { logger.error("createUserAndGetAuth Failure", e); throw e; } } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public Set<Long> createAppUserAndGetAuthorizations(EzSecurityToken token, List<String> chain, String securityId, String appName) throws EzSecurityTokenException, AuthorizationException, EzGroupOperationException, GroupQueryException { System.out.println("createAppUserAndGetAuthorizations test..."); try { logger.debug( "createAppUserAndGetAuthorizations - requesting app: {}, request chain: {}, security id: {}, " + "app name: {}", token.getValidity().getIssuedTo(), chain, securityId, appName); validatePrivilegedPeer(token, new EzX509()); try { return getUserAuthorizations(token, TokenType.APP, securityId, chain); } catch (final GroupQueryException e) { if (e.getErrorType() == GroupQueryError.USER_NOT_FOUND) { createAppUser(token, securityId, appName); return getUserAuthorizations(token, TokenType.APP, securityId, chain); } else { logger.error("createAppUserAndGetAuth Failure", e); throw e; } } } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get a set of longs containing all of the group IDs the user is a member of * * @param token a token granting access to EzGroups * @param userType the type of token this will be for, either USER or APP * @param userId the user for which groups will be queried * @param chain the chain of apps in the request * @return a set of longs with a bit set for every group the user is a member of */ @Override public Set<Long> getUserAuthorizations(EzSecurityToken token, TokenType userType, String userId, List<String> chain) throws EzSecurityTokenException, GroupQueryException { try { validatePrivilegedPeer(token, new EzX509()); logger.debug( "getUserAuthorizations request. Initiator: {}, UserType: {}, User Id: {}, Request chain: {}", token.getValidity().getIssuedTo(), userType, userId, chain); return graph.getAuthorizations(vertexTypeFromTokenType(userType), userId, chain); } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Get user details such as principal and whether or not user is active * * @param token - authorization token * @param userType - user type as defined in UserType enum * @param userId - usually DN of a user or security id for app user * @return User object */ @Override public ezbake.groups.thrift.User getUser(EzSecurityToken token, UserType userType, String userId) throws EzSecurityTokenException { try { validateToken(token); logger.debug("getUser request. Initiator: {}, UserType: {}, User Id: {}", token.getValidity().getIssuedTo(), userType, userId); final ezbake.groups.thrift.User result = new ezbake.groups.thrift.User(); try { final User user = graph.getUser(userTypeFromUserType(userType), userId); graph.commitTransaction(); return result.setIsActive(user.isActive()).setName(user.getName()) .setPrincipal(user.getPrincipal()); } catch (InvalidVertexTypeException | UserNotFoundException e) { logger.error("Failed to retrieve user {} details", userId); } return result; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } /** * Gets given group metadata, for example whether or not group is active. EzAdmin will have access to all groups, * while regular users will only be able to retrieve group metadata for groups which they have admin read access * to. * * @param token EzSecurityToken belonging to the party performing the query. The user must have admin_read on the * group referenced by groupName * @param groupName the group name for which metadata is being requested * @return group metadata for the requested group * @throws EzSecurityTokenException if the token cannot be validated */ @Override public ezbake.groups.thrift.Group getGroup(EzSecurityToken token, String groupName) throws EzSecurityTokenException { try { validateToken(token); logger.debug("getGroup request. Initiator: {}, Group name: {}", token.getValidity().getIssuedTo(), groupName); final String realGroupName = nameHelper.addRootGroupPrefix(groupName); ezbake.groups.thrift.Group result = new ezbake.groups.thrift.Group(); try { Group group = null; if (EzSecurityTokenUtils.isEzAdmin(token)) { group = graph.getGroup(realGroupName); } else { group = graph.getGroup(vertexTypeFromTokenType(token.getType()), token.getTokenPrincipal().getPrincipal(), realGroupName); } result = internalGroupToThriftGroup(group, graph.getGroupInheritancePermissions(group.getGroupName())); graph.commitTransaction(); } catch (final Exception e) { logger.error("Failed to get group {}. Failed with exception: {}", realGroupName, e.getMessage()); } return result; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } @Override public Map<Long, String> getGroupNamesByIndices(EzSecurityToken token, Set<Long> groupIndices) throws EzSecurityTokenException, GroupQueryException { try { validateToken(token); logger.debug("getGroupNamesByIndices request. Initiator: {}, User: {}, IDs: {}", token.getValidity().getIssuedTo(), token.getTokenPrincipal().getPrincipal(), groupIndices); final EzSecurityTokenWrapper wrapper = new EzSecurityTokenWrapper(token); final String userId = token.getTokenPrincipal().getPrincipal(); final String appId = token.getValidity().getIssuedTo(); Set<Group> groupsToReturn = null; Map<Long, String> groupNames = null; try { if (wrapper.isEzAdmin()) { groupsToReturn = graph.getGroupsByIds(groupIndices); } else { try { final Set<Group> userHasGroups = graph.getGroupsByIdsWithAuths(userTypeFromToken(token), userId, groupIndices); final Set<Group> appHasGroups = graph .getGroupsByIdsWithAuths(BaseVertex.VertexType.APP_USER, appId, groupIndices); // get the intersection of the available groups to the app and user // a group is returned if and only if both the app and the user have access groupsToReturn = Sets.intersection(userHasGroups, appHasGroups); } catch (final UserNotFoundException e) { final String errMsg = String.format( "Could not find APP_USER (%s) or USER (%s) referenced in token!", userId, appId); logger.error(errMsg, e); throw new GroupQueryException(errMsg, GroupQueryError.USER_NOT_FOUND); } catch (final InvalidVertexTypeException e) { // this should never happen final String errMsg = "Token points to an invalid user type! This should never happen!"; logger.error(errMsg, e); throw new GroupQueryException(errMsg, GroupQueryError.USER_NOT_FOUND); } } groupNames = getUnloadedGroupIndexToNameMap(groupIndices); for (final Group group : groupsToReturn) { groupNames.put(group.getIndex(), nameHelper.removeRootGroupPrefix(group.getGroupName())); } } finally { // free up resources; rolling back instead of committing because we *should* only be reading. graph.commitTransaction(true); } return groupNames; } catch (TException | RuntimeException e) { graph.commitTransaction(true); throw e; } finally { graph.commitTransaction(); } } private void ensureUserHasAdminGroup(BaseVertex.VertexType userType, String userId, String groupName, BaseEdge.EdgeType adminType, boolean privileged) throws AuthorizationException { String err = null; try { if (!graph.userPermissionsOnGroup(userType, userId, groupName).contains(adminType)) { err = userId + " does not have the required permission on: " + nameHelper.removeRootGroupPrefix(groupName); } } catch (UserNotFoundException | VertexNotFoundException e) { err = "user: " + userId + " does not exist. " + e.getMessage(); } if (err != null && !privileged) { logger.error("Bad access. User: {} does not have {} on group {} - {}", userId, adminType, groupName, err); throw new AuthorizationException(err, AuthorizationError.ACCESS_DENIED); } } private AllGroupMembers getGroupMembers(BaseVertex.VertexType userType, String userId, String groupName, boolean includeUsers, boolean includeApps, boolean explicit) throws GroupQueryException { final Set<String> userPrincipals = new HashSet<>(); final Set<String> appPrincipals = new HashSet<>(); try { final Set<User> users = graph.groupMembers(userType, userId, groupName, includeUsers, includeApps, explicit); for (final User user : users) { logger.debug("Group: {} has user: {} - {}", groupName, user.getPrincipal(), user.getIndex()); switch (user.getType()) { case USER: userPrincipals.add(user.getPrincipal()); break; case APP_USER: appPrincipals.add(user.getPrincipal()); break; } } graph.commitTransaction(); } catch (final VertexNotFoundException e) { logger.error("Group {} not found", groupName); throw new GroupQueryException("No group found with name: " + groupName, GroupQueryError.GROUP_NOT_FOUND); } catch (final UserNotFoundException e) { logger.error("User {}({}) not found", userId, userType); throw new GroupQueryException("No user found: " + userId + " (" + userType + ")", GroupQueryError.USER_NOT_FOUND); } final AllGroupMembers members = new AllGroupMembers(); members.setApps(appPrincipals); members.setUsers(userPrincipals); return members; } private User getUser(BaseVertex.VertexType type, String principal) throws UserNotFoundException { try { return graph.getUser(type, principal); } catch (final InvalidVertexTypeException e) { throw new UserNotFoundException("User was of invalid user type", e); } } /** * Get groups that an user is a member of optionally including inactive groups. Group membership is defined as * having data access to the group Group membership is represented on the graph by a path of outgoing DATA_ACCESS * edges from the user to the group. * * @param type type of user, USER or APP_USER * @param id EzBake ID of the user for which to get the groups they are a member of * @param includeInactive if inactive groups should be included in the results * @return the set of groups to which an user belongs, optionally including inactive groups * @throws GroupQueryException if the user groups are being requested for cannot be found */ private Set<Group> getUserGroups(BaseVertex.VertexType type, String id, boolean includeInactive) throws GroupQueryException { final Set<Group> gs; try { // Get the groups from the graph and add them to the set gs = graph.userGroups(type, id, false, includeInactive); } catch (final UserNotFoundException e) { logger.error("Cannot get user groups for {}. User Not found", id); throw new GroupQueryException("User " + id + " not found. Cannot compute group user groups", GroupQueryError.USER_NOT_FOUND); } return gs; } /** * Modify a user by updating their external ID * * @param type type of the user, app user or user * @param id the user id * @param newId the new user id * @param newName the new user name * @throws EzGroupOperationException */ private void modifyUserOfType(BaseVertex.VertexType type, String id, String newId, String newName) throws EzGroupOperationException { try { graph.updateUser(type, id, newId, newName); } catch (final InvalidVertexTypeException e) { logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException("Unable to modify users at this time", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { logger.error("Cannot update principal from {} -> {}. User Not found", id, newId); throw new EzGroupOperationException("User " + id + " not found. Cannot update principal", OperationError.USER_NOT_FOUND); } catch (final VertexNotFoundException e) { logger.error( "Cannot update principal from {} -> {} with new name: {}. Group was not found. Message: {} ", id, newId, newName, e.getMessage()); throw new EzGroupOperationException("Failed changing id:" + id + " to:" + newId + " with name:" + newName + ". Failed updating group names. " + e.getMessage(), OperationError.USER_NOT_FOUND); } catch (final VertexExistsException e) { final String errMsg = String.format("Cannot update principal from %s -> %s. User %s already exists", id, newId, newId); logger.error(errMsg); throw new EzGroupOperationException(errMsg, OperationError.USER_EXISTS); } } /** * Activate a user. The user will immediately become active * * @param type type of the user, app user or user * @param id the user id * @throws EzGroupOperationException */ private void activateUserOfType(BaseVertex.VertexType type, String id) throws EzGroupOperationException { try { graph.setUserActiveOrNot(type, id, true); } catch (final InvalidVertexTypeException e) { logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException( "Unable to activate users at this time. Internal error, invalid " + "user type", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { logger.error("Cannot activate user {} ({}). User Not found", id, type); throw new EzGroupOperationException("User " + id + " not found. Cannot activate the user", OperationError.USER_NOT_FOUND); } } /** * Deactivate a user, The user should not be granted any accesses from the service while inactive * * @param type type of the user, app user or user * @param id the user id * @throws EzGroupOperationException */ private void deactivateUserOfType(BaseVertex.VertexType type, String id) throws EzGroupOperationException { try { graph.setUserActiveOrNot(type, id, false); } catch (final InvalidVertexTypeException e) { logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException( "Unable to deactivate users at this time. Internal error, invalid " + "user type", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { logger.error("Cannot deactivate user from {}. User Not found", id); throw new EzGroupOperationException("User " + id + " not found. Cannot deactivate the user", OperationError.USER_NOT_FOUND); } } /** * Delete a user from the graph. This may leave the group orphaned * * @param type type of user to delete, app_user or user * @param id the user id * @throws EzGroupOperationException */ private void deleteUserOfType(BaseVertex.VertexType type, String id) throws EzGroupOperationException { try { graph.deleteUser(type, id); } catch (final InvalidVertexTypeException e) { logger.error("Invalid vertex type. Valid vertex types are BaseVertex.VertexType.USER and " + "BaseVertex.VertexType.APP_USER"); throw new EzGroupOperationException( "Unable to activate users at this time. Internal error, invalid " + "user type", OperationError.UNRECOVERABLE_ERROR); } catch (final UserNotFoundException e) { logger.error("Cannot delete user from {}. User Not found", id); throw new EzGroupOperationException("User " + id + " not found. Cannot delete the user", OperationError.USER_NOT_FOUND); } } private Set<String> querySpecialGroupForApps(BaseVertex.VertexType type, String id, String group) throws GroupQueryException { final Set<String> groups; try { final User user = graph.getUser(type, id); groups = graph.specialAppNamesQuery(group, user.asVertex()); graph.commitTransaction(); } catch (UserNotFoundException | InvalidVertexTypeException e) { logger.error("No user found id: {}", id, e); throw new GroupQueryException("No user found for id: " + id, GroupQueryError.USER_NOT_FOUND); } catch (final VertexNotFoundException e) { logger.error("App Group not found", e); throw new GroupQueryException("Internal Error", GroupQueryError.GROUP_NOT_FOUND); } return groups; } /** * Converts a set of internal Groups to thrift Groups by requesting permissions for each group and then calling * {@link #internalGroupToThriftGroup(ezbake.groups.graph.frames.vertex.Group, * ezbake.groups.thrift.GroupInheritancePermissions)} * with the group and its corresponding permissions. * * @param groupsToConvert internal groups to convert to thrift groups * @return a set of thrift Groups that are equivalent to the internal groups they were converted from * @throws ezbake.groups.thrift.GroupQueryException if a group could not be found in graph */ private Set<ezbake.groups.thrift.Group> internalGroupsToThriftGroups(Set<Group> groupsToConvert) throws GroupQueryException { final Set<ezbake.groups.thrift.Group> convertedGroups = Sets.newHashSet(); for (final Group g : groupsToConvert) { GroupInheritancePermissions inheritance = null; try { inheritance = graph.getGroupInheritancePermissions(g.getGroupName()); } catch (final VertexNotFoundException e) { final String errMsg = String.format("Could not get expected groups! Could not find group '%s!'", g.getGroupName()); logger.error(errMsg, e); throw new GroupQueryException(errMsg, GroupQueryError.GROUP_NOT_FOUND); } convertedGroups.add(internalGroupToThriftGroup(g, inheritance)); } return convertedGroups; } /** * Convert an internal Group to a thrift Group. * * @param group Group to convert to thrift Group * @param inheritance inheritance to assign group * @return a thrift Group with fields populated by values in the internal Group */ private static ezbake.groups.thrift.Group internalGroupToThriftGroup(Group group, GroupInheritancePermissions inheritance) { final ezbake.groups.thrift.Group thriftGroup = new ezbake.groups.thrift.Group(group.getIndex(), nameHelper.removeRootGroupPrefix(group.getGroupName())); thriftGroup.setInheritancePermissions(inheritance); thriftGroup.setIsActive(true); thriftGroup.setRequireOnlyUser(group.isRequireOnlyUser()); thriftGroup.setRequireOnlyAPP(group.isRequireOnlyApp()); thriftGroup.setFriendlyName(group.getGroupFriendlyName()); return thriftGroup; } /** * Builds a map using the given set of Long as keys and an error string as values. * * @param ids IDs to use as keys in the map * @return a map with keys from the given set of Long and an error string for values */ public static Map<Long, String> getUnloadedGroupIndexToNameMap(Set<Long> ids) { final Map<Long, String> unloadedMap = Maps.newHashMap(); for (final Long index : ids) { unloadedMap.put(index, UNABLE_TO_RETRIEVE_GROUP_NAME); } return unloadedMap; } /** * Closes this stream and releases any system resources associated with it. If the stream is already closed then * invoking this method has no effect. * * @throws java.io.IOException if an I/O error occurs */ @Override public void close() throws IOException { if (graph != null) { graph.close(); } } }