/*! * 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.test.platform.web.http.api; import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM; import static javax.ws.rs.core.MediaType.TEXT_PLAIN; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; import static org.pentaho.test.platform.web.http.api.JerseyTestUtil.assertResponse; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.jcr.Repository; import javax.ws.rs.core.Response; import junit.framework.TestCase; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.pentaho.platform.api.engine.IAuthorizationPolicy; import org.pentaho.platform.api.engine.IPentahoDefinableObjectFactory.Scope; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IUserRoleListService; import org.pentaho.platform.api.engine.security.userroledao.IPentahoRole; import org.pentaho.platform.api.engine.security.userroledao.IPentahoUser; import org.pentaho.platform.api.engine.security.userroledao.IUserRoleDao; import org.pentaho.platform.api.mt.ITenant; import org.pentaho.platform.api.mt.ITenantManager; import org.pentaho.platform.api.mt.ITenantedPrincipleNameResolver; import org.pentaho.platform.api.repository2.unified.IBackingRepositoryLifecycleManager; import org.pentaho.platform.api.repository2.unified.IRepositoryVersionManager; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.core.mt.Tenant; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.StandaloneSession; import org.pentaho.platform.engine.core.system.boot.PlatformInitializationException; import org.pentaho.platform.plugin.services.importer.NameBaseMimeResolver; import org.pentaho.platform.repository2.ClientRepositoryPaths; import org.pentaho.platform.repository2.mt.RepositoryTenantManager; import org.pentaho.platform.repository2.unified.DefaultRepositoryVersionManager; import org.pentaho.platform.repository2.unified.IRepositoryFileDao; import org.pentaho.platform.repository2.unified.ServerRepositoryPaths; import org.pentaho.platform.repository2.unified.jcr.RepositoryFileProxyFactory; import org.pentaho.platform.repository2.unified.jcr.SimpleJcrTestUtils; import org.pentaho.platform.repository2.unified.jcr.jackrabbit.security.TestPrincipalProvider; import org.pentaho.platform.repository2.unified.jcr.sejcr.CredentialsStrategy; import org.pentaho.platform.security.policy.rolebased.IRoleAuthorizationPolicyRoleBindingDao; import org.pentaho.platform.security.policy.rolebased.RoleAuthorizationPolicy; import org.pentaho.platform.security.userroledao.service.UserRoleDaoUserDetailsService; import org.pentaho.platform.security.userroledao.service.UserRoleDaoUserRoleListService; import org.pentaho.test.platform.engine.core.MicroPlatform; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.extensions.jcr.JcrTemplate; import org.springframework.extensions.jcr.SessionFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse.Status; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.test.framework.AppDescriptor; import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.WebAppDescriptor; import com.sun.jersey.test.framework.spi.container.TestContainerFactory; import com.sun.jersey.test.framework.spi.container.grizzly.GrizzlyTestContainerFactory; import com.sun.jersey.test.framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory; @RunWith ( SpringJUnit4ClassRunner.class ) @ContextConfiguration ( locations = { "classpath:/repository.spring.xml", "classpath:/repository-test-override.spring.xml" } ) @SuppressWarnings ( "nls" ) public class DirectoryResourceIT extends JerseyTest implements ApplicationContextAware { private static MicroPlatform mp = new MicroPlatform(); private static WebAppDescriptor webAppDescriptor = new WebAppDescriptor.Builder( "org.pentaho.platform.web.http.api.resources" ).contextPath( "api" ).build(); public static final String MAIN_TENANT_1 = "maintenant1"; private IUnifiedRepository repo; private IUserRoleListService userRoleListService; private boolean startupCalled; private String repositoryAdminUsername; private String adminAuthorityName; private String authenticatedAuthorityName; private JcrTemplate testJcrTemplate; private IBackingRepositoryLifecycleManager manager; private IAuthorizationPolicy authorizationPolicy; IUserRoleDao userRoleDao; private ITenantManager tenantManager; private String sysAdminAuthorityName; private String sysAdminUserName; private IRepositoryFileDao repositoryFileDao; private ITenantedPrincipleNameResolver tenantedRoleNameUtils; private ITenantedPrincipleNameResolver tenantedUserNameUtils; private IRoleAuthorizationPolicyRoleBindingDao roleBindingDaoTarget; public static final String SYSTEM_PROPERTY = "spring.security.strategy"; public DirectoryResourceIT() throws Exception { super(); this.setTestContainerFactory( new GrizzlyTestContainerFactory() ); mp.setFullyQualifiedServerUrl( getBaseURI() + webAppDescriptor.getContextPath() + "/" ); } protected AppDescriptor configure() { return webAppDescriptor; } protected TestContainerFactory getTestContainerFactory() { return new GrizzlyWebTestContainerFactory(); } @BeforeClass public static void beforeClass() throws Exception { System.setProperty( SYSTEM_PROPERTY, "MODE_GLOBAL" ); PentahoSessionHolder.setStrategyName( PentahoSessionHolder.MODE_GLOBAL ); FileUtils.deleteDirectory( new File( "/tmp/jackrabbit-test-TRUNK" ) ); SecurityContextHolder.setStrategyName( SecurityContextHolder.MODE_GLOBAL ); } @AfterClass public static void afterClass() { PentahoSessionHolder.setStrategyName( PentahoSessionHolder.MODE_GLOBAL ); SecurityContextHolder.setStrategyName( SecurityContextHolder.MODE_GLOBAL ); } private void cleanupUserAndRoles( final ITenant tenant ) { loginAsRepositoryAdmin(); for ( IPentahoRole role : userRoleDao.getRoles( tenant ) ) { userRoleDao.deleteRole( role ); } for ( IPentahoUser user : userRoleDao.getUsers( tenant ) ) { userRoleDao.deleteUser( user ); } } @Before public void beforeTest() throws PlatformInitializationException { mp = new MicroPlatform(); // used by DefaultPentahoJackrabbitAccessControlHelper mp.defineInstance( IAuthorizationPolicy.class, authorizationPolicy ); mp.defineInstance( ITenantManager.class, tenantManager ); mp.define( ITenant.class, Tenant.class ); mp.defineInstance( "roleAuthorizationPolicyRoleBindingDaoTarget", roleBindingDaoTarget ); mp.defineInstance( IRoleAuthorizationPolicyRoleBindingDao.class, roleBindingDaoTarget ); mp.defineInstance( "tenantedUserNameUtils", tenantedUserNameUtils ); mp.defineInstance( "tenantedRoleNameUtils", tenantedRoleNameUtils ); mp.defineInstance( "repositoryAdminUsername", repositoryAdminUsername ); mp.define( IRoleAuthorizationPolicyRoleBindingDao.class, RoleAuthorizationPolicy.class, Scope.GLOBAL ); mp.define( ITenantManager.class, RepositoryTenantManager.class, Scope.GLOBAL ); mp.defineInstance( "singleTenantAdminAuthorityName", new String( "Administrator" ) ); mp.defineInstance( "RepositoryFileProxyFactory", new RepositoryFileProxyFactory( this.testJcrTemplate, this.repositoryFileDao ) ); DefaultRepositoryVersionManager defaultRepositoryVersionManager = new DefaultRepositoryVersionManager(); defaultRepositoryVersionManager.setPlatformMimeResolver( new NameBaseMimeResolver() ); mp.defineInstance( IRepositoryVersionManager.class, defaultRepositoryVersionManager ); UserRoleDaoUserDetailsService userDetailsService = new UserRoleDaoUserDetailsService(); userDetailsService.setUserRoleDao( userRoleDao ); List<String> systemRoles = new ArrayList<String>(); systemRoles.add( "Admin" ); List<String> extraRoles = Arrays.asList( new String[]{"Authenticated", "Anonymous"} ); String adminRole = "Admin"; userRoleListService = new UserRoleDaoUserRoleListService( userRoleDao, userDetailsService, tenantedUserNameUtils, systemRoles, extraRoles, adminRole ); ( (UserRoleDaoUserRoleListService) userRoleListService ).setUserRoleDao( userRoleDao ); ( (UserRoleDaoUserRoleListService) userRoleListService ).setUserDetailsService( userDetailsService ); mp.defineInstance( IUserRoleListService.class, userRoleListService ); mp.start(); logout(); startupCalled = true; SecurityContextHolder.setStrategyName( SecurityContextHolder.MODE_GLOBAL ); } @After public void afterTest() throws Exception { clearRoleBindings(); // null out fields to get back memory authorizationPolicy = null; loginAsRepositoryAdmin(); SimpleJcrTestUtils.deleteItem( testJcrTemplate, ServerRepositoryPaths.getPentahoRootFolderPath() ); logout(); repositoryAdminUsername = null; adminAuthorityName = null; authenticatedAuthorityName = null; authorizationPolicy = null; testJcrTemplate = null; if ( startupCalled ) { manager.shutdown(); } mp.stop(); // null out fields to get back memory repo = null; } protected void clearRoleBindings() throws Exception { loginAsRepositoryAdmin(); } protected void createTestFile( String pathId, String text ) { WebResource webResource = resource(); ClientResponse response = webResource.path( "repo/files/" + pathId ).type( TEXT_PLAIN ).put( ClientResponse.class, text ); assertResponse( response, Status.OK ); } protected void createTestFileBinary( String pathId, byte[] data ) { WebResource webResource = resource(); ClientResponse response = webResource.path( "repo/files/" + pathId ).type( APPLICATION_OCTET_STREAM ) .put( ClientResponse.class, new String( data ) ); assertResponse( response, Status.OK ); } protected void createTestFolder( String pathId ) { WebResource webResource = resource(); // webResource.path("repo/dirs/" + pathId).put(); ClientResponse response = webResource.path( "repo/dirs/" + pathId ).type( TEXT_PLAIN ).put( ClientResponse.class ); assertResponse( response, Status.OK ); } public void setApplicationContext( final ApplicationContext applicationContext ) throws BeansException { manager = (IBackingRepositoryLifecycleManager) applicationContext.getBean( "backingRepositoryLifecycleManager" ); SessionFactory jcrSessionFactory = (SessionFactory) applicationContext.getBean( "jcrSessionFactory" ); testJcrTemplate = new JcrTemplate( jcrSessionFactory ); testJcrTemplate.setAllowCreate( true ); testJcrTemplate.setExposeNativeSession( true ); repositoryAdminUsername = (String) applicationContext.getBean( "repositoryAdminUsername" ); authenticatedAuthorityName = (String) applicationContext.getBean( "singleTenantAuthenticatedAuthorityName" ); adminAuthorityName = (String) applicationContext.getBean( "singleTenantAdminAuthorityName" ); sysAdminAuthorityName = (String) applicationContext.getBean( "superAdminAuthorityName" ); sysAdminUserName = (String) applicationContext.getBean( "superAdminUserName" ); authorizationPolicy = (IAuthorizationPolicy) applicationContext.getBean( "authorizationPolicy" ); roleBindingDaoTarget = (IRoleAuthorizationPolicyRoleBindingDao) applicationContext .getBean( "roleAuthorizationPolicyRoleBindingDaoTarget" ); tenantManager = (ITenantManager) applicationContext.getBean( "tenantMgrTxn" ); repositoryFileDao = (IRepositoryFileDao) applicationContext.getBean( "repositoryFileDao" ); userRoleDao = (IUserRoleDao) applicationContext.getBean( "userRoleDaoTxn" ); tenantedUserNameUtils = (ITenantedPrincipleNameResolver) applicationContext.getBean( "tenantedUserNameUtils" ); tenantedRoleNameUtils = (ITenantedPrincipleNameResolver) applicationContext.getBean( "tenantedRoleNameUtils" ); repo = (IUnifiedRepository) applicationContext.getBean( "unifiedRepository" ); TestPrincipalProvider.userRoleDao = (IUserRoleDao) applicationContext.getBean( "userRoleDaoTxn" ); TestPrincipalProvider.adminCredentialsStrategy = (CredentialsStrategy) applicationContext.getBean( "jcrAdminCredentialsStrategy" ); TestPrincipalProvider.repository = (Repository) applicationContext.getBean( "jcrRepository" ); } protected void loginAsRepositoryAdmin() { StandaloneSession pentahoSession = new StandaloneSession( repositoryAdminUsername ); pentahoSession.setAuthenticated( repositoryAdminUsername ); final List<GrantedAuthority> repositoryAdminAuthorities = Arrays.asList( new GrantedAuthority[] { new SimpleGrantedAuthority( sysAdminAuthorityName ) } ); final String password = "ignored"; UserDetails repositoryAdminUserDetails = new User( repositoryAdminUsername, password, true, true, true, true, repositoryAdminAuthorities ); Authentication repositoryAdminAuthentication = new UsernamePasswordAuthenticationToken( repositoryAdminUserDetails, password, repositoryAdminAuthorities ); PentahoSessionHolder.setSession( pentahoSession ); // this line necessary for Spring Security's MethodSecurityInterceptor SecurityContextHolder.getContext().setAuthentication( repositoryAdminAuthentication ); } protected void logout() { PentahoSessionHolder.removeSession(); SecurityContextHolder.getContext().setAuthentication( null ); } protected void login( final String username, final ITenant tenant ) { login( username, tenant, false ); } /** * Logs in with given username. * * @param username username of user * @param tenant tenant to which this user belongs * @tenantAdmin true to add the tenant admin authority to the user's roles */ protected void login( final String username, final ITenant tenant, String[] roles ) { StandaloneSession pentahoSession = new StandaloneSession( username ); pentahoSession.setAuthenticated( tenant.getId(), username ); PentahoSessionHolder.setSession( pentahoSession ); pentahoSession.setAttribute( IPentahoSession.TENANT_ID_KEY, tenant.getId() ); final String password = "password"; List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); for ( String roleName : roles ) { authList.add( new SimpleGrantedAuthority( roleName ) ); } UserDetails userDetails = new User( username, password, true, true, true, true, authList ); Authentication auth = new UsernamePasswordAuthenticationToken( userDetails, password, authList ); PentahoSessionHolder.setSession( pentahoSession ); // this line necessary for Spring Security's MethodSecurityInterceptor SecurityContextHolder.getContext().setAuthentication( auth ); } /** * Logs in with given username. * * @param username username of user * @param tenant tenant to which this user belongs * @tenantAdmin true to add the tenant admin authority to the user's roles */ protected void login( final String username, final ITenant tenant, final boolean tenantAdmin ) { StandaloneSession pentahoSession = new StandaloneSession( username ); pentahoSession.setAuthenticated( username ); pentahoSession.setAttribute( IPentahoSession.TENANT_ID_KEY, tenant.getId() ); final String password = "password"; List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); authList.add( new SimpleGrantedAuthority( authenticatedAuthorityName ) ); if ( tenantAdmin ) { authList.add( new SimpleGrantedAuthority( adminAuthorityName ) ); } UserDetails userDetails = new User( username, password, true, true, true, true, authList ); Authentication auth = new UsernamePasswordAuthenticationToken( userDetails, password, authList ); PentahoSessionHolder.setSession( pentahoSession ); // this line necessary for Spring Security's MethodSecurityInterceptor SecurityContextHolder.getContext().setAuthentication( auth ); } @Test public void testCreateDir() { loginAsRepositoryAdmin(); ITenant systemTenant = tenantManager.createTenant( null, ServerRepositoryPaths.getPentahoRootFolderName(), adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( systemTenant, sysAdminUserName, "password", "", new String[] { adminAuthorityName } ); ITenant mainTenant_1 = tenantManager.createTenant( systemTenant, MAIN_TENANT_1, adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( mainTenant_1, "admin", "password", "", new String[] { adminAuthorityName } ); try { login( "admin", mainTenant_1, new String[] { authenticatedAuthorityName } ); // set object in PentahoSystem mp.defineInstance( IUnifiedRepository.class, repo ); WebResource webResource = resource(); String publicFolderPath = ClientRepositoryPaths.getPublicFolderPath(); String newDirPathId = publicFolderPath.replaceAll( "/", ":" ) + ":testDir"; // Create directory. Success is expected. webResource.path( "repo/dirs/" + newDirPathId ).put(); // Create duplicate directory. CONFLICT (409) is expected. try { webResource.path( "repo/dirs/" + newDirPathId ).put(); fail( "CONFLICT is expected" ); } catch ( UniformInterfaceException e ) { assertEquals( Response.Status.CONFLICT.getStatusCode(), e.getResponse().getStatus() ); } } catch ( AssertionError assertion ) { throw assertion; } catch ( Throwable ex ) { TestCase.fail( ex.getMessage() ); } finally { cleanupUserAndRoles( mainTenant_1 ); cleanupUserAndRoles( systemTenant ); logout(); } } @Test public void testCreateDir_RootLevel() { loginAsRepositoryAdmin(); ITenant systemTenant = tenantManager.createTenant( null, ServerRepositoryPaths.getPentahoRootFolderName(), adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( systemTenant, sysAdminUserName, "password", "", new String[] { adminAuthorityName } ); ITenant mainTenant_1 = tenantManager.createTenant( systemTenant, MAIN_TENANT_1, adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( mainTenant_1, "admin", "password", "", new String[] { adminAuthorityName } ); try { login( "admin", mainTenant_1, new String[] { authenticatedAuthorityName } ); // set object in PentahoSystem mp.defineInstance( IUnifiedRepository.class, repo ); WebResource webResource = resource(); String rootLevelDirPathId = ":testRootLevelDir"; // Create duplicate directory. FORBIDDEN (403) is expected. try { webResource.path( "repo/dirs/" + rootLevelDirPathId ).put(); fail( "FORBIDDEN is expected" ); } catch ( UniformInterfaceException e ) { assertEquals( Response.Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus() ); } } catch ( AssertionError assertion ) { throw assertion; } catch ( Throwable ex ) { TestCase.fail( ex.getMessage() ); } finally { cleanupUserAndRoles( mainTenant_1 ); cleanupUserAndRoles( systemTenant ); logout(); } } @Test public void testCreateDir_ServerError() { loginAsRepositoryAdmin(); ITenant systemTenant = tenantManager.createTenant( null, ServerRepositoryPaths.getPentahoRootFolderName(), adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( systemTenant, sysAdminUserName, "password", "", new String[] { adminAuthorityName } ); ITenant mainTenant_1 = tenantManager.createTenant( systemTenant, MAIN_TENANT_1, adminAuthorityName, authenticatedAuthorityName, "Anonymous" ); userRoleDao.createUser( mainTenant_1, "admin", "password", "", new String[] { adminAuthorityName } ); try { login( "admin", mainTenant_1, new String[] { authenticatedAuthorityName } ); // set object in PentahoSystem mp.defineInstance( IUnifiedRepository.class, repo ); WebResource webResource = resource(); String invalidPathId = "/////"; // Invalid path id. INTERNAL_SERVER_ERROR (500) is expected. try { webResource.path( "repo/dirs/" + invalidPathId ).put(); fail( "INTERNAL_SERVER_ERROR is expected" ); } catch ( UniformInterfaceException e ) { assertEquals( Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getResponse().getStatus() ); } } catch ( AssertionError assertion ) { throw assertion; } catch ( Throwable ex ) { TestCase.fail( ex.getMessage() ); } finally { cleanupUserAndRoles( mainTenant_1 ); cleanupUserAndRoles( systemTenant ); logout(); } } }