/* * Copyright (c) 2008-2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.authorization; import java.net.URI; import java.util.HashSet; import java.util.Set; import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; import com.emc.vipr.client.core.BlockSnapshotSessions; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponseFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.api.service.impl.resource.*; import com.emc.storageos.coordinator.client.model.Site; import com.emc.storageos.coordinator.client.service.DrUtil; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.*; import com.emc.storageos.db.client.model.DataObject.Flag; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.security.SecurityDisabler; import com.emc.storageos.security.authorization.*; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.sun.jersey.spi.container.ResourceFilter; /** * Class implements ResourceFilterFactory to add permissions filter where needed */ public class PermissionsFilterFactory extends AbstractPermissionsFilterFactory { private static final Logger _log = LoggerFactory.getLogger(PermissionsFilterFactory.class); private PermissionsHelper _permissionsHelper; private boolean disableLicenseCheck = false; @Autowired(required = false) private SecurityDisabler _disabler; private @Context UriInfo uriInfo; @Autowired private DrUtil drUtil; private boolean isStandby; // default to false /** * PermissionsFilter for apisvc */ private class ApisvcPermissionFilter extends AbstractPermissionFilter { ApisvcPermissionFilter(Role[] roles, ACL[] acls, boolean blockProxies, Class resourceClazz, PermissionsHelper helper) { super(roles, acls, blockProxies, resourceClazz, helper); } @Override protected UriInfo getUriInfo() { return uriInfo; } /** * Get tenant id from the uri * * @return */ @Override protected URI getTenantIdFromURI(UriInfo uriInfo) { if (_resourceClazz.isAssignableFrom(TenantsService.class)) { String uriStr = uriInfo.getPathParameters().getFirst("id"); if (uriStr != null && !uriStr.isEmpty()) { return URI.create(uriStr); } } else if (_resourceClazz.isAssignableFrom(HostService.class) || _resourceClazz.isAssignableFrom(ClusterService.class) || _resourceClazz.isAssignableFrom(VcenterDataCenterService.class)) { String tenantResourceUriStr = uriInfo.getPathParameters().getFirst("id"); if (tenantResourceUriStr != null && !tenantResourceUriStr.isEmpty()) { URI tenantURI = _permissionsHelper.getTenantResourceTenantId(tenantResourceUriStr); if (NullColumnValueGetter.isNullURI(tenantURI)) { APIException.forbidden.resourceDoesNotBelongToAnyTenant(VcenterDataCenterService.class.getSimpleName(), tenantResourceUriStr); } return _permissionsHelper.getTenantResourceTenantId(tenantResourceUriStr); } } else if (_resourceClazz.isAssignableFrom(InitiatorService.class) || _resourceClazz.isAssignableFrom(IpInterfaceService.class)) { String tenantResourceUriStr = uriInfo.getPathParameters().getFirst("id"); if (tenantResourceUriStr != null && !tenantResourceUriStr.isEmpty()) { return _permissionsHelper.getTenantResourceTenantId(tenantResourceUriStr); } } else if (_resourceClazz.isAssignableFrom(ProjectService.class)) { String projectUriStr = uriInfo.getPathParameters().getFirst("id"); if (projectUriStr != null && !projectUriStr.isEmpty()) { return _permissionsHelper.getTenantIdFromProjectId(projectUriStr, isIdEmbeddedInURL(projectUriStr)); } } else { URI projectUri = getProjectIdFromURI(uriInfo); if (projectUri != null) { return _permissionsHelper.getTenantIdFromProjectId(projectUri.toString(), isIdEmbeddedInURL(projectUri.toString())); } } return null; } /** * Retrieve project id from the resource id * * @param uri * @param clazz * @return */ private URI getProjectIdFromResourceId(String uri, Class<? extends DataObject> clazz) { URI id = URI.create(uri); ProjectResource projObj = null; DataObject obj = _permissionsHelper.getObjectById(id, clazz); if (obj == null) { throw APIException.notFound.unableToFindEntityInURL(id); } projObj = (ProjectResource) obj; if ( obj.checkInternalFlags(Flag.INTERNAL_OBJECT) ) { _log.info("Resource {} is an internal resource", id); DataObject parentObj = _permissionsHelper.getParentObject(obj); if (parentObj != null) { _log.info("Resource {}'s parent is {}", id, parentObj.getId()); projObj = (ProjectResource) parentObj; } } if (projObj.getProject() == null) { throw APIException.badRequests.unauthorizedAccessToNonPublicResource(); } return projObj.getProject().getURI(); } /** * Retrieve project id from the snapshot resource id * * @param uri * @param clazz * @return */ private URI getProjectIdFromResourceSnapshotId(String uri, Class<? extends DataObject> clazz) { URI id = URI.create(uri); ProjectResourceSnapshot obj = (ProjectResourceSnapshot) _permissionsHelper .getObjectById(id, clazz); if (obj == null) { throw APIException.notFound.unableToFindEntityInURL(id); } return getProjectIdFromResourceId(obj.getParent().getURI().toString(), obj.parentClass()); } /** * Retrieve project id from the block snapshot id * * @param uri * @param clazz * @return */ private URI getProjectIdFromResourceBlockSnapshotId(String uri, Class<? extends DataObject> clazz) { URI id = URI.create(uri); ProjectResourceSnapshot obj = (ProjectResourceSnapshot) _permissionsHelper .getObjectById(id, clazz); if (obj == null) { throw APIException.notFound.unableToFindEntityInURL(id); } return obj.getProject().getURI(); } /** * Retrieve project id from the snapshot resource id * * @param uri * @return */ private URI getProjectIdFromComputeResources(String uri) { URI id = URI.create(uri); if (URIUtil.isType(id, Host.class)) { Host host = _permissionsHelper.getObjectById(id, Host.class); return host.getProject(); } else if (URIUtil.isType(id, Cluster.class)) { Cluster cluster = _permissionsHelper.getObjectById(id, Cluster.class); return cluster.getProject(); } else if (URIUtil.isType(id, Initiator.class)) { Initiator ini = _permissionsHelper.getObjectById(id, Initiator.class); if (ini.getHost() != null) { Host host = _permissionsHelper.getObjectById(ini.getHost(), Host.class); return host.getProject(); } else { return null; } } else if (URIUtil.isType(id, IpInterface.class)) { IpInterface hostIf = _permissionsHelper.getObjectById(id, IpInterface.class); if (hostIf.getHost() != null) { Host host = _permissionsHelper.getObjectById(hostIf.getHost(), Host.class); return host.getProject(); } else { return null; } } return null; } /** * Get project id from the uri * * @return */ @Override protected URI getProjectIdFromURI(UriInfo uriInfo) { if (_resourceClazz.isAssignableFrom(ProjectService.class)) { String projectUriStr = uriInfo.getPathParameters().getFirst("id"); if (projectUriStr != null && !projectUriStr.isEmpty()) { return URI.create(projectUriStr); } } else { String projectUriStr = uriInfo.getQueryParameters().getFirst("project"); String uriStr = uriInfo.getPathParameters().getFirst("id"); if (projectUriStr != null && !projectUriStr.isEmpty()) { return URI.create(projectUriStr); } else if (uriStr != null && !uriStr.isEmpty()) { if (_resourceClazz.isAssignableFrom(FileService.class)) { return getProjectIdFromResourceId(uriStr, FileShare.class); } else if (_resourceClazz.isAssignableFrom(ExportGroupService.class)) { return getProjectIdFromResourceId(uriStr, ExportGroup.class); } else if (_resourceClazz.isAssignableFrom(BlockService.class)) { return getProjectIdFromResourceId(uriStr, BlockService.getBlockServiceResourceClass(uriStr)); } else if (_resourceClazz.isAssignableFrom(BlockConsistencyGroupService.class)) { return getProjectIdFromResourceId(uriStr, BlockConsistencyGroup.class); } else if (_resourceClazz.isAssignableFrom(BlockSnapshotService.class)) { return getProjectIdFromResourceBlockSnapshotId(uriStr, BlockSnapshot.class); } else if (_resourceClazz.isAssignableFrom(FileSnapshotService.class)) { return getProjectIdFromResourceSnapshotId(uriStr, Snapshot.class); } else if (_resourceClazz.isAssignableFrom(HostService.class)) { return getProjectIdFromComputeResources(uriStr); } else if (_resourceClazz.isAssignableFrom(ClusterService.class)) { return getProjectIdFromComputeResources(uriStr); } else if (_resourceClazz.isAssignableFrom(IpInterfaceService.class)) { return getProjectIdFromComputeResources(uriStr); } else if (_resourceClazz.isAssignableFrom(InitiatorService.class)) { return getProjectIdFromComputeResources(uriStr); } else if (_resourceClazz.isAssignableFrom(BucketService.class)) { return getProjectIdFromResourceId(uriStr, Bucket.class); } else if (_resourceClazz.isAssignableFrom(BlockSnapshotSessionService.class)) { return getProjectIdFromResourceBlockSnapshotId(uriStr, BlockSnapshotSession.class); } else if (_resourceClazz.isAssignableFrom(StorageSystemTypeService.class)) { return getProjectIdFromResourceId(uriStr, StorageSystemType.class); } else if (_resourceClazz.isAssignableFrom(Volume.class)) { return getProjectIdFromResourceId(uriStr, Volume.class); } } else { _log.warn("project id not available for this resource type"); } } return null; } @Override protected Set<String> getUsageAclsFromURI(String tenantId, UriInfo uriInfo) { Set<String> acls = null; if (tenantId != null) { String uriStr = uriInfo.getPathParameters().getFirst("id"); if (uriStr != null && !uriStr.isEmpty()) { URI uri = URI.create(uriStr); if (VirtualPoolService.class.isAssignableFrom(_resourceClazz)) { VirtualPool obj = _permissionsHelper.getObjectById(uri, VirtualPool.class); // if no acls, consider open for all if (obj.getAcls() == null || obj.getAcls().size() == 0) { acls = new HashSet<String>(); acls.add(ACL.USE.toString()); } else { acls = obj.getAclSet(new PermissionsKey(PermissionsKey.Type.TENANT, tenantId, obj.getType()).toString()); } } else if (ComputeVirtualPoolService.class.isAssignableFrom(_resourceClazz)) { ComputeVirtualPool obj = _permissionsHelper.getObjectById(uri, ComputeVirtualPool.class); // if no acls, consider open for all if (obj.getAcls() == null || obj.getAcls().size() == 0) { acls = new HashSet<String>(); acls.add(ACL.USE.toString()); } else { acls = obj.getAclSet(new PermissionsKey(PermissionsKey.Type.TENANT, tenantId.toString(), obj.getSystemType()).toString()); } } else if (_resourceClazz.isAssignableFrom(VirtualArrayService.class)) { VirtualArray obj = _permissionsHelper .getObjectById(uri, VirtualArray.class); // if no acls, consider open for all if (obj.getAcls() == null || obj.getAcls().size() == 0) { acls = new HashSet<String>(); acls.add(ACL.USE.toString()); } else { acls = obj.getAclSet(new PermissionsKey(PermissionsKey.Type.TENANT, tenantId).toString()); } } else if (_resourceClazz.isAssignableFrom(VcenterService.class)) { Vcenter obj = _permissionsHelper.getObjectById(uri, Vcenter.class); if (obj.getAcls() == null || obj.getAcls().size() == 0) { acls = new HashSet<String>(); acls.add(ACL.USE.toString()); } else { acls = obj.getAclSet(new PermissionsKey(PermissionsKey.Type.TENANT, tenantId).toString()); } } else if (_resourceClazz.isAssignableFrom(HostService.class) || _resourceClazz.isAssignableFrom(VcenterDataCenterService.class) || _resourceClazz.isAssignableFrom(InitiatorService.class) || _resourceClazz.isAssignableFrom(IpInterfaceService.class) || _resourceClazz.isAssignableFrom(ClusterService.class)) { // do nothing, if there is no project association, there are no ACLs } else { throw new RuntimeException("undefined permission check on resource: " + _resourceClazz); } } } return acls; } /** * Get tenant ids from the uri * * @return */ @Override protected Set<URI> getTenantIdsFromURI(UriInfo uriInfo) { if (_resourceClazz.isAssignableFrom(VcenterService.class)) { String uriStr = uriInfo.getPathParameters().getFirst("id"); if (uriStr != null && !uriStr.isEmpty()) { return _permissionsHelper.getTenantResourceTenantIds(uriStr); } } return null; } } /** * License filter for apisvc */ private class ApisvcLicenseFilter extends AbstractLicenseFilter { @Override public ContainerRequest filter(ContainerRequest request) { if (!_permissionsHelper.hasAnyLicense()) { throw APIException.forbidden.noLicenseFound(); } return request; } } /** * Setter for permissions helper object * * @param permissionsHelper */ public void setPermissionsHelper(PermissionsHelper permissionsHelper) { _permissionsHelper = permissionsHelper; } /** * Setter for disabling license check * * @param disableLicenseCheck */ public void setDisableLicenseCheck(boolean disableLicenseCheck) { this.disableLicenseCheck = disableLicenseCheck; } public void setIsStandby(boolean isStandby) { this.isStandby = isStandby; } @Override protected boolean isSecurityDisabled() { return (_disabler != null); } @Override protected boolean isLicenseCheckDisabled() { return disableLicenseCheck; } @Override protected ResourceFilter getPreFilter() { return new StandbyApisvcFilter(); } @Override protected ResourceFilter getPostFilter() { return null; } @Override protected AbstractPermissionFilter getPermissionsFilter(Role[] roles, ACL[] acls, boolean blockProxies, Class resourceClazz) { return new ApisvcPermissionFilter(roles, acls, blockProxies, resourceClazz, _permissionsHelper); } @Override protected AbstractLicenseFilter getLicenseFilter() { return new ApisvcLicenseFilter(); } /** * Request filter for apisvc on standby node. We disable all post request except bulk API and DR API */ private class StandbyApisvcFilter implements ResourceFilter, ContainerRequestFilter { @Override public ContainerRequest filter(ContainerRequest request) { // allow all request on active site // use a injected variable rather than querying with DrUtil every time // because if a ZK quorum is lost on the active site all the ZK accesses will fail // note that readonly mode is not enabled on the active site. if (!isStandby) { return request; } String path = request.getPath(); // allow all requests for DR if (path.startsWith("site")) { return request; } String method = request.getMethod(); // allow keystore related operation if (path.contains("keystore")) { return request; } // allow all GET request or bulk request if (method.equalsIgnoreCase("GET") || path.endsWith("/bulk")) { return request; } // disallowed operation Site activeSite = drUtil.getActiveSite(); throw APIException.forbidden.disallowOperationOnDrStandby(activeSite.getVipEndPoint()); } @Override public ContainerRequestFilter getRequestFilter() { return this; } @Override public ContainerResponseFilter getResponseFilter() { return null; } } }