/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.dataaccess.datasource.api;
import com.google.common.collect.Sets;
import com.sun.jersey.core.header.FormDataContentDisposition;
import org.apache.commons.io.IOUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.platform.api.engine.IAuthorizationPolicy;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.api.repository2.unified.IPlatformImportBundle;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl;
import org.pentaho.platform.api.repository2.unified.RepositoryFileSid;
import org.pentaho.platform.dataaccess.datasource.wizard.service.impl.IDataAccessPermissionHandler;
import org.pentaho.platform.plugin.action.mondrian.catalog.IAclAwareMondrianCatalogService;
import org.pentaho.platform.plugin.action.mondrian.catalog.IMondrianCatalogService;
import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalog;
import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCube;
import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianSchema;
import org.pentaho.platform.plugin.services.importer.IPlatformImporter;
import org.pentaho.platform.plugin.services.importer.PlatformImportException;
import org.pentaho.platform.plugin.services.importer.RepositoryFileImportBundle;
import org.pentaho.platform.repository2.unified.fs.FileSystemBackedUnifiedRepository;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileAclAdapter;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileAclDto;
import org.pentaho.platform.security.policy.rolebased.actions.AdministerSecurityAction;
import org.pentaho.platform.security.policy.rolebased.actions.RepositoryCreateAction;
import org.pentaho.platform.security.policy.rolebased.actions.RepositoryReadAction;
import org.pentaho.test.platform.engine.core.MicroPlatform;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.*;
public class AnalysisServiceTest {
private static IMetadataDomainRepository metadataRepository;
private static IPlatformImporter importer;
private static IAuthorizationPolicy policy;
private static IAclAwareMondrianCatalogService catalogService;
private static IDataAccessPermissionHandler permissionHandler;
private static final RepositoryFileAclDto acl = new RepositoryFileAclDto();
@BeforeClass
public static void initPlatform() throws Exception {
MicroPlatform platform = new MicroPlatform();
metadataRepository = mock( IMetadataDomainRepository.class );
platform.defineInstance( IMetadataDomainRepository.class, metadataRepository );
importer = mock( IPlatformImporter.class );
platform.defineInstance( IPlatformImporter.class, importer );
policy = mock( IAuthorizationPolicy.class );
platform.defineInstance( IAuthorizationPolicy.class, policy );
catalogService = mock( IAclAwareMondrianCatalogService.class );
platform.defineInstance( IMondrianCatalogService.class, catalogService );
permissionHandler = mock( IDataAccessPermissionHandler.class );
platform.defineInstance( IDataAccessPermissionHandler.class, permissionHandler );
final IUnifiedRepository unifiedRepository = new FileSystemBackedUnifiedRepository( "target/test-classes/solution1" );
platform.defineInstance( IUnifiedRepository.class, unifiedRepository );
platform.start();
acl.setOwner( "owner" );
acl.setOwnerType( RepositoryFileSid.Type.USER.ordinal() );
}
@After
public void tearDown() throws Exception {
Mockito.reset( metadataRepository, importer, policy, catalogService, permissionHandler );
}
private void policyAccess() {
when( policy.isAllowed( any( String.class ) ) ).thenReturn( true );
}
private void allAccess() {
policyAccess();
when( permissionHandler.hasDataAccessPermission( any( IPentahoSession.class ) ) ).thenReturn( true );
}
private InputStream getSchemaAsStream() {
return getClass().getResourceAsStream( "schema.xml" );
}
@Test
public void testImportingSchemaRemovesExistingAnnotationsByDefault() throws Exception {
MondrianCatalog otherCatalog = new MondrianCatalog( "other", "", "", null );
MondrianCatalog salesCatalog = new MondrianCatalog( "sales", "", "", null );
when( catalogService.listCatalogs( any( IPentahoSession.class ), eq( false ) ) )
.thenReturn( Arrays.asList( otherCatalog, salesCatalog ) );
putMondrianSchemaWithSchemaFileName( "stubFileName", "overwrite=true" );
verify( importer ).importFile( argThat( matchBundle( true, acl ) ) );
verify( catalogService ).removeCatalog( eq( "sales" ), any( IPentahoSession.class ) );
}
@Test
public void testImportingSkipsRemoveWhenNotPresent() throws Exception {
MondrianCatalog otherCatalog = new MondrianCatalog( "other", "", "", null );
when( catalogService.listCatalogs( any( IPentahoSession.class ), eq( false ) ) )
.thenReturn( Collections.singletonList( otherCatalog ) );
putMondrianSchemaWithSchemaFileName( "stubFileName", "overwrite=true" );
verify( importer ).importFile( argThat( matchBundle( true, acl ) ) );
verify( catalogService, never() ).removeCatalog( eq( "sales" ), any( IPentahoSession.class ) );
}
@Test
public void testImportingSchemaCanRetainAnnotations() throws Exception {
putMondrianSchemaWithSchemaFileName( "stubFileName" );
verify( importer ).importFile( argThat( matchBundle( true, acl ) ) );
verify( catalogService, Mockito.times( 0 ) ).removeCatalog( eq( "sales" ), any( IPentahoSession.class ) );
}
@Test
public void testImportingSchemaWillNotOverwrite() throws Exception {
putMondrianSchemaWithSchemaFileName( "stubFileName", "overwrite=false;retainInlineAnnotations=true" );
verify( importer ).importFile( argThat( matchBundle( false, acl ) ) );
verify( catalogService, never() ).removeCatalog( eq( "sales" ), any( IPentahoSession.class ) );
}
private BaseMatcher<IPlatformImportBundle> matchBundle( final boolean overwrite, final RepositoryFileAclDto acl ) {
return new BaseMatcher<IPlatformImportBundle>() {
@Override
public void describeTo( final Description description ) {
}
@Override
public boolean matches( final Object item ) {
RepositoryFileImportBundle bundle = (RepositoryFileImportBundle) item;
return bundle.getName().equals( "sales" ) && bundle.isOverwriteInRepository() == overwrite && bundle.getAcl()
.equals( new RepositoryFileAclAdapter().unmarshal( acl ) );
}
};
}
@Test
public void testDoGetAnalysisFilesAsDownload() throws Exception {
allAccess();
final Map<String, InputStream> sampleData = new AnalysisService().doGetAnalysisFilesAsDownload( "SampleData" );
assertEquals( 1, sampleData.size() );
final FileInputStream inputStream = new FileInputStream( "target/test-classes/solution1/etc/mondrian/SampleData/schema.xml" );
assertEquals( IOUtils.toString( inputStream ), IOUtils.toString( sampleData.get( "SampleData.mondrian.xml" ) ) );
}
@Test( expected = PentahoAccessControlException.class )
public void testDoGetAnalysisFilesAsDownloadError() throws Exception {
when( policy.isAllowed( any( String.class ) ) ).thenReturn( true );
when( permissionHandler.hasDataAccessPermission( any( IPentahoSession.class ) ) ).thenReturn( false );
new AnalysisService().doGetAnalysisFilesAsDownload( "SampleData" );
}
@Test
public void testRemoveAnalysisRequiresMultiplePermissions() throws Exception {
testRemove( true, true, true );
testRemove( false, false, false );
testRemove( true, true, false );
testRemove( true, false, true );
testRemove( false, true, true );
testRemove( false, false, true );
testRemove( true, false, false );
}
private void testRemove( final boolean hasRead, final boolean hasCreate, final boolean hasAdmin )
throws PentahoAccessControlException {
when( policy.isAllowed( RepositoryReadAction.NAME ) ).thenReturn( hasRead );
when( policy.isAllowed( RepositoryCreateAction.NAME ) ).thenReturn( hasCreate );
when( policy.isAllowed( AdministerSecurityAction.NAME ) ).thenReturn( hasAdmin );
if ( hasRead && hasCreate && hasAdmin ) {
when( policy.isAllowed( any( String.class ) ) ).thenReturn( true );
}
try {
new AnalysisService().removeAnalysis( "analysisId" );
if ( hasRead && hasCreate && hasAdmin ) {
verify( catalogService ).removeCatalog( eq( "analysisId" ), any( IPentahoSession.class ) );
} else {
fail( "should have got exception" );
}
} catch ( PentahoAccessControlException e ) {
if ( hasRead && hasCreate && hasAdmin ) {
fail( "should not have got exception" );
} else {
verify( catalogService, never() ).removeCatalog( any( String.class ), any( IPentahoSession.class ) );
}
}
Mockito.reset( policy, catalogService );
}
@Test
public void testGetAnalysisDatasourceIds() throws Exception {
final MondrianCatalog foodmartCatalog = new MondrianCatalog( "foodmart", "info", "file:///place",
new MondrianSchema( "foodmart", Collections.<MondrianCube>emptyList() ) );
final MondrianCatalog foodmartCatalog2 = new MondrianCatalog( "foodmart2", "info", "file:///place",
new MondrianSchema( "foodmart2", Collections.<MondrianCube>emptyList() ) );
final List<MondrianCatalog> catalogs = Arrays.asList( foodmartCatalog, foodmartCatalog2 );
doReturn( catalogs ).when( catalogService ).listCatalogs( any( IPentahoSession.class ), eq( false ) );
final HashSet<String> domainIds = Sets.newHashSet( "foodmart.xmi", "sample.xmi" );
doReturn( domainIds ).when( metadataRepository ).getDomainIds();
final List<String> response = new AnalysisService().getAnalysisDatasourceIds();
assertEquals( Collections.singletonList( "foodmart2" ), response );
}
@Test
public void testGetAnalysisDatasourceAcl() throws Exception {
allAccess();
final String catalogName = "SampleData";
final RepositoryFileAcl expectedAcl = new RepositoryFileAcl.Builder( "owner" ).build();
when( catalogService.getAclFor( catalogName ) ).thenReturn( expectedAcl );
final MondrianCatalog mondrianCatalog = mock( MondrianCatalog.class );
when( catalogService.getCatalog( eq( catalogName ), any( IPentahoSession.class ) ) ).thenReturn( mondrianCatalog );
final RepositoryFileAclDto actualAcl = new AnalysisService().getAnalysisDatasourceAcl( catalogName );
assertEquals( expectedAcl, new RepositoryFileAclAdapter().unmarshal( actualAcl ) );
}
@Test
public void testGetAnalysisDatasourceAclNoAcl() throws Exception {
allAccess();
final String catalogName = "catalogName";
final MondrianCatalog mondrianCatalog = mock( MondrianCatalog.class );
when( catalogService.getCatalog( eq( catalogName ), any( IPentahoSession.class ) ) ).thenReturn( mondrianCatalog );
when( catalogService.getAclFor( catalogName ) ).thenReturn( null );
final RepositoryFileAclDto aclDto = new AnalysisService().getAnalysisDatasourceAcl( catalogName );
assertNull( aclDto );
}
@Test
public void testSetAnalysisDatasourceAcl() throws Exception {
allAccess();
final String catalogName = "catalogName";
final RepositoryFileAclDto aclDto = new RepositoryFileAclDto();
aclDto.setOwner( "owner" );
aclDto.setOwnerType( RepositoryFileSid.Type.USER.ordinal() );
final MondrianCatalog mondrianCatalog = mock( MondrianCatalog.class );
when( catalogService.getCatalog( eq( catalogName ), any( IPentahoSession.class ) ) ).thenReturn( mondrianCatalog );
new AnalysisService().setAnalysisDatasourceAcl( catalogName, aclDto );
verify( catalogService ).setAclFor( eq( catalogName ), eq( new RepositoryFileAclAdapter().unmarshal( aclDto ) ) );
}
@Test
public void testSetAnalysisDatasourceAclNoAcl() throws Exception {
allAccess();
String catalogName = "catalogName";
final MondrianCatalog mondrianCatalog = mock( MondrianCatalog.class );
when( catalogService.getCatalog( eq( catalogName ), any( IPentahoSession.class ) ) ).thenReturn( mondrianCatalog );
new AnalysisService().setAnalysisDatasourceAcl( catalogName, null );
verify( catalogService ).setAclFor( eq( catalogName ), (RepositoryFileAcl) isNull() );
}
@Test( expected = PlatformImportException.class )
public void testPutNullMondrianSchema() throws Exception {
putMondrianSchemaWithSchemaFileName( null );
}
@Test( expected = PlatformImportException.class )
public void testPutXmiMondrianSchema() throws Exception {
putMondrianSchemaWithSchemaFileName( "sample.xmi" );
}
@Test
public void testPutEmptyMondrianSchema() throws Exception {
putMondrianSchemaWithSchemaFileName( "" );
}
private void putMondrianSchemaWithSchemaFileName( String fileName ) throws Exception {
String params = "overwrite=true;retainInlineAnnotations=true";
putMondrianSchemaWithSchemaFileName( fileName, params );
}
private void putMondrianSchemaWithSchemaFileName( String fileName, String parameters ) throws Exception {
policyAccess();
FormDataContentDisposition schemaFileInfoMock = mock( FormDataContentDisposition.class );
if ( fileName != null ) {
when( schemaFileInfoMock.getFileName() ).thenReturn( fileName );
}
InputStream schema = getSchemaAsStream();
new AnalysisService()
.putMondrianSchema( schema, schemaFileInfoMock, "sample", null, "sample", true, false, parameters, acl );
}
}