/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE file at the root of the source * tree and available online at * * https://github.com/keeps/roda */ package org.roda.core.common; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; import javax.servlet.http.HttpServletRequest; import org.roda.core.RodaCoreFactory; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.AuthorizationDeniedException; import org.roda.core.data.exceptions.GenericException; import org.roda.core.data.exceptions.NotFoundException; import org.roda.core.data.exceptions.RequestNotValidException; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.core.data.v2.index.select.SelectedItemsFilter; import org.roda.core.data.v2.index.select.SelectedItemsList; import org.roda.core.data.v2.ip.DIPFile; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.ip.IndexedDIP; import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.Permissions.PermissionType; import org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent; import org.roda.core.data.v2.user.User; import org.roda.core.index.IndexService; import org.roda.core.index.utils.IterableIndexResult; import org.roda.core.model.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; public class UserUtility { private static final Logger LOGGER = LoggerFactory.getLogger(UserUtility.class); public static final String RODA_USER = "RODA_USER"; private static final String REGISTER_ACTIVE_PROPERTY = "ui.register.active"; private static final String REGISTER_DEFAULT_GROUPS = "ui.register.defaultGroups"; private static final String REGISTER_DEFAULT_ROLES = "ui.register.defaultRoles"; private static LdapUtility ldapUtility; /** Private empty constructor */ private UserUtility() { // do nothing } public static LdapUtility getLdapUtility() { return ldapUtility; } public static void setLdapUtility(LdapUtility utility) { ldapUtility = utility; } public static User getApiUser(final HttpServletRequest request) throws AuthorizationDeniedException { return getUser(request, false); } public static User getUser(final HttpServletRequest request, final boolean returnGuestIfNoUserInSession) { User user = (User) request.getSession().getAttribute(RODA_USER); if (user == null) { user = returnGuestIfNoUserInSession ? getGuest() : null; } else { if (user.isGuest()) { user = getGuest(); } } return user; } public static User getUser(final HttpServletRequest request) { return getUser(request, true); } public static void checkRoles(final User rsu, final List<String> rolesToCheck) throws AuthorizationDeniedException { // INFO 20170220 nvieira containsAll changed to set intersection (contain at // least one role) if (!rolesToCheck.isEmpty() && Sets.intersection(rsu.getAllRoles(), new HashSet<>(rolesToCheck)).isEmpty()) { final List<String> missingRoles = new ArrayList<>(rolesToCheck); missingRoles.removeAll(rsu.getAllRoles()); throw new AuthorizationDeniedException("The user '" + rsu.getId() + "' does not have all needed permissions", missingRoles); } } public static void checkGroup(final User rsu, final String group) throws AuthorizationDeniedException { if (!rsu.getGroups().contains(group)) { LOGGER.debug("User '{}' groups: {} vs. group to check: {}", rsu.getId(), rsu.getGroups(), group); throw new AuthorizationDeniedException( "The user '" + rsu.getId() + "' does not belong to the group '" + group + "'"); } } public static void checkRoles(final User user, final String... rolesToCheck) throws AuthorizationDeniedException { checkRoles(user, Arrays.asList(rolesToCheck)); } public static void checkRoles(final User user, final Class<?> invokingMethodInnerClass) throws AuthorizationDeniedException { checkRoles(user, invokingMethodInnerClass, null); } public static void checkRoles(final User user, final Class<?> invokingMethodInnerClass, final Class<?> classToReturn) throws AuthorizationDeniedException { final Method method = invokingMethodInnerClass.getEnclosingMethod(); final String classParam = (classToReturn == null) ? "" : "(" + classToReturn.getSimpleName() + ")"; final String configKey = String.format("core.roles.%s.%s%s", method.getDeclaringClass().getName(), method.getName(), classParam); if (RodaCoreFactory.getRodaConfiguration().containsKey(configKey)) { LOGGER.trace("Testing if user '{}' has permissions to '{}'", user.getName(), configKey); final List<String> roles = RodaCoreFactory.getRodaConfigurationAsList(configKey); checkRoles(user, roles); } else { LOGGER.error("Unable to determine which roles the user '{}' needs because the config. key '{}' is not defined", user.getName(), configKey); throw new AuthorizationDeniedException( "Unable to determine which roles the user needs because the config. key '" + configKey + "' is not defined"); } } public static void setUser(final HttpServletRequest request, final User user) { request.getSession(true).setAttribute(RODA_USER, user); } public static void logout(HttpServletRequest servletRequest) { servletRequest.getSession().removeAttribute(RODA_USER); // CAS specific clean up servletRequest.getSession().removeAttribute("edu.yale.its.tp.cas.client.filter.user"); servletRequest.getSession().removeAttribute("_const_cas_assertion_"); } /** * Retrieves guest used */ public static User getGuest() { return new User("guest", "guest", true); } private static boolean iterativeDisjoint(Set<String> set1, Set<String> set2) { boolean noCommonElement = true; for (String string : set1) { if (set2.contains(string)) { noCommonElement = false; break; } } return noCommonElement; } /** * This method make sure that a normal user can only upload a file to a folder * with its own username * * @param user * @param ids * @throws AuthorizationDeniedException */ public static void checkTransferredResourceAccess(User user, List<String> ids) throws AuthorizationDeniedException { // do nothing } public static boolean isAdministrator(String username) { return username.equals(RodaConstants.ADMIN); } public static boolean isAdministrator(User user) { return user.getName().equals(RodaConstants.ADMIN); } public static void checkAIPPermissions(User user, IndexedAIP aip, PermissionType permissionType) throws AuthorizationDeniedException { if (isAdministrator(user)) { return; } Set<String> users = aip.getPermissions().getUsers().get(permissionType); Set<String> groups = aip.getPermissions().getGroups().get(permissionType); LOGGER.debug("Checking if user '{}' has permissions to {} object {} (object read permissions: {} & {})", user.getId(), permissionType, aip.getId(), users, groups); if (!users.contains(user.getId()) && iterativeDisjoint(groups, user.getGroups())) { throw new AuthorizationDeniedException( "The user '" + user.getId() + "' does not have permissions to " + permissionType); } } public static void checkDIPPermissions(User user, IndexedDIP dip, PermissionType permissionType) throws AuthorizationDeniedException { if (isAdministrator(user)) { return; } Set<String> users = dip.getPermissions().getUsers().get(permissionType); Set<String> groups = dip.getPermissions().getGroups().get(permissionType); LOGGER.debug("Checking if user '{}' has permissions to {} dip {} (object read permissions: {} & {})", user.getId(), permissionType, dip.getId(), users, groups); if (!users.contains(user.getId()) && iterativeDisjoint(groups, user.getGroups())) { throw new AuthorizationDeniedException( "The user '" + user.getId() + "' does not have permissions to " + permissionType); } } public static <T extends IsIndexed> void checkObjectPermissions(User user, T obj, PermissionType permissionType) throws AuthorizationDeniedException { if (obj instanceof IndexedAIP) { checkAIPPermissions(user, (IndexedAIP) obj, permissionType); } else if (obj instanceof IndexedRepresentation) { checkRepresentationPermissions(user, (IndexedRepresentation) obj, permissionType); } else if (obj instanceof IndexedFile) { checkFilePermissions(user, (IndexedFile) obj, permissionType); } else if (obj instanceof IndexedDIP) { checkDIPPermissions(user, (IndexedDIP) obj, permissionType); } else if (obj instanceof DIPFile) { checkDIPFilePermissions(user, (DIPFile) obj, permissionType); } else if (obj instanceof IndexedPreservationEvent) { checkPreservationEventPermissions(user, (IndexedPreservationEvent) obj, permissionType); } } private static <T extends IsIndexed> void checkAIPObjectPermissions(User user, T obj, Function<T, String> toAIP, PermissionType permissionType) throws AuthorizationDeniedException { if (isAdministrator(user)) { return; } String aipId = toAIP.apply(obj); IndexedAIP aip; try { aip = RodaCoreFactory.getIndexService().retrieve(IndexedAIP.class, aipId, RodaConstants.AIP_PERMISSIONS_FIELDS_TO_RETURN); } catch (NotFoundException | GenericException e) { throw new AuthorizationDeniedException("Could not check permissions of object " + obj, e); } Set<String> users = aip.getPermissions().getUsers().get(permissionType); Set<String> groups = aip.getPermissions().getGroups().get(permissionType); LOGGER.debug("Checking if user '{}' has permissions to {} object {} (object read permissions: {} & {})", user.getId(), permissionType, aip.getId(), users, groups); if (!users.contains(user.getId()) && iterativeDisjoint(groups, user.getGroups())) { throw new AuthorizationDeniedException( "The user '" + user.getId() + "' does not have permissions to " + permissionType); } } private static <T extends IsIndexed> void checkDIPObjectPermissions(User user, T obj, Function<T, String> toDIP, PermissionType permissionType) throws AuthorizationDeniedException { if (isAdministrator(user)) { return; } String dipId = toDIP.apply(obj); IndexedDIP dip; try { dip = RodaCoreFactory.getIndexService().retrieve(IndexedDIP.class, dipId, RodaConstants.DIP_PERMISSIONS_FIELDS_TO_RETURN); } catch (NotFoundException | GenericException e) { throw new AuthorizationDeniedException("Could not check permissions of object " + obj, e); } Set<String> users = dip.getPermissions().getUsers().get(permissionType); Set<String> groups = dip.getPermissions().getGroups().get(permissionType); LOGGER.debug("Checking if user '{}' has permissions to {} object {} (object read permissions: {} & {})", user.getId(), permissionType, dip.getId(), users, groups); if (!users.contains(user.getId()) && iterativeDisjoint(groups, user.getGroups())) { throw new AuthorizationDeniedException( "The user '" + user.getId() + "' does not have permissions to " + permissionType); } } public static void checkRepresentationPermissions(User user, IndexedRepresentation rep, PermissionType permissionType) throws AuthorizationDeniedException { checkAIPObjectPermissions(user, rep, r -> r.getAipId(), permissionType); } public static void checkFilePermissions(User user, IndexedFile file, PermissionType permissionType) throws AuthorizationDeniedException { checkAIPObjectPermissions(user, file, f -> f.getAipId(), permissionType); } public static void checkDIPFilePermissions(User user, DIPFile file, PermissionType permissionType) throws AuthorizationDeniedException { checkDIPObjectPermissions(user, file, f -> f.getDipId(), permissionType); } public static void checkPreservationEventPermissions(User user, IndexedPreservationEvent event, PermissionType permissionType) throws AuthorizationDeniedException { checkAIPObjectPermissions(user, event, f -> f.getAipID(), permissionType); } public static void checkAIPPermissions(User user, SelectedItems<IndexedAIP> selected, PermissionType permission) throws AuthorizationDeniedException, GenericException, RequestNotValidException { if (isAdministrator(user)) { return; } IndexService index = RodaCoreFactory.getIndexService(); if (selected instanceof SelectedItemsFilter) { SelectedItemsFilter<IndexedAIP> selectedItems = (SelectedItemsFilter<IndexedAIP>) selected; IterableIndexResult<IndexedAIP> findAll = index.findAll(IndexedAIP.class, selectedItems.getFilter(), RodaConstants.AIP_PERMISSIONS_FIELDS_TO_RETURN); for (IndexedAIP aip : findAll) { checkAIPPermissions(user, aip, permission); } } else if (selected instanceof SelectedItemsList) { SelectedItemsList<IndexedAIP> selectedItems = (SelectedItemsList<IndexedAIP>) selected; List<IndexedAIP> aips = ModelUtils.getIndexedAIPsFromObjectIds(selectedItems); for (IndexedAIP aip : aips) { checkAIPPermissions(user, aip, permission); } } else { throw new RequestNotValidException( "SelectedItems implementations not supported: " + selected.getClass().getName()); } } public static void checkDIPPermissions(User user, SelectedItems<IndexedDIP> selected, PermissionType permission) throws AuthorizationDeniedException, GenericException, RequestNotValidException { if (isAdministrator(user)) { return; } IndexService index = RodaCoreFactory.getIndexService(); if (selected instanceof SelectedItemsFilter) { SelectedItemsFilter<IndexedDIP> selectedItems = (SelectedItemsFilter<IndexedDIP>) selected; IterableIndexResult<IndexedDIP> findAll = index.findAll(IndexedDIP.class, selectedItems.getFilter(), RodaConstants.DIP_PERMISSIONS_FIELDS_TO_RETURN); for (IndexedDIP dip : findAll) { checkDIPPermissions(user, dip, permission); } } else if (selected instanceof SelectedItemsList) { SelectedItemsList<IndexedDIP> selectedItems = (SelectedItemsList<IndexedDIP>) selected; List<IndexedDIP> dips = ModelUtils.getIndexedDIPsFromObjectIds(selectedItems); for (IndexedDIP dip : dips) { checkDIPPermissions(user, dip, permission); } } else { throw new RequestNotValidException( "SelectedItems implementations not supported: " + selected.getClass().getName()); } } @SuppressWarnings("unchecked") public static <T extends IsIndexed> void checkObjectPermissions(User user, SelectedItems<T> selected, PermissionType permissionType) throws AuthorizationDeniedException, GenericException, RequestNotValidException { Class<T> classToReturn = SelectedItemsUtils.parseClass(selected.getSelectedClass()); if (classToReturn.equals(IndexedAIP.class)) { checkAIPPermissions(user, (SelectedItems<IndexedAIP>) selected, permissionType); } else if (classToReturn.equals(IndexedRepresentation.class)) { checkRepresentationPermissions(user, (SelectedItems<IndexedRepresentation>) selected, permissionType); } else if (classToReturn.equals(IndexedFile.class)) { checkFilePermissions(user, (SelectedItems<IndexedFile>) selected, permissionType); } else if (classToReturn.equals(IndexedDIP.class)) { checkDIPPermissions(user, (SelectedItems<IndexedDIP>) selected, permissionType); } } private static <T extends IsIndexed> void checkObjectPermissions(User user, SelectedItems<T> selected, Function<T, String> toAIP, PermissionType permission, List<String> fieldsToRequestIndex) throws AuthorizationDeniedException, GenericException, RequestNotValidException { if (isAdministrator(user)) { return; } Class<T> classToReturn = SelectedItemsUtils.parseClass(selected.getSelectedClass()); IndexService index = RodaCoreFactory.getIndexService(); if (selected instanceof SelectedItemsFilter) { SelectedItemsFilter<T> selectedItems = (SelectedItemsFilter<T>) selected; IterableIndexResult<T> findAll = index.findAll(classToReturn, selectedItems.getFilter(), fieldsToRequestIndex); for (T obj : findAll) { String aipId = toAIP.apply(obj); IndexedAIP aip; try { aip = index.retrieve(IndexedAIP.class, aipId, RodaConstants.AIP_PERMISSIONS_FIELDS_TO_RETURN); checkAIPPermissions(user, aip, permission); } catch (NotFoundException e) { // conservative approach throw new AuthorizationDeniedException( "Could not verify permissions of object [" + classToReturn.getSimpleName() + "] " + obj.getUUID(), e); } } } else if (selected instanceof SelectedItemsList) { SelectedItemsList<T> selectedItems = (SelectedItemsList<T>) selected; List<IndexedAIP> aips = new ArrayList<>(); for (String uuid : selectedItems.getIds()) { T obj; try { obj = index.retrieve(classToReturn, uuid, fieldsToRequestIndex); String aipId = toAIP.apply(obj); IndexedAIP aip = index.retrieve(IndexedAIP.class, aipId, RodaConstants.AIP_PERMISSIONS_FIELDS_TO_RETURN); aips.add(aip); } catch (NotFoundException e) { // conservative approach throw new AuthorizationDeniedException( "Could not verify permissions of object [" + classToReturn.getSimpleName() + "] " + uuid, e); } } for (IndexedAIP aip : aips) { checkAIPPermissions(user, aip, permission); } } else { throw new RequestNotValidException( "SelectedItems implementations not supported: " + selected.getClass().getName()); } } public static void checkRepresentationPermissions(User user, SelectedItems<IndexedRepresentation> selected, PermissionType permission) throws AuthorizationDeniedException, GenericException, RequestNotValidException { checkObjectPermissions(user, selected, rep -> rep.getAipId(), permission, RodaConstants.REPRESENTATION_FIELDS_TO_RETURN); } public static void checkFilePermissions(User user, SelectedItems<IndexedFile> selected, PermissionType permission) throws AuthorizationDeniedException, GenericException, RequestNotValidException { checkObjectPermissions(user, selected, file -> file.getAipId(), permission, RodaConstants.FILE_FIELDS_TO_RETURN); } public static User resetGroupsAndRoles(User user) { List<Object> defaultRoles = RodaCoreFactory.getRodaConfiguration().getList(REGISTER_DEFAULT_ROLES); List<Object> defaultGroups = RodaCoreFactory.getRodaConfiguration().getList(REGISTER_DEFAULT_GROUPS); if (defaultRoles != null && !defaultRoles.isEmpty()) { user.setDirectRoles(new HashSet<String>(RodaUtils.copyList(defaultRoles))); } else { user.setDirectRoles(new HashSet<String>()); } if (defaultGroups != null && !defaultGroups.isEmpty()) { user.setGroups(new HashSet<String>(RodaUtils.copyList(defaultGroups))); } else { user.setGroups(new HashSet<String>()); } user.setActive(RodaCoreFactory.getRodaConfiguration().getBoolean(REGISTER_ACTIVE_PROPERTY)); return user; } }