diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java index b78370f8ff3c195505e5c6b7788af7d698741c74..2fd82194dd53aaa0bb17edd5774f5bb192dab2d9 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java @@ -732,10 +732,15 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp throw new UserFailureException("It is not allowed to change the user from remote host " + remoteHost); } - PersonPE person = daoFactory.getPersonDAO().tryFindPersonByUserId(userID); + injectPerson(session, userID); + } + + protected void injectPerson(Session session, String personID) + { + PersonPE person = daoFactory.getPersonDAO().tryFindPersonByUserId(personID); if (person == null) { - throw new UserFailureException("Unknown user: " + userID); + throw new UserFailureException("Unknown user: " + personID); } HibernateUtils.initialize(person.getAllPersonRoles()); session.setPerson(person); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java index bc496b785cc7f4cee893c87dbac4f8324a9a7deb..6432556f4ad93d7b9ec4cec718c04b45d82bd234 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java @@ -27,8 +27,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import javax.servlet.http.HttpServletRequest; + import org.apache.commons.lang.StringUtils; +import ch.systemsx.cisd.authentication.DefaultSessionManager; +import ch.systemsx.cisd.authentication.DummyAuthenticationService; import ch.systemsx.cisd.authentication.IAuthenticationService; import ch.systemsx.cisd.authentication.ISessionManager; import ch.systemsx.cisd.common.collections.CollectionUtils; @@ -40,6 +44,8 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.common.serviceconversation.ServiceConversationDTO; import ch.systemsx.cisd.common.serviceconversation.ServiceMessage; import ch.systemsx.cisd.common.serviceconversation.server.ServiceConversationServer; +import ch.systemsx.cisd.common.servlet.IRequestContextProvider; +import ch.systemsx.cisd.common.servlet.RequestContextProviderAdapter; import ch.systemsx.cisd.common.spring.HttpInvokerUtils; import ch.systemsx.cisd.common.spring.IInvocationLoggerContext; import ch.systemsx.cisd.openbis.generic.server.api.v1.SearchCriteriaToDetailedSearchCriteriaTranslator; @@ -72,6 +78,7 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleTypeDAO; import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService; import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; import ch.systemsx.cisd.openbis.generic.shared.IServer; +import ch.systemsx.cisd.openbis.generic.shared.LogMessagePrefixGenerator; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchableEntityKind; import ch.systemsx.cisd.openbis.generic.shared.basic.EntityOperationsState; @@ -191,25 +198,32 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements private final ServiceConversationServer server; + private final IEntityOperationChecker entityOperationChecker; + + private final DefaultSessionManager<Session> sessionManagerForEntityOperation; + public ETLService(IAuthenticationService authenticationService, ISessionManager<Session> sessionManager, IDAOFactory daoFactory, ICommonBusinessObjectFactory boFactory, IDataStoreServiceFactory dssFactory, - TrustedCrossOriginDomainsProvider trustedOriginDomainProvider) + TrustedCrossOriginDomainsProvider trustedOriginDomainProvider, + IEntityOperationChecker entityOperationChecker) { this(authenticationService, sessionManager, daoFactory, null, boFactory, dssFactory, - trustedOriginDomainProvider); + trustedOriginDomainProvider, entityOperationChecker); } ETLService(IAuthenticationService authenticationService, ISessionManager<Session> sessionManager, IDAOFactory daoFactory, IPropertiesBatchManager propertiesBatchManager, ICommonBusinessObjectFactory boFactory, IDataStoreServiceFactory dssFactory, - TrustedCrossOriginDomainsProvider trustedOriginDomainProvider) + TrustedCrossOriginDomainsProvider trustedOriginDomainProvider, + IEntityOperationChecker entityOperationChecker) { super(authenticationService, sessionManager, daoFactory, propertiesBatchManager, boFactory); this.daoFactory = daoFactory; this.dssFactory = dssFactory; this.trustedOriginDomainProvider = trustedOriginDomainProvider; + this.entityOperationChecker = entityOperationChecker; org.hibernate.SessionFactory sessionFactory = daoFactory.getPersistencyResources().getSessionFactoryOrNull(); @@ -217,6 +231,18 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements server = new ServiceConversationServer(); server.addServiceType(new RmiServiceFactory<IETLLIMSService>(server, this, IETLLIMSService.class, PROGRESS_TIMEOUT, sessionFactory)); + sessionManagerForEntityOperation = + new DefaultSessionManager<Session>(new SessionFactory(), + new LogMessagePrefixGenerator(), new DummyAuthenticationService(), + new RequestContextProviderAdapter(new IRequestContextProvider() + { + @Override + public HttpServletRequest getHttpServletRequest() + { + return null; + } + }), 30); + } @Override @@ -1329,33 +1355,44 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements EntityOperationsInProgress.getInstance().addRegistrationPending(registrationId); + String sessionTokenForEntityOperation = null; try { - final Session session = getSession(sessionToken); + Session sessionForEntityOperation = session; + String userId = operationDetails.tryUserIdOrNull(); + if (userId != null) + { + sessionTokenForEntityOperation = + sessionManagerForEntityOperation.tryToOpenSession(userId, "dummy password"); + sessionForEntityOperation = + sessionManagerForEntityOperation.getSession(sessionTokenForEntityOperation); + injectPerson(sessionForEntityOperation, userId); + } - List<Space> spacesCreated = createSpaces(session, operationDetails, progressListener); + List<Space> spacesCreated = + createSpaces(sessionForEntityOperation, operationDetails, progressListener); List<Material> materialsCreated = - createMaterials(session, operationDetails, progressListener); + createMaterials(sessionForEntityOperation, operationDetails, progressListener); List<Project> projectsCreated = - createProjects(session, operationDetails, progressListener); + createProjects(sessionForEntityOperation, operationDetails, progressListener); List<Experiment> experimentsCreated = - createExperiments(session, operationDetails, progressListener); + createExperiments(sessionForEntityOperation, operationDetails, progressListener); List<Sample> samplesCreated = - createSamples(session, operationDetails, progressListener); + createSamples(sessionForEntityOperation, operationDetails, progressListener); List<Sample> samplesUpdated = - updateSamples(session, operationDetails, progressListener); + updateSamples(sessionForEntityOperation, operationDetails, progressListener); List<ExternalData> dataSetsCreated = - createDataSets(session, operationDetails, progressListener); + createDataSets(sessionForEntityOperation, operationDetails, progressListener); List<ExternalData> dataSetsUpdated = - updateDataSets(session, operationDetails, progressListener); + updateDataSets(sessionForEntityOperation, operationDetails, progressListener); // If the id is not null, the caller wants to persist the fact that the operation was // invoked and completed; @@ -1371,6 +1408,10 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements } finally { EntityOperationsInProgress.getInstance().removeRegistrationPending(registrationId); + if (sessionTokenForEntityOperation != null) + { + sessionManagerForEntityOperation.closeSession(sessionTokenForEntityOperation); + } } } @@ -1404,6 +1445,8 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements { ArrayList<SpacePE> spacePEsCreated = new ArrayList<SpacePE>(); List<NewSpace> newSpaces = operationDetails.getSpaceRegistrations(); + assertSpaceCreationAllowed(session, newSpaces); + int index = 0; for (NewSpace newSpace : newSpaces) { @@ -1415,6 +1458,14 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements return SpaceTranslator.translate(spacePEsCreated); } + protected void assertSpaceCreationAllowed(Session session, List<NewSpace> newSpaces) + { + if (newSpaces != null && newSpaces.isEmpty() == false) + { + entityOperationChecker.assertSpaceCreationAllowed(session, newSpaces); + } + } + private List<Material> createMaterials(Session session, AtomicEntityOperationDetails operationDetails, IProgressListener progress) { @@ -1422,6 +1473,7 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements new MaterialHelper(session, businessObjectFactory, getDAOFactory(), getPropertiesBatchManager()); Map<String, List<NewMaterial>> materialRegs = operationDetails.getMaterialRegistrations(); + assertMaterialCreationAllowed(session, materialRegs); List<Material> registeredMaterials = new ArrayList<Material>(); int index = 0; for (Entry<String, List<NewMaterial>> newMaterialsEntry : materialRegs.entrySet()) @@ -1435,6 +1487,15 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements return registeredMaterials; } + protected void assertMaterialCreationAllowed(Session session, + Map<String, List<NewMaterial>> materials) + { + if (materials != null && materials.isEmpty() == false) + { + entityOperationChecker.assertMaterialCreationAllowed(session, materials); + } + } + private SpacePE registerSpaceInternal(Session session, NewSpace newSpace, String registratorUserIdOrNull) { @@ -1472,6 +1533,7 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements { ArrayList<ProjectPE> projectPEsCreated = new ArrayList<ProjectPE>(); List<NewProject> newProjects = operationDetails.getProjectRegistrations(); + assertProjectCreationAllowed(session, newProjects); int index = 0; for (NewProject newProject : newProjects) { @@ -1483,6 +1545,14 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements return ProjectTranslator.translate(projectPEsCreated); } + protected void assertProjectCreationAllowed(Session session, List<NewProject> newProjects) + { + if (newProjects != null && newProjects.isEmpty() == false) + { + entityOperationChecker.assertProjectCreationAllowed(session, newProjects); + } + } + private ProjectPE registerProjectInternal(Session session, NewProject newProject, String registratorUserIdOrNull) { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityOperationChecker.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityOperationChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..9dfbace7bab91899b67a06854342cb33bf556f28 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityOperationChecker.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.server; + +import java.util.List; +import java.util.Map; + +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewProject; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSpace; +import ch.systemsx.cisd.openbis.generic.shared.dto.IAuthSession; +import ch.systemsx.cisd.openbis.generic.shared.dto.Session; + +/** + * Implementation of {@link IEntityOperationChecker} which does nothing because checking is done by + * aspects. + * + * @author Franz-Josef Elmer + */ +public class EntityOperationChecker implements IEntityOperationChecker +{ + + @Override + public void assertSpaceCreationAllowed(IAuthSession session, List<NewSpace> newSpaces) + { + } + + @Override + public void assertMaterialCreationAllowed(IAuthSession session, + Map<String, List<NewMaterial>> materials) + { + } + + @Override + public void assertProjectCreationAllowed(Session session, List<NewProject> newProjects) + { + } + + @Override + public void assertExperimentCreationAllowed(Session session, List<NewExperiment> newExperiments) + { + } + +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/IEntityOperationChecker.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/IEntityOperationChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..3a0c228b74aecc7ea4f42a7752aaec9fc07f7182 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/IEntityOperationChecker.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.server; + +import java.util.List; +import java.util.Map; + +import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.AuthorizationGuard; +import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.Capability; +import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RolesAllowed; +import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.NewExperimentPredicate; +import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.NewProjectPredicate; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewProject; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSpace; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy; +import ch.systemsx.cisd.openbis.generic.shared.dto.IAuthSession; +import ch.systemsx.cisd.openbis.generic.shared.dto.Session; + +/** + * Checking methods to be invoked to check authorization in context of + * {@link ETLService#performEntityOperations(String, ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails)} + * . + * + * @author Franz-Josef Elmer + */ +public interface IEntityOperationChecker +{ + @RolesAllowed(RoleWithHierarchy.INSTANCE_ETL_SERVER) + public void assertSpaceCreationAllowed(IAuthSession session, List<NewSpace> newSpaces); + + @RolesAllowed(RoleWithHierarchy.INSTANCE_ETL_SERVER) + public void assertMaterialCreationAllowed(IAuthSession session, + Map<String, List<NewMaterial>> materials); + + @RolesAllowed( + { RoleWithHierarchy.SPACE_ADMIN, RoleWithHierarchy.SPACE_ETL_SERVER }) + @Capability("CREATE_PROJECTS_VIA_DSS") + public void assertProjectCreationAllowed(Session session, + @AuthorizationGuard(guardClass = NewProjectPredicate.class) + List<NewProject> newProjects); + + @RolesAllowed( + { RoleWithHierarchy.SPACE_ADMIN, RoleWithHierarchy.SPACE_ETL_SERVER }) + @Capability("CREATE_PROJECTS_VIA_DSS") + public void assertExperimentCreationAllowed(Session session, + @AuthorizationGuard(guardClass = NewExperimentPredicate.class) + List<NewExperiment> newExperiments); +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/authorization/predicate/NewProjectPredicate.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/authorization/predicate/NewProjectPredicate.java new file mode 100644 index 0000000000000000000000000000000000000000..e3783534521c583e20c47d70c10ef3e526365748 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/authorization/predicate/NewProjectPredicate.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.shared.authorization.predicate; + +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewProject; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifierFactory; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier; + +/** + * Predicate for {@link NewProject}. + * + * @author Franz-Josef Elmer + */ +public class NewProjectPredicate extends DelegatedPredicate<SpaceIdentifier, NewProject> +{ + public NewProjectPredicate() + { + super(new ExistingSpaceIdentifierPredicate()); + } + + @Override + public SpaceIdentifier tryConvert(NewProject project) + { + ProjectIdentifier identifier = new ProjectIdentifierFactory(project.getIdentifier()).createIdentifier(); + return identifier; + } + + @Override + public String getCandidateDescription() + { + return "new project"; + } + +} diff --git a/openbis/source/java/genericApplicationContext.xml b/openbis/source/java/genericApplicationContext.xml index 7777c3cb1f06ebb14915b4a15487f4ad9a2e1ce4..3baf2dec7f089142f32ceaaf1e90bc0f179d8346 100644 --- a/openbis/source/java/genericApplicationContext.xml +++ b/openbis/source/java/genericApplicationContext.xml @@ -150,7 +150,10 @@ <constructor-arg ref="common-business-object-factory" /> <constructor-arg ref="dss-factory" /> <constructor-arg ref="trusted-origin-domain-provider" /> + <constructor-arg ref="entity-operation-checker" /> </bean> + + <bean id="entity-operation-checker" class="ch.systemsx.cisd.openbis.generic.server.EntityOperationChecker"/> <!-- // Transaction diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java index 4f3ba1cf2520197fc4fdbe407cd01a101e1d8752..64aad99535ff7403b67ec7ee1dc07a3960bb9944 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java @@ -134,6 +134,8 @@ public class ETLServiceTest extends AbstractServerTestCase private IDataStoreService dataStoreService; + private IEntityOperationChecker entityOperationChecker; + @Override @BeforeMethod public final void setUp() @@ -142,6 +144,7 @@ public class ETLServiceTest extends AbstractServerTestCase boFactory = context.mock(ICommonBusinessObjectFactory.class); dssfactory = context.mock(IDataStoreServiceFactory.class); dataStoreService = context.mock(IDataStoreService.class); + entityOperationChecker = context.mock(IEntityOperationChecker.class); MaterialConfigurationProvider.initializeForTesting(false); } @@ -1025,7 +1028,7 @@ public class ETLServiceTest extends AbstractServerTestCase { "c1", "c2" }); prepareEntityOperationsExpectations(samplePE, sampleUpdate, material, materialType, - newMaterial, newSamplePE, newSampleIdentifier, newSample, externalData, + materialRegistrations, newSamplePE, newSampleIdentifier, newSample, externalData, updatedDataSetCode, dataSetUpdate); AtomicEntityOperationDetails details = @@ -1054,7 +1057,7 @@ public class ETLServiceTest extends AbstractServerTestCase private void prepareEntityOperationsExpectations(final SamplePE samplePE, final SampleUpdatesDTO sampleUpdate, final MaterialPE material, - final MaterialTypePE materialType, final NewMaterial newMaterial, + final MaterialTypePE materialType, final Map<String, List<NewMaterial>> newMaterials, final SamplePE newSamplePE, final SampleIdentifier newSampleIdentifier, final NewSample newSample, final NewExternalData externalData, final String updatedDataSetCode, final DataSetUpdatesDTO dataSetUpdate) @@ -1068,13 +1071,16 @@ public class ETLServiceTest extends AbstractServerTestCase allowing(entityTypeDAO).tryToFindEntityTypeByCode(materialType.getCode()); will(returnValue(materialType)); - final List<NewMaterial> newMaterials = Arrays.asList(newMaterial); - one(propertiesBatchManager).manageProperties(materialType, newMaterials, null); + one(entityOperationChecker) + .assertMaterialCreationAllowed(SESSION, newMaterials); + List<NewMaterial> newMaterialsList = newMaterials.values().iterator().next(); + one(propertiesBatchManager).manageProperties(materialType, newMaterialsList, + null); one(boFactory).createMaterialTable(SESSION); will(returnValue(materialTable)); - one(materialTable).add(newMaterials, materialType); + one(materialTable).add(newMaterialsList, materialType); one(materialTable).save(); one(materialTable).getMaterials(); will(returnValue(Arrays.asList(material))); @@ -1193,7 +1199,7 @@ public class ETLServiceTest extends AbstractServerTestCase { "c1", "c2" }); prepareEntityOperationsExpectations(samplePE, sampleUpdate, material, materialType, - newMaterial, newSamplePE, newSampleIdentifier, newSample, externalData, + materialRegistrations, newSamplePE, newSampleIdentifier, newSample, externalData, updatedDataSetCode, dataSetUpdate); context.checking(new Expectations() @@ -1367,7 +1373,7 @@ public class ETLServiceTest extends AbstractServerTestCase private IETLLIMSService createService() { return new ETLService(authenticationService, sessionManager, daoFactory, - propertiesBatchManager, boFactory, dssfactory, null); + propertiesBatchManager, boFactory, dssfactory, null, entityOperationChecker); } private DataStoreServerInfo createDSSInfo() diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityOperationTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityOperationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a07d5280e9613dc69157767139d5cbc1d025d1e1 --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityOperationTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2012 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.systemtest; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewProject; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSpace; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode; +import ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails; +import ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationResult; +import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUpdatesDTO; +import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier; + +/** + * System tests for + * {@link IETLLIMSService#performEntityOperations(String, AtomicEntityOperationDetails)} + * + * @author Franz-Josef Elmer + */ +@Test(groups = "system test") +public class EntityOperationTest extends SystemTestCase +{ + private static final String PREFIX = "EO_"; + + private static final String SPACE_ETL_SERVER_FOR_A = PREFIX + "S_ETL_A"; + + private static final SpaceIdentifier SPACE_A = new SpaceIdentifier("CISD", "CISD"); + + private static final SpaceIdentifier SPACE_B = new SpaceIdentifier("CISD", "TESTGROUP"); + + private static final class EntityOperationBuilder + { + private final List<NewSpace> spaces = new ArrayList<NewSpace>(); + + private final List<NewProject> projects = new ArrayList<NewProject>(); + + private final List<NewExperiment> experiments = new ArrayList<NewExperiment>(); + + private final List<NewSample> samples = new ArrayList<NewSample>(); + + private final List<SampleUpdatesDTO> sampleUpdates = new ArrayList<SampleUpdatesDTO>(); + + private final List<? extends NewExternalData> dataSets = new ArrayList<NewExternalData>(); + + private final List<DataSetUpdatesDTO> dataSetUpdates = new ArrayList<DataSetUpdatesDTO>(); + + private final Map<String, List<NewMaterial>> materials = + new HashMap<String, List<NewMaterial>>(); + + private TechId registrationID; + + private String userID; + + EntityOperationBuilder(long registrationID) + { + this.registrationID = new TechId(registrationID); + } + + EntityOperationBuilder user(String userID) + { + this.userID = userID; + return this; + } + + EntityOperationBuilder space(String code) + { + return space(new NewSpace(code, null, null)); + } + + EntityOperationBuilder space(NewSpace space) + { + spaces.add(space); + return this; + } + + EntityOperationBuilder project(SpaceIdentifier spaceIdentifier, String projectCode) + { + String projectIdentifier = + new ProjectIdentifier(spaceIdentifier, projectCode).toString(); + return project(new NewProject(projectIdentifier, null)); + } + + EntityOperationBuilder project(NewProject project) + { + projects.add(project); + return this; + } + + AtomicEntityOperationDetails create() + { + return new AtomicEntityOperationDetails(registrationID, userID, spaces, projects, + experiments, sampleUpdates, samples, materials, dataSets, dataSetUpdates); + } + + } + + @BeforeClass + public void createTestUsers() + { + assignSpaceRole(registerPerson(SPACE_ETL_SERVER_FOR_A), RoleCode.ETL_SERVER, SPACE_A); + } + + @Test + public void testCreateProjectAsSpaceETLServerSuccessfully() + { + String sessionToken = authenticateAs(SPACE_ETL_SERVER_FOR_A); + AtomicEntityOperationDetails eo = + new EntityOperationBuilder(1).project(SPACE_A, "P1").create(); + + AtomicEntityOperationResult result = etlService.performEntityOperations(sessionToken, eo); + + assertEquals("[/" + SPACE_A.getSpaceCode() + "/P1]", result.getProjectsCreated().toString()); + } + + @Test + public void testCreateProjectAsSpaceETLServerThrowsAuthorizationFailure() + { + String sessionToken = authenticateAs(SPACE_ETL_SERVER_FOR_A); + AtomicEntityOperationDetails eo = + new EntityOperationBuilder(1).project(SPACE_B, "P1").create(); + + try + { + etlService.performEntityOperations(sessionToken, eo); + fail("AuthorizationFailureException expected"); + } catch (AuthorizationFailureException ex) + { + assertEquals("Authorization failure: ERROR: \"User '" + SPACE_ETL_SERVER_FOR_A + + "' does not have enough privileges.\".", ex.getMessage()); + } + } +} diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/RelationshipServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/RelationshipServiceTest.java index e49dc88547bda7650cd1f3e4c0944316766d1128..67aff37e592d201abfc02813846505402f62da92 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/RelationshipServiceTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/RelationshipServiceTest.java @@ -27,7 +27,6 @@ import org.testng.annotations.Test; import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException; import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Grantee; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSamplesWithTypes; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode; @@ -42,33 +41,43 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier; @Test(groups = "system test") public class RelationshipServiceTest extends SystemTestCase { + private static final String PREFIX = "RS_"; + + private static final String BASIC_USER = PREFIX + "basic_user"; + + private static final String POWER_USER = PREFIX + "power_user"; + + private static final String SOURCE_SPACE_ADMIN = PREFIX + "source_space_admin"; + + private static final String DESTINATION_SPACE_ADMIN = PREFIX + "destination_space_admin"; + + private static final String BOTH_SPACE_ADMIN = PREFIX + "both_space_admin"; + + private static final String INSTANCE_ADMIN = PREFIX + "instance_admin"; private SpaceIdentifier sourceSpace = new SpaceIdentifier("CISD", "CISD"); private SpaceIdentifier destinationSpace = new SpaceIdentifier("CISD", "TESTGROUP"); - private String systemSessionToken; - private ExperimentUpdatesDTO projectUpdate; @BeforeClass public void loginSystemUser() { - systemSessionToken = commonServer.tryToAuthenticateAsSystem().getSessionToken(); projectUpdate = getProjectUpdate(); - createSpaceUser("basic_user", RoleCode.USER, RoleCode.USER); - createSpaceUser("power_user", RoleCode.POWER_USER, RoleCode.POWER_USER); - createSpaceUser("source_space_admin", RoleCode.ADMIN, RoleCode.USER); - createSpaceUser("destination_space_admin", RoleCode.USER, RoleCode.ADMIN); - createSpaceUser("both_space_admin", RoleCode.ADMIN, RoleCode.ADMIN); - createInstanceUser("instance_admin", RoleCode.ADMIN); + createSpaceUser(BASIC_USER, RoleCode.USER, RoleCode.USER); + createSpaceUser(POWER_USER, RoleCode.POWER_USER, RoleCode.POWER_USER); + createSpaceUser(SOURCE_SPACE_ADMIN, RoleCode.ADMIN, RoleCode.USER); + createSpaceUser(DESTINATION_SPACE_ADMIN, RoleCode.USER, RoleCode.ADMIN); + createSpaceUser(BOTH_SPACE_ADMIN, RoleCode.ADMIN, RoleCode.ADMIN); + createInstanceUser(INSTANCE_ADMIN, RoleCode.ADMIN); } @Test(expectedExceptions = { AuthorizationFailureException.class }) public void basicUserIsNotAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("basic_user"); + String session = authenticateAs(BASIC_USER); commonServer.updateExperiment(session, projectUpdate); } @@ -76,7 +85,7 @@ public class RelationshipServiceTest extends SystemTestCase { AuthorizationFailureException.class }) public void powerUserIsNotAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("power_user"); + String session = authenticateAs(POWER_USER); commonServer.updateExperiment(session, projectUpdate); } @@ -84,7 +93,7 @@ public class RelationshipServiceTest extends SystemTestCase { AuthorizationFailureException.class }) public void spaceAdminOfOnlySourceSpaceIsNotAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("source_space_admin"); + String session = authenticateAs(SOURCE_SPACE_ADMIN); commonServer.updateExperiment(session, projectUpdate); } @@ -92,64 +101,54 @@ public class RelationshipServiceTest extends SystemTestCase { AuthorizationFailureException.class }) public void spaceAdminOfOnlyDestinationSpaceIsNotAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("destination_space_admin"); + String session = authenticateAs(DESTINATION_SPACE_ADMIN); commonServer.updateExperiment(session, projectUpdate); } @Test public void spaceAdminOfBothSpacesIsAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("both_space_admin"); + String session = authenticateAs(BOTH_SPACE_ADMIN); commonServer.updateExperiment(session, projectUpdate); Experiment experiment = - commonServer.getExperimentInfo(session, - new ExperimentIdentifier( - "CISD", "TESTGROUP", "TESTPROJ", "EXP1")); + commonServer.getExperimentInfo(session, new ExperimentIdentifier("CISD", + "TESTGROUP", "TESTPROJ", "EXP1")); assertThat(experiment.getProject().getCode(), is("TESTPROJ")); } @Test public void instanceAdminIsAllowedToUpdateExperienceProjectRelationship() { - String session = authenticate("instance_admin"); + String session = authenticateAs(INSTANCE_ADMIN); commonServer.updateExperiment(session, projectUpdate); Experiment experiment = - commonServer.getExperimentInfo(session, - new ExperimentIdentifier( - "CISD", "TESTGROUP", "TESTPROJ", "EXP1")); + commonServer.getExperimentInfo(session, new ExperimentIdentifier("CISD", + "TESTGROUP", "TESTPROJ", "EXP1")); assertThat(experiment.getProject().getCode(), is("TESTPROJ")); } - private String authenticate(String user) - { - return commonServer.tryToAuthenticate(user, "password").getSessionToken(); - } - private void createSpaceUser(String userName, RoleCode sourceSpaceRole, RoleCode destinationSpaceRole) { - String sessionToken = commonServer.tryToAuthenticateAsSystem().getSessionToken(); - commonServer.registerPerson(sessionToken, userName); - commonServer.registerSpaceRole(sessionToken, sourceSpaceRole, - sourceSpace, Grantee.createPerson(userName)); - commonServer.registerSpaceRole(sessionToken, destinationSpaceRole, - destinationSpace, Grantee.createPerson(userName)); + registerPerson(userName); + assignSpaceRole(userName, sourceSpaceRole, sourceSpace); + assignSpaceRole(userName, destinationSpaceRole, destinationSpace); } private void createInstanceUser(String userName, RoleCode role) { - commonServer.registerPerson(systemSessionToken, userName); - commonServer.registerInstanceRole(systemSessionToken, role, Grantee.createPerson(userName)); + registerPerson(userName); + assignInstanceRole(userName, role); } private ExperimentUpdatesDTO getProjectUpdate() { ExperimentUpdatesDTO updates = new ExperimentUpdatesDTO(); Experiment experiment = - commonServer.getExperimentInfo(systemSessionToken, new ExperimentIdentifier( - "CISD", "CISD", "NEMO", "EXP1")); + commonServer.getExperimentInfo(systemSessionToken, new ExperimentIdentifier("CISD", + "CISD", "NEMO", "EXP1")); updates.setExperimentId(new TechId(experiment)); updates.setVersion(experiment.getModificationDate()); diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/SystemTestCase.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/SystemTestCase.java index e97f1a2d360eef2eca8cf8218daedc62efb731d9..f026ace361aa41008ef69b4d5713739a9559c050 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/SystemTestCase.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/SystemTestCase.java @@ -33,6 +33,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; import org.springframework.test.context.transaction.TransactionConfiguration; import org.testng.AssertJUnit; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeSuite; import ch.systemsx.cisd.common.servlet.SpringRequestContextProvider; @@ -43,15 +44,19 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TypedTableResultSe import ch.systemsx.cisd.openbis.generic.client.web.server.UploadedFilesBean; import ch.systemsx.cisd.openbis.generic.server.ICommonServerForInternalUse; import ch.systemsx.cisd.openbis.generic.server.util.TestInitializer; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeWithRegistration; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DisplaySettings; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Grantee; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityPropertiesHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.PropertyBuilder; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier; import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.IGenericClientService; import ch.systemsx.cisd.openbis.plugin.generic.shared.IGenericServer; @@ -76,14 +81,24 @@ public abstract class SystemTestCase extends AbstractTransactionalTestNGSpringCo protected IGenericClientService genericClientService; + protected IETLLIMSService etlService; + protected MockHttpServletRequest request; + protected String systemSessionToken; + @BeforeSuite public void beforeSuite() { TestInitializer.init(); } + @BeforeClass + public void loginAsSystem() + { + systemSessionToken = commonServer.tryToAuthenticateAsSystem().getSessionToken(); + } + /** * Sets a {@link MockHttpServletRequest} for the specified context provider */ @@ -142,6 +157,13 @@ public abstract class SystemTestCase extends AbstractTransactionalTestNGSpringCo this.genericClientService = genericClientService; } + @Autowired + public void setETLService(IETLLIMSService etlService) + { + this.etlService = etlService; + + } + protected SessionContext logIntoCommonClientService() { SessionContext context = commonClientService.tryToLogin("test", "a"); @@ -209,6 +231,39 @@ public abstract class SystemTestCase extends AbstractTransactionalTestNGSpringCo } } + /** + * Register a person with specified user ID. + * + * @return userID + */ + protected String registerPerson(String userID) + { + commonServer.registerPerson(systemSessionToken, userID); + return userID; + } + + protected void assignInstanceRole(String userID, RoleCode roleCode) + { + commonServer.registerInstanceRole(systemSessionToken, roleCode, + Grantee.createPerson(userID)); + } + + protected void assignSpaceRole(String userID, RoleCode roleCode, SpaceIdentifier spaceIdentifier) + { + commonServer.registerSpaceRole(systemSessionToken, roleCode, spaceIdentifier, + Grantee.createPerson(userID)); + } + + /** + * Authenticates as specified user. + * + * @return session token + */ + protected String authenticateAs(String user) + { + return commonServer.tryToAuthenticate(user, "password").getSessionToken(); + } + protected NewSampleBuilder sample(String identifier) { return new NewSampleBuilder(identifier);