/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.response; import java.net.URI; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.emc.storageos.api.service.authorization.PermissionsHelper; import com.emc.storageos.db.client.model.ActionableEvent; import com.emc.storageos.db.client.model.Cluster; import com.emc.storageos.db.client.model.ComputeVirtualPool; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.DataObject.Flag; import com.emc.storageos.db.client.model.FilePolicy; import com.emc.storageos.db.client.model.Host; import com.emc.storageos.db.client.model.HostInterface; import com.emc.storageos.db.client.model.Migration; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.ProjectResource; import com.emc.storageos.db.client.model.SchedulePolicy; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Task; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.UserGroup; import com.emc.storageos.db.client.model.Vcenter; import com.emc.storageos.db.client.model.VcenterDataCenter; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.VirtualPool.Type; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.security.authentication.StorageOSUser; import com.emc.storageos.security.authorization.ACL; import com.emc.storageos.security.authorization.Role; import com.google.common.base.Function; /** * Iterator based list of resources */ public class BulkList<T> implements List<T> { private static final Logger _log = LoggerFactory.getLogger(BulkList.class); private Iterator<T> _iterator; public BulkList() { } public static <E extends DataObject, T> BulkList<T> wrapping(Iterator<E> dbIterator, Function<E, T> adapter, ResourceFilter<E> filter) { BulkList<T> list = new BulkList<T>(); list.setIterator(new AdaptingIterator<E, T>(dbIterator, adapter, filter)); return list; } public static <E extends DataObject, T> BulkList<T> wrapping(Iterator<E> dbIterator, Function<E, T> adapter) { return wrapping(dbIterator, adapter, new ResourceFilter<E>()); } @Override public int size() { throw new UnsupportedOperationException(); } @Override public boolean isEmpty() { throw new UnsupportedOperationException(); } @Override public boolean contains(Object o) { throw new UnsupportedOperationException(); } @Override public Iterator<T> iterator() { return _iterator; } public void setIterator(Iterator<T> iterator) { _iterator = iterator; } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public <T> T[] toArray(T[] a) { throw new UnsupportedOperationException(); } @Override public boolean add(T e) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends T> c) { throw new UnsupportedOperationException(); } @Override public boolean addAll(int index, Collection<? extends T> c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public T get(int index) { throw new UnsupportedOperationException(); } @Override public T set(int index, T element) { throw new UnsupportedOperationException(); } @Override public void add(int index, T element) { throw new UnsupportedOperationException(); } @Override public T remove(int index) { throw new UnsupportedOperationException(); } @Override public int indexOf(Object o) { throw new UnsupportedOperationException(); } @Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); } @Override public ListIterator<T> listIterator() { throw new UnsupportedOperationException(); } @Override public ListIterator<T> listIterator(int index) { throw new UnsupportedOperationException(); } @Override public List<T> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); } /** * An iterator to create any type of element from a DataObject iterator. This is based * on the original RepFilteringIterator implementation but allows: * - Arbitrary function to define mapping. * - No reflect for construction. Works with any object type * - Same class allows filtering or not */ public static class AdaptingIterator<E extends DataObject, T> implements Iterator<T> { private final Iterator<E> dbIterator; private final Function<E, T> adapter; private ResourceFilter<E> filter = null; E _next = null; public AdaptingIterator(Iterator<E> dbIterator, Function<E, T> adapter, ResourceFilter<E> filter) { this.dbIterator = dbIterator; this.adapter = adapter; this.filter = filter; } @Override public boolean hasNext() { if (null == _next) { while (dbIterator.hasNext()) { E element = dbIterator.next(); if (filter == null || filter.isExposed(element)) { _next = element; break; } } } return _next != null; } @Override public T next() { E next = null; T ret = null; if (_next != null) { next = _next; } else { if (hasNext()) { next = _next; } } if (next != null) { ret = adapter.apply(next); _next = null; } return ret; } @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Base ResourceFilter which only performs non-user/role specific exclusions */ public static class ResourceFilter<E extends DataObject> { /** * check whether to expose the specified resource * * The base implementation of this method only excludes * objects with the NO_PUBLIC_ACCESS flag * * @param resource the resource to be checked upon. * @return true if the object should be exposed */ public boolean isExposed(E resource) { return !resource.checkInternalFlags(Flag.NO_PUBLIC_ACCESS); } } /** * Abstract ResourceFilter to assist with performing user/role based filtering */ public static abstract class PermissionsEnforcingResourceFilter<E extends DataObject> extends ResourceFilter<E> { protected PermissionsHelper _permissionsHelper; protected StorageOSUser _user; private final ResourceFilteringCache _cache = new ResourceFilteringCache(); protected PermissionsEnforcingResourceFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { _user = user; _permissionsHelper = permissionsHelper; } /** * check whether to expose the specified resource * * Overrides the default implmentation to require both the * absence of the NO_PUBLIC_ACCESS flag, and a true result * from isAccessible(). Subclasses must provide implementations * of isAccessible() which reflect the appropriate tenant, project, * or other appliciable permissions. */ @Override public boolean isExposed(E resource) { return super.isExposed(resource) && isAccessible(resource); } /** * Subclasses must implement this method in such a way that it * returns true only if the current user should have access to * the resource * * @param resource the resource to be checked upon. * @return true if the user has the appropriate permissions */ protected abstract boolean isAccessible(E resource); /** * verify whether the user in the filter has access to tenant * * @param tenant the tenant to be checked upon. * @return true if user can access the tenant. */ protected boolean isTenantAccessible(URI tenant) { if (tenant == null) { return false; } if (_cache._accessibleParentResources.contains(tenant)) { return true; } if (_cache._nonAccessibleParentResources.contains(tenant)) { return false; } boolean ret = _permissionsHelper.userHasGivenRole( _user, tenant, Role.TENANT_ADMIN, Role.SECURITY_ADMIN); if (ret) { _cache._accessibleParentResources.add(tenant); } else { _cache._nonAccessibleParentResources.add(tenant); } return ret; } /** * verify whether the user in the filter has access to the project * * @param project the project to be checked upon. * @return true if user can access the project. */ protected boolean isProjectAccessible(URI project) { if (project == null) { return false; } if (_cache._accessibleParentResources.contains(project)) { return true; } if (_cache._nonAccessibleParentResources.contains(project)) { return false; } boolean ret = _permissionsHelper.userHasGivenACL( _user, project, ACL.ANY); if (ret) { _cache._accessibleParentResources.add(project); } else { _cache._nonAccessibleParentResources.add(project); } return ret; } } public static abstract class TenantResourceFilter<E extends DataObject> extends PermissionsEnforcingResourceFilter<E> { protected TenantResourceFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } protected boolean isTenantResourceAccessible(URI tenantId) { boolean ret = false; ret = tenantId.toString().equals(_user.getTenantId()); if (!ret) { ret = _permissionsHelper.userHasGivenRole( _user, null, Role.SYSTEM_MONITOR, Role.SYSTEM_ADMIN); } if (!ret) { ret = isTenantAccessible(tenantId); } return ret; } } public static class ProjectResourceFilter<E extends DataObject & ProjectResource> extends PermissionsEnforcingResourceFilter<E> { public ProjectResourceFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(E resource) { boolean ret = false; ret = isTenantAccessible(resource.getTenant().getURI()); if (!ret) { NamedURI proj = resource.getProject(); if (proj != null) { ret = isProjectAccessible(proj.getURI()); } } return ret; } } public static class FilePolicyResourceFilter extends PermissionsEnforcingResourceFilter<FilePolicy> { public FilePolicyResourceFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(FilePolicy filePolicy) { return canAccessFilePolicy(filePolicy); } private boolean canAccessFilePolicy(FilePolicy filePolicy) { StringSet tenants = filePolicy.getTenantOrg(); if (tenants == null || tenants.isEmpty()) { return true; } if (_permissionsHelper.userHasGivenRole( _user, null, Role.SYSTEM_ADMIN)) { return true; } String userTenantId = _user.getTenantId(); return tenants.contains(userTenantId); } } public static class ResourceFilteringCache { public HashSet<URI> _accessibleParentResources = new HashSet<URI>(); public HashSet<URI> _nonAccessibleParentResources = new HashSet<URI>(); } public static class VirtualArrayACLFilter extends PermissionsEnforcingResourceFilter<VirtualArray> { public VirtualArrayACLFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(VirtualArray resource) { return isNeighborhoodAccessible(resource); } /** * verify whether the user in the filter has access to the neighbor * based on resource ACL * * @return true if user can access the resource. */ private boolean isNeighborhoodAccessible(VirtualArray resource) { List<URI> tenantUris = _permissionsHelper.getSubtenantsWithRoles(_user); return _permissionsHelper.tenantHasUsageACL( URI.create(_user.getTenantId()), resource) || _permissionsHelper.tenantHasUsageACL(tenantUris, resource); } } public static class ProjectFilter extends PermissionsEnforcingResourceFilter<Project> { public ProjectFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(Project resource) { boolean ret = false; ret = isTenantAccessible(resource.getTenantOrg().getURI()); if (!ret) { return ret = _permissionsHelper.userHasGivenACL( _user, resource.getId(), ACL.ANY); } return ret; } } public static class TenantFilter extends PermissionsEnforcingResourceFilter<TenantOrg> { public TenantFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(TenantOrg resource) { return _permissionsHelper.userHasGivenRole( _user, resource.getId(), Role.TENANT_ADMIN, Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN); } } public static class EventFilter extends TenantResourceFilter<ActionableEvent> { public EventFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(ActionableEvent resource) { if (NullColumnValueGetter.isNullURI(resource.getTenant())) { return false; } return isTenantResourceAccessible(resource.getTenant()); } } public static class HostFilter extends TenantResourceFilter<Host> { public HostFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(Host resource) { if (NullColumnValueGetter.isNullURI(resource.getTenant())) { return false; } return isTenantResourceAccessible(resource.getTenant()); } } public static class HostInterfaceFilter extends PermissionsEnforcingResourceFilter<HostInterface> { public HostInterfaceFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(HostInterface resource) { if (_permissionsHelper.userHasGivenRole( _user, null, Role.SYSTEM_MONITOR, Role.SYSTEM_ADMIN)) { return true; } if (resource.getHost() == null) { return false; } boolean ret = false; Host host = _permissionsHelper.getObjectById(resource.getHost(), Host.class, true); ret = host.getTenant().toString().equals(_user.getTenantId()); if (!ret) { ret = isTenantAccessible(host.getTenant()); } return ret; } } public static class ClusterFilter extends TenantResourceFilter<Cluster> { public ClusterFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(Cluster resource) { if (NullColumnValueGetter.isNullURI(resource.getTenant())) { return false; } return isTenantResourceAccessible(resource.getTenant()); } } public static class VcenterFilter extends TenantResourceFilter<Vcenter> { public VcenterFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(Vcenter resource) { if (_permissionsHelper.userHasGivenRole(_user, null, Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN)) { return true; } Set<URI> tenantIds = _permissionsHelper.getUsageURIsFromAcls(resource.getAcls()); if (CollectionUtils.isEmpty(tenantIds)) { return false; } Iterator<URI> uriIterator = tenantIds.iterator(); while (uriIterator.hasNext()) { if (isTenantResourceAccessible(uriIterator.next())) { return true; } } return false; } @Override protected boolean isTenantResourceAccessible(URI tenantId) { if (tenantId.toString().equals(_user.getTenantId())) { return true; } return isTenantAccessible(tenantId); } } public static class VcenterDataCenterFilter extends TenantResourceFilter<VcenterDataCenter> { public VcenterDataCenterFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(VcenterDataCenter resource) { if (_permissionsHelper.userHasGivenRole(_user, null, Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN)) { return true; } if (NullColumnValueGetter.isNullURI(resource.getTenant())) { return false; } return isTenantResourceAccessible(resource.getTenant()); } } public static class VirtualPoolFilter extends PermissionsEnforcingResourceFilter<VirtualPool> { Type vpoolType; public VirtualPoolFilter(Type vpoolType) { super(null, null); this.vpoolType = vpoolType; } public VirtualPoolFilter(Type vpoolType, StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); this.vpoolType = vpoolType; } @Override public boolean isAccessible(VirtualPool resource) { if (resource.getType().equals(vpoolType.name())) { if (_user != null) { return isVpoolAccessible(resource); } else { return true; } } return false; } /** * verify whether the user in the filter has access to the vpool * based on resource ACL * * @return true if user can access the resource. */ private boolean isVpoolAccessible(VirtualPool resource) { List<URI> tenantUris = _permissionsHelper.getSubtenantsWithRoles(_user); return _permissionsHelper.tenantHasUsageACL( URI.create(_user.getTenantId()), resource) || _permissionsHelper.tenantHasUsageACL(tenantUris, resource); } } public static class ComputeVirtualPoolFilter extends PermissionsEnforcingResourceFilter<ComputeVirtualPool> { Type vpoolType; public ComputeVirtualPoolFilter() { super(null, null); } public ComputeVirtualPoolFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(ComputeVirtualPool resource) { if (_user != null) { return isComputeVirtualPoolAccessible(resource); } else { return true; } } /** * verify whether the user in the filter has access to the vcpool * based on resource ACL * * @return true if user can access the resource. */ private boolean isComputeVirtualPoolAccessible(ComputeVirtualPool resource) { if (_permissionsHelper.userHasGivenRole(_user, null, Role.SYSTEM_ADMIN) || _permissionsHelper.userHasGivenRole(_user, null, Role.SYSTEM_MONITOR)) { return true; } else { List<URI> tenantUris = _permissionsHelper.getSubtenantsWithRoles(_user); return _permissionsHelper.tenantHasUsageACL( URI.create(_user.getTenantId()), resource) || _permissionsHelper.tenantHasUsageACL(tenantUris, resource); } } } /** * Used to control access to migrations for bulk requests. Essentially * a project resource filter, but the migration itself is not a project * resource. The project for a migration is the project for the volume * being migrated. */ public static class MigrationFilter extends PermissionsEnforcingResourceFilter<Migration> { /** * Default constructor */ public MigrationFilter() { super(null, null); } /** * Parameter constructor. * * @param user User requesting access to a migration. * @param permissionsHelper Reference to the permissions helper. */ public MigrationFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } /** * {@inheritDoc} */ @Override public boolean isAccessible(Migration resource) { if (_user != null) { return isUserAuthorizedForMigration(resource, _user, _permissionsHelper); } else { return true; } } /** * Determines if the user is authorized for the passed migration. User * is assumed to be a tenant admin, system admin, or system monitor. * * @param migration A reference to the migration. * @param user A reference to the user. * @param permissionsHelper A reference to a permissions helper. * * @return true if the user is authorized, false otherwise. */ public static boolean isUserAuthorizedForMigration(Migration migration, StorageOSUser user, PermissionsHelper permissionsHelper) { URI volumeURI = migration.getVolume(); Volume volume = permissionsHelper.getObjectById(volumeURI, Volume.class); if (volume == null) { return false; } URI projectURI = volume.getProject().getURI(); Project project = permissionsHelper.getObjectById(projectURI, Project.class); if ((permissionsHelper.userHasGivenRole(user, project.getTenantOrg().getURI(), Role.TENANT_ADMIN)) || (permissionsHelper.userHasGivenACL(user, project.getId(), ACL.OWN, ACL.ALL))) { return true; } else { return false; } } } public static class TaskFilter extends PermissionsEnforcingResourceFilter<Task> { public TaskFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override protected boolean isAccessible(Task resource) { return true; } } public static class UserGroupFilter extends PermissionsEnforcingResourceFilter<UserGroup> { public UserGroupFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(UserGroup resource) { return (_permissionsHelper.userHasGivenRoleInAnyTenant(_user, Role.SECURITY_ADMIN, Role.TENANT_ADMIN) || _permissionsHelper .userHasGivenProjectACL(_user, ACL.OWN)); } } public static class SchedulePolicyFilter extends PermissionsEnforcingResourceFilter<SchedulePolicy> { public SchedulePolicyFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { super(user, permissionsHelper); } @Override public boolean isAccessible(SchedulePolicy resource) { boolean ret = false; ret = isTenantAccessible(resource.getTenantOrg().getURI()); if (!ret) { return ret = _permissionsHelper.userHasGivenRole( _user, resource.getId(), Role.SECURITY_ADMIN, Role.TENANT_ADMIN, Role.SYSTEM_MONITOR); } return ret; } } }