/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.impl; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.easymock.classextension.EasyMock.*; import static org.easymock.EasyMock.*; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.Predicates; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.util.CloseableIterator; import org.geoserver.catalog.util.CloseableIteratorAdapter; import org.geoserver.security.CatalogMode; import org.geoserver.security.DataAccessLimits; import org.geoserver.security.ResourceAccessManager; import org.geoserver.security.SecureCatalogImpl; import org.geoserver.security.VectorAccessLimits; import org.geoserver.security.WorkspaceAccessLimits; import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.opengis.filter.Filter; import org.opengis.filter.sort.SortBy; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; public class SecureCatalogImplFilterTest { Authentication anonymous = new TestingAuthenticationToken("anonymous", null); ResourceAccessManager manager; static <T> List<T> collectAndClose(CloseableIterator<T> it) throws IOException { if(it==null) return null; try { LinkedList<T> list = new LinkedList<T>(); while(it.hasNext()) { list.add(it.next()); } return list; } finally { it.close(); } } static <T> CloseableIterator<T> makeCIterator(List<T> source, Filter f) { return CloseableIteratorAdapter.filter(source.iterator(), f); } FeatureTypeInfo createMockFeatureType(String name, WorkspaceInfo ws, CatalogMode mode, Filter mockFilter, boolean read, boolean write) { DataStoreInfo mockStoreInfo = createMock(DataStoreInfo.class); FeatureTypeInfo mockFTInfo = createMock(FeatureTypeInfo.class); expect(mockFTInfo.getName()).andStubReturn(name); expect(mockFTInfo.getStore()).andStubReturn(mockStoreInfo); expect(mockStoreInfo.getWorkspace()).andStubReturn(ws); replay(mockStoreInfo); expect(manager.getAccessLimits(eq(anonymous), eq(mockFTInfo))).andStubReturn(new VectorAccessLimits(mode, null, null, null, null)); expect(mockFilter.evaluate(mockFTInfo)).andStubReturn(read||mode==CatalogMode.CHALLENGE); return mockFTInfo; } static Matcher<FeatureTypeInfo> matchFT(String name, WorkspaceInfo ws) { return allOf( hasProperty("name", is(name)), hasProperty("store", hasProperty("workspace", is(ws))) ); } @Test public void testFeatureTypeList() throws Exception { Catalog catalog = createMock(Catalog.class); manager = createMock(ResourceAccessManager.class); Filter mockFilter = createMock(Filter.class); expect(manager.getSecurityFilter(eq(anonymous), eq(FeatureTypeInfo.class))).andStubReturn(mockFilter); //TODO final Capture<Filter> filterCapture = new Capture<Filter>(); final List<FeatureTypeInfo> source = new ArrayList<FeatureTypeInfo>(); WorkspaceInfo mockWSInfo = createMock(WorkspaceInfo.class); expect(manager.getAccessLimits(eq(anonymous), eq(mockWSInfo))).andStubReturn(new WorkspaceAccessLimits(CatalogMode.HIDE, true, false, false)); FeatureTypeInfo mockFTInfo = createMockFeatureType("foo", mockWSInfo, CatalogMode.HIDE, mockFilter, true, false); source.add(mockFTInfo); replay(mockFTInfo); mockFTInfo = createMockFeatureType("bar", mockWSInfo, CatalogMode.HIDE, mockFilter, false, false); source.add(mockFTInfo); replay(mockFTInfo); mockFTInfo = createMockFeatureType("baz", mockWSInfo, CatalogMode.CHALLENGE, mockFilter, false, false); source.add(mockFTInfo); replay(mockFTInfo); expect(catalog.list( eq(FeatureTypeInfo.class), capture(filterCapture), (Integer)isNull(), (Integer)isNull(), (SortBy)isNull())).andStubAnswer(new IAnswer<CloseableIterator<FeatureTypeInfo>>() { @Override public CloseableIterator<FeatureTypeInfo> answer() throws Throwable { Filter filter = filterCapture.getValue(); return CloseableIteratorAdapter.filter(source.iterator(), filter); } }); replay(catalog, manager, mockFilter); @SuppressWarnings("serial") SecureCatalogImpl sc = new SecureCatalogImpl(catalog, manager) { // Calls static method we can't mock @Override protected boolean isAdmin(Authentication authentication) { return false; } // Not relevant to the test ad complicates things due to static calls @Override protected <T extends CatalogInfo> T checkAccess( Authentication user, T info, MixedModeBehavior mixedModeBehavior) { return info; } }; // use no user at all SecurityContextHolder.getContext().setAuthentication(anonymous); List<FeatureTypeInfo> ftResult = collectAndClose(sc.list(FeatureTypeInfo.class, Predicates.acceptAll())); WorkspaceInfo foo = ftResult.get(0).getStore().getWorkspace(); assertThat( ftResult, contains( matchFT("foo", mockWSInfo), matchFT("baz", mockWSInfo) ) ); verify(catalog, manager); } }