/* * Copyright (c) 2013, 2015 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Alex Lagarde - initial API and implementation */ package org.eclipse.emf.cdo.tests.bugzilla; import org.eclipse.emf.cdo.CDODeltaNotification; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.security.CDOPermission; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.security.Access; import org.eclipse.emf.cdo.security.PatternStyle; import org.eclipse.emf.cdo.security.Realm; import org.eclipse.emf.cdo.security.Role; import org.eclipse.emf.cdo.security.SecurityFactory; import org.eclipse.emf.cdo.security.User; import org.eclipse.emf.cdo.server.security.ISecurityManager; import org.eclipse.emf.cdo.server.security.SecurityManagerUtil; import org.eclipse.emf.cdo.session.CDOSession; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig; import org.eclipse.emf.cdo.tests.model1.Category; import org.eclipse.emf.cdo.tests.model1.Company; import org.eclipse.emf.cdo.transaction.CDOTransaction; import org.eclipse.emf.cdo.util.CDOUtil; import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent; import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.security.IPasswordCredentials; import org.eclipse.net4j.util.security.PasswordCredentials; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.impl.TransactionChangeRecorder; import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Bug 417483 - [Security] Issues in invalidation when missing write Permission. * <p> * Tests ensuring that the permissions are correctly computed, no matter what passive update mode is chosen. * * @author Alex Lagarde */ @CleanRepositoriesBefore(reason = "TEST_SECURITY_MANAGER") @CleanRepositoriesAfter(reason = "TEST_SECURITY_MANAGER") public class Bugzilla_417483_Test extends AbstractCDOTest { private static final SecurityFactory SF = SecurityFactory.eINSTANCE; private static final IPasswordCredentials CREDENTIALS = new PasswordCredentials("Stepper", "12345"); private static final IPasswordCredentials CREDENTIALS_READ_ONLY = new PasswordCredentials("Lagarde", "54321"); @Override protected void doSetUp() throws Exception { super.doSetUp(); ISecurityManager securityManager = SecurityManagerUtil.createSecurityManager("/security", getServerContainer()); // Start repository getTestProperties().put(RepositoryConfig.PROP_TEST_SECURITY_MANAGER, securityManager); getRepository(); securityManager.modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(CREDENTIALS); user.getRoles().add(realm.getRole("All Objects Writer")); User userReadOnly = realm.addUser(CREDENTIALS_READ_ONLY); userReadOnly.getRoles().add(realm.getRole("All Objects Reader")); } }); } public void testPassiveUpdates_Invalidations() throws Exception { doPassiveUpdates(PassiveUpdateMode.INVALIDATIONS, false); } public void testPassiveUpdates_InvalidationsWithEditingDomain() throws Exception { doPassiveUpdates(PassiveUpdateMode.INVALIDATIONS, true); } public void testPassiveUpdates_Changes() throws Exception { doPassiveUpdates(PassiveUpdateMode.CHANGES, false); } public void testPassiveUpdates_Changes_WithEditingDomain() throws Exception { doPassiveUpdates(PassiveUpdateMode.CHANGES, true); } public void testPassiveUpdates_Additions() throws Exception { doPassiveUpdates(PassiveUpdateMode.ADDITIONS, false); } public void testPassiveUpdates_Additions_WithEditingDomain() throws Exception { doPassiveUpdates(PassiveUpdateMode.ADDITIONS, true); } protected void doPassiveUpdates(PassiveUpdateMode passiveUpdateMode, boolean withEditingDomain) throws Exception { // Both users connect to the repository CDOSession session = openSession(CREDENTIALS); CDOTransaction transaction = session.openTransaction(); CDOSession sessionReadOnly = openSession(CREDENTIALS_READ_ONLY); sessionReadOnly.options().setPassiveUpdateEnabled(true); sessionReadOnly.options().setPassiveUpdateMode(passiveUpdateMode); ResourceSet resourceSetReadOnly; if (withEditingDomain) { resourceSetReadOnly = createTransactionalEditingDomain().getResourceSet(); } else { resourceSetReadOnly = new ResourceSetImpl(); } CDOTransaction transactionReadOnly = sessionReadOnly.openTransaction(resourceSetReadOnly); assertEquals(CREDENTIALS.getUserID(), session.getUserID()); assertEquals(CREDENTIALS_READ_ONLY.getUserID(), sessionReadOnly.getUserID()); // User with write permission creates a resource and commits CDOResource resource = transaction.createResource(getResourcePath("/res")); assertEquals(true, isWritable(resource)); Category category = getModel1Factory().createCategory(); resource.getContents().add(category); commitAndSync(transaction, transactionReadOnly); // User without write permission should be able to integrate changes without permission issues CDOResource resourceReadOnly = transactionReadOnly.getResource(getResourcePath("/res")); assertEquals(1, resourceReadOnly.getContents().size()); assertEquals("User should not have write permission on resource", false, isWritable(resourceReadOnly)); // => Trigger loading of resource root so that invalidation are sent Category categoryReadOnly = (Category)resourceReadOnly.getContents().get(0); assertEquals("User should not have write permission on element", false, isWritable(categoryReadOnly)); // User with write permission modifies the resource root and commits category.getProducts().add(getModel1Factory().createProduct1()); category.setName("RENAMED"); final CountDownLatch latch = new CountDownLatch(1); transactionReadOnly.addListener(new IListener() { public void notifyEvent(IEvent event) { if (event instanceof CDOViewInvalidationEvent) { latch.countDown(); } } }); transaction.commit(); // User without write permission should be able to integrate changes without permission issues boolean notified = latch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); assertEquals("Timeout: User should have been notified of changes", true, notified); assertEquals("User should not have write permission on resource", false, isWritable(resourceReadOnly)); assertEquals("User should not have write permission on element", false, isWritable(categoryReadOnly)); } /** * Creates a {@link TransactionalEditingDomain} which has a {@link TransactionChangeRecorder} that does not throw an {@link IllegalStateException} if model is modified through a {@link CDODeltaNotification}. */ private TransactionalEditingDomain createTransactionalEditingDomain() { return new TransactionalEditingDomainImpl(null) { @Override protected TransactionChangeRecorder createChangeRecorder(ResourceSet rset) { return new TransactionChangeRecorder(this, rset) { @Override public void notifyChanged(Notification notification) { if (!(notification instanceof CDODeltaNotification)) { super.notifyChanged(notification); } } }; } }; } public void testLoad_NoPermission() throws Exception { final IPasswordCredentials credentials = new PasswordCredentials("user", "password"); final String protectedResource = getResourcePath("/protected"); getSecurityManager().modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(credentials); Role role = realm.addRole(protectedResource + " Reader"); role.getPermissions().add(SF.createFilterPermission(Access.READ, // SF.createResourceFilter(protectedResource, PatternStyle.EXACT, true).setModelObjects(false))); user.getRoles().add(role); } }); { // Init model with write full access CDOSession session = openSession(CREDENTIALS); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(protectedResource); resource.getContents().add(getModel1Factory().createCompany()); transaction.commit(); session.close(); } CDOSession session = openSession(credentials); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.getResource(protectedResource); assertEquals(CDOPermission.READ, resource.cdoPermission()); Company company = (Company)resource.getContents().get(0); assertEquals(CDOPermission.NONE, CDOUtil.getCDOObject(company).cdoPermission()); } public void testLoad_ReadPermission() throws Exception { final IPasswordCredentials credentials = new PasswordCredentials("user", "password"); final String protectedResource = getResourcePath("/protected"); getSecurityManager().modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(credentials); Role role = realm.addRole(protectedResource + " Reader"); role.getPermissions().add(SF.createFilterPermission(Access.READ, // SF.createResourceFilter(protectedResource, PatternStyle.TREE, true))); user.getRoles().add(role); } }); { // Init model with write full access CDOSession session = openSession(CREDENTIALS); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(protectedResource); resource.getContents().add(getModel1Factory().createCompany()); transaction.commit(); session.close(); } CDOSession session = openSession(credentials); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.getResource(protectedResource); assertEquals(CDOPermission.READ, resource.cdoPermission()); Company company = (Company)resource.getContents().get(0); assertEquals(CDOPermission.READ, CDOUtil.getCDOObject(company).cdoPermission()); } public void testCommit_NoPermission() throws Exception { final IPasswordCredentials credentials = new PasswordCredentials("user", "password"); final String protectedResource = getResourcePath("/protected"); getSecurityManager().modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(credentials); Role role = realm.addRole(protectedResource + " Writer"); role.getPermissions().add(SF.createFilterPermission(Access.WRITE, // SF.createResourceFilter(protectedResource, PatternStyle.EXACT, true).setModelObjects(false))); user.getRoles().add(role); } }); CDOSession session = openSession(credentials); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(protectedResource); assertEquals(CDOPermission.WRITE, resource.cdoPermission()); Company company = getModel1Factory().createCompany(); resource.getContents().add(company); assertEquals(CDOPermission.WRITE, CDOUtil.getCDOObject(company).cdoPermission()); transaction.commit(); assertEquals(CDOPermission.NONE, CDOUtil.getCDOObject(company).cdoPermission()); } public void testCommit_ReadPermission() throws Exception { final IPasswordCredentials credentials = new PasswordCredentials("user", "password"); final String protectedResource = getResourcePath("/protected"); getSecurityManager().modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(credentials); Role role = realm.addRole(protectedResource + " Writer"); role.getPermissions().add(SF.createFilterPermission(Access.WRITE, // SF.createResourceFilter(protectedResource, PatternStyle.EXACT, true).setModelObjects(false))); role.getPermissions().add(SF.createFilterPermission(Access.READ, // SF.createResourceFilter(protectedResource, PatternStyle.EXACT, false).setModelObjects(true))); user.getRoles().add(role); } }); CDOSession session = openSession(credentials); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(protectedResource); assertEquals(CDOPermission.WRITE, resource.cdoPermission()); Company company = getModel1Factory().createCompany(); resource.getContents().add(company); assertEquals(CDOPermission.WRITE, CDOUtil.getCDOObject(company).cdoPermission()); transaction.commit(); assertEquals(CDOPermission.READ, CDOUtil.getCDOObject(company).cdoPermission()); } public void testCommit_WritePermission() throws Exception { final IPasswordCredentials credentials = new PasswordCredentials("user", "password"); final String protectedResource = getResourcePath("/protected"); getSecurityManager().modify(new ISecurityManager.RealmOperation() { public void execute(Realm realm) { User user = realm.addUser(credentials); Role role = realm.addRole(protectedResource + " Writer"); role.getPermissions().add(SF.createFilterPermission(Access.WRITE, // SF.createResourceFilter(protectedResource, PatternStyle.EXACT, true).setModelObjects(true))); user.getRoles().add(role); } }); CDOSession session = openSession(credentials); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(protectedResource); assertEquals(CDOPermission.WRITE, resource.cdoPermission()); Company company = getModel1Factory().createCompany(); resource.getContents().add(company); assertEquals(CDOPermission.WRITE, CDOUtil.getCDOObject(company).cdoPermission()); transaction.commit(); assertEquals(CDOPermission.WRITE, CDOUtil.getCDOObject(company).cdoPermission()); } private ISecurityManager getSecurityManager() { InternalRepository repository = getRepository(); return SecurityManagerUtil.getSecurityManager(repository); } private boolean isWritable(EObject object) { CDOObject cdoObject = CDOUtil.getCDOObject(object); CDORevision revision = cdoObject.cdoRevision(true); return revision.isWritable(); } }