/* * Copyright (C) 2006-2011 University of Dundee & Open Microscopy Environment. * All rights reserved. * * Use is subject to license terms supplied in LICENSE.txt */ package ome.server.utests.sec; import static ome.model.internal.Permissions.Right.READ; import static ome.model.internal.Permissions.Role.GROUP; import static ome.model.internal.Permissions.Role.WORLD; import java.util.List; import org.hibernate.Filter; import org.hibernate.Session; import org.jmock.Mock; import org.jmock.core.Invocation; import org.jmock.core.Stub; import org.jmock.core.stub.DefaultResultStub; import org.springframework.orm.hibernate3.HibernateCallback; import org.testng.annotations.Test; import ome.conditions.ApiUsageException; import ome.conditions.SecurityViolation; import ome.model.IEnum; import ome.model.IObject; import ome.model.core.Image; import ome.model.internal.Details; import ome.model.internal.Permissions; import ome.model.meta.Event; import ome.model.meta.EventLog; import ome.model.meta.Experimenter; import ome.model.meta.ExperimenterGroup; import ome.security.AdminAction; import ome.security.SecureAction; import ome.security.basic.OneGroupSecurityFilter; @Test public class SecuritySystemTest extends AbstractBasicSecuritySystemTest { /* * Test method for 'ome.security.SecuritySystem.isReady()' */ public void testIsReady() { prepareMocksWithUserDetails(false); assertFalse(sec.isReady()); sec.loadEventContext(false); assertTrue(sec.isReady()); sec.invalidateEventContext(); assertFalse(sec.isReady()); // don't need ready sec.sys. sec.isReady(); sec.isSystemType(null); aclVoter.allowLoad(null, user.getClass(), Details.create(), 1L); sec.getSecurityRoles(); sf.mockQuery.expects(atLeastOnce()).method("contains").will( returnValue(true)); sec.doAction(new SecureAction() { public <T extends IObject> T updateObject(T... obj) { return null; }; }, user); sec.copyToken(user, user); sec.invalidateEventContext(); sec.isReady(); sec.disable("foo"); sec.enable(); sec.isDisabled(""); // need ready sec.sys try { sec.enableReadFilter(null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; // See documentation in method. No longer throws // try { // sec.disableReadFilter(null); // fail("Should throw ApiUsage"); // } catch (ApiUsageException api) { // } // ; try { sec.newTransientDetails(null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; try { sec.checkManagedDetails(null, null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; try { // Requires ready prepareMocksWithUserDetails(false); assertFalse(sec.isReady()); sec.loadEventContext(false); sec.addLog(null, Image.class, 1L); fail("Should throw IllegalArgumentException"); } catch (IllegalArgumentException iae) { } ; // aclVoter doesn't participate in isReady() } /* * Test method for 'ome.security.SecuritySystem.isSystemType(Class<? * extends IObject>)' * * ticket:1784 - "system" group contents are system types */ public void testIsSystemType() { assertTrue(sec.isSystemType(Experimenter.class)); assertTrue(sec.isSystemType(ExperimenterGroup.class)); assertTrue(sec.isSystemType(Event.class)); assertTrue(sec.isSystemType(EventLog.class)); assertTrue(sec.isSystemType(IEnum.class)); assertFalse(sec.isSystemType(Image.class)); // TODO what else } /* * Test method for 'ome.security.SecuritySystem.enableReadFilter(Object)' * Test method for 'ome.security.SecuritySystem.disableReadFilter(Object)' */ @Test(enabled = false) // Basically is just testing the implementation. public void testEnableAndDisableReadFilter() { prepareMocksWithUserDetails(false); Mock mockFilter = mock(Filter.class); Filter f = (Filter) mockFilter.proxy(); mockFilter.expects(once()).method("setParameter").with( eq(OneGroupSecurityFilter.is_adminorpi), eq(Boolean.FALSE)).will( returnValue(f)); mockFilter.expects(once()).method("setParameter").with( eq(OneGroupSecurityFilter.current_user), eq(user.getId())).will( returnValue(f)); Mock mockSession = mock(Session.class); mockSession.expects(once()).method("enableFilter").with( eq("securityFilter")).will(returnValue(f)); mockSession.expects(once()).method("disableFilter").with( eq("securityFilter")); Session s = (Session) mockSession.proxy(); sec.loadEventContext(false); sec.enableReadFilter(s); sec.disableReadFilter(s); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.addLog(String, Class, Long)' */ public void testAddLog() { prepareMocksWithUserDetails(true); sec.loadEventContext(true); assertTrue(sec.getLogs().size() == 0); sec.addLog("SHOULDN'T BE ADDED", Event.class, 1L); assertTrue(sec.getLogs().size() == 0); sec.addLog("SHOULD BE ADDED", Image.class, 2L); // ticket:328 assertTrue(sec.getLogs().size() == 1); EventLog onlyLog = sec.getLogs().get(0); assertEquals(onlyLog.getAction(), "SHOULD BE ADDED"); assertEquals(onlyLog.getEntityType(), Image.class.getName()); assertEquals(onlyLog.getEntityId(), new Long(2L)); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.getCreationEvent()' */ public void testGetCurrentEvent() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); assertSame(cd.getEvent(), event); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.clearCurrentDetails()' */ public void testClearCurrentDetails() { prepareMocksWithUserDetails(false); assertFalse(sec.isReady()); sec.loadEventContext(false); assertTrue(sec.isReady()); sec.invalidateEventContext(); assertFalse(sec.isReady()); } /* * Test method for 'ome.security.SecuritySystem.setCurrentDetails()' */ public void testSetCurrentDetails() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); assertEquals(user.getId(), cd.getOwner().getId()); assertEquals(event.getId(), cd.getEvent().getId()); assertEquals(group.getId(), cd.getGroup().getId()); assertTrue(sec.isReady()); sec.invalidateEventContext(); } @Test public void testNullChecksOnAllMethods() throws Exception { prepareMocksWithRootDetails(false); sec.loadEventContext(false); // can handle nulls sec.isSystemType(null); sec.copyToken(null, null); sec.enable((java.lang.String[])null); // uses Springs assert try { aclVoter.allowLoad(null, null, null, 1L); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.allowCreation(null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.allowUpdate(null, null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.allowDelete(null, null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { sec.doAction(null, (IObject[])null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { sec.addLog(null, null, null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.throwLoadViolation(null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.throwCreationViolation(null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.throwUpdateViolation(null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; try { aclVoter.throwDeleteViolation(null); fail("Should throw IllegalArg"); } catch (IllegalArgumentException iae) { } ; // api usage try { sec.enableReadFilter(null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; // See documentation in method. No longer throws. // try { // sec.disableReadFilter(null); // fail("Should throw ApiUsage"); // } catch (ApiUsageException api) { // } ; try { sec.newTransientDetails(null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; try { sec.checkManagedDetails(null, null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; try { sec.isDisabled(null); fail("Should throw ApiUsage"); } catch (ApiUsageException api) { } ; try { sec.disable((java.lang.String[])null); fail("Should throw ApiUSage"); } catch (ApiUsageException api) { } ; } @Test public void testIsSystemGroup() throws Exception { prepareMocksWithRootDetails(true); sec.loadEventContext(true); assertTrue(sec.getSecurityRoles().isSystemGroup(group)); sec.invalidateEventContext(); } @Test public void testLeaderOfGroups() throws Exception { prepareMocksWithUserDetails(false); sec.loadEventContext(false); assertEquals(cd.getCurrentEventContext().getLeaderOfGroupsList(), leaderOfGroups); sec.invalidateEventContext(); } @Test public void testDisblingSubSystems() throws Exception { prepareMocksWithUserDetails(false); sec.loadEventContext(false); assertFalse(sec.isDisabled("foo")); sec.disable("foo"); assertTrue(sec.isDisabled("foo")); sec.enable("foo"); assertFalse(sec.isDisabled("foo")); sec.disable("foo"); assertTrue(sec.isDisabled("foo")); sec.enable(); assertFalse(sec.isDisabled("foo")); sec.invalidateEventContext(); } // ~ CAN USE MORE WORK // ========================================================================= /* * Test method for 'ome.security.SecuritySystem.allowCreation(IObject)' */ public void testAllowCreation() { Experimenter e = new Experimenter(); Image i = new Image(); prepareMocksWithUserDetails(false); // 1. not system type sec.loadEventContext(false); assertFalse(aclVoter.allowCreation(e)); assertTrue(aclVoter.allowCreation(i)); sec.invalidateEventContext(); // 2. is privileged SecureAction checkAllowCreate = new SecureAction() { public <T extends IObject> T updateObject(T... objs) { assertTrue(aclVoter.allowCreation(objs[0])); return null; } }; sec.doAction(checkAllowCreate, e); sec.doAction(checkAllowCreate, i); // 3. user is admin. prepareMocksWithRootDetails(false); sec.loadEventContext(false); assertTrue(aclVoter.allowCreation(e)); assertTrue(aclVoter.allowCreation(i)); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.allowUpdate(IObject)' */ public void testAllowUpdate() { Experimenter e = new Experimenter(); Image i = new Image(); Details d = Details.create(); d.setPermissions(new Permissions()); prepareMocksWithUserDetails(false); // BASICS // 1. not system type sec.loadEventContext(false); assertFalse(aclVoter.allowUpdate(e, d)); assertTrue(aclVoter.allowUpdate(i, d)); sec.invalidateEventContext(); // 2. is privileged SecureAction checkAllowCreate = new SecureAction() { public <T extends IObject> T updateObject(T... objs) { assertTrue(aclVoter.allowUpdate(objs[0], objs[0].getDetails())); return null; } }; sec.doAction(checkAllowCreate, e); sec.doAction(checkAllowCreate, i); // 3. user is admin. prepareMocksWithRootDetails(false); sec.loadEventContext(false); assertTrue(aclVoter.allowUpdate(e, e.getDetails())); assertTrue(aclVoter.allowUpdate(i, i.getDetails())); sec.invalidateEventContext(); // PERMISSIONS BASED prepareMocksWithUserDetails(false); sec.loadEventContext(false); // different owner but all permissions i = new Image(2L, true); i.getDetails().setOwner(new Experimenter(2L, false)); i.getDetails().setGroup(new ExperimenterGroup(2L, false)); i.getDetails().setPermissions(new Permissions()); assertTrue(aclVoter.allowUpdate(i, i.getDetails())); // now lower permissions prepareMocksWithUserDetails(false, Permissions.READ_ONLY); sec.loadEventContext(false); assertFalse(aclVoter.allowUpdate(i, i.getDetails())); } /* * Test method for 'ome.security.SecuritySystem.allowDelete(IObject)' */ public void testAllowDelete() { Experimenter e = new Experimenter(); Image i = new Image(); Details d = Details.create(); d.setPermissions(new Permissions()); prepareMocksWithUserDetails(false); // 1. not system type sec.loadEventContext(false); assertFalse(aclVoter.allowDelete(e, d)); assertTrue(aclVoter.allowDelete(i, d)); sec.invalidateEventContext(); // 2. is privileged SecureAction checkAllowCreate = new SecureAction() { public <T extends IObject> T updateObject(T... objs) { assertTrue(aclVoter.allowDelete(objs[0], objs[0].getDetails())); return null; } }; sec.doAction(checkAllowCreate, e); sec.doAction(checkAllowCreate, i); // 3. user is admin. prepareMocksWithRootDetails(false); sec.loadEventContext(false); assertTrue(aclVoter.allowDelete(e, e.getDetails())); assertTrue(aclVoter.allowDelete(i, i.getDetails())); sec.invalidateEventContext(); // PERMISSIONS BASED prepareMocksWithUserDetails(false, Permissions.WORLD_WRITEABLE); sec.loadEventContext(false); // different owner but all permissions i = new Image(2L, true); i.getDetails().setOwner(new Experimenter(2L, false)); i.getDetails().setGroup(new ExperimenterGroup(2L, false)); i.getDetails().setPermissions(new Permissions()); assertTrue(aclVoter.allowDelete(i, i.getDetails())); // now lower permissions prepareMocksWithUserDetails(false, Permissions.READ_ONLY); sec.loadEventContext(false); assertFalse(aclVoter.allowDelete(i, i.getDetails())); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.allowLoad(IObject)' */ public void testAllowLoad() { prepareMocksWithUserDetails(false, Permissions.PUBLIC); sec.loadEventContext(false); Details d = Details.create(); d.setOwner(new Experimenter(2L, false)); d.setGroup(new ExperimenterGroup(2L, false)); // in same group d.setPermissions(new Permissions()); assertTrue(aclVoter.allowLoad(null, Image.class, d, 1L)); prepareMocksWithUserDetails(false, Permissions.PRIVATE); sec.loadEventContext(false); assertFalse(aclVoter.allowLoad(null, Image.class, d, 1L)); sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.transientDetails(IObject)' */ public void testTransientDetails() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); Permissions p = new Permissions(); Image i = new Image(); // setting permissions i.getDetails().setOwner(new Experimenter(1L, false)); i.getDetails().setPermissions(p); Details test = sec.newTransientDetails(i); assertEquals(p, test.getPermissions()); assertEquals(test.getOwner().getId(), user.getId()); assertEquals(test.getGroup().getId(), group.getId()); // can't change that value i.getDetails().setOwner(new Experimenter(3L, false)); i.getDetails().setPermissions(p); try { sec.newTransientDetails(i); fail("should throw sec. vio."); } catch (SecurityViolation sv) { } sec.invalidateEventContext(); } /* * Test method for 'ome.security.SecuritySystem.managedDetails(IObject, * Details)' */ public void testManagedDetails() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); Permissions p = new Permissions(); Image i = new Image(1L, true); Details oldDetails = Details.create(); oldDetails.setOwner(user); oldDetails.setGroup(group); oldDetails.setCreationEvent(event); oldDetails.setPermissions(new Permissions()); // setting permissions i.getDetails().setOwner(new Experimenter(1L, false)); i.getDetails().setGroup(new ExperimenterGroup(2L, false)); i.getDetails().setCreationEvent(new Event(1L, false)); i.getDetails().setPermissions(p); Details test = sec.checkManagedDetails(i, oldDetails); assertTrue(p.sameRights(test.getPermissions())); assertEquals(test.getOwner().getId(), user.getId()); assertEquals(test.getGroup().getId(), group.getId()); // can't change that value i.getDetails().setOwner(new Experimenter(3L, false)); i.getDetails().setPermissions(p); try { sec.checkManagedDetails(i, oldDetails); fail("should throw sec. vio."); } catch (SecurityViolation sv) { } sec.invalidateEventContext(); } @Test public void testRunAsAdmin() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); assertFalse(cd.getCurrentEventContext().isCurrentUserAdmin()); Mock mockFilter = mock(Filter.class); final Filter filter = (Filter) mockFilter.proxy(); mockFilter.setDefaultStub(new DefaultResultStub()); Mock mockSession = mock(Session.class); final Session session = (Session) mockSession.proxy(); mockSession.expects(atLeastOnce()).method("enableFilter").will( returnValue(filter)); sf.mockQuery.expects(once()).method("execute").will(new Stub() { public Object invoke(Invocation arg0) throws Throwable { ((HibernateCallback) arg0.parameterValues.get(0)) .doInHibernate(session); return null; } public StringBuffer describeTo(StringBuffer arg0) { return arg0.append("call doInHibernate"); } }); AdminAction action = new AdminAction() { public void runAsAdmin() { assertTrue(cd.getCurrentEventContext().isCurrentUserAdmin()); } }; sec.runAsAdmin(action); assertFalse(cd.getCurrentEventContext().isCurrentUserAdmin()); } @Test public void testDoAction() { prepareMocksWithUserDetails(false); sec.loadEventContext(false); try { sec.doAction(new SecureAction() { public <T extends IObject> T updateObject(T... objs) { fail("implement"); return null; } }); fail("Where's the IllegalArgumentEx?"); } catch (IllegalArgumentException iae) { // ok } } @Test public void testTokenFunctionality() throws Exception { Image i = new Image(); assertFalse(sec.hasPrivilegedToken(i)); prepareMocksWithUserDetails(false); sec.loadEventContext(false); sec.doAction(new SecureAction() { public <T extends IObject> T updateObject(T... objs) { assertTrue(sec.hasPrivilegedToken(objs[0])); Image test = new Image(); sec.copyToken(objs[0], test); assertTrue(sec.hasPrivilegedToken(test)); return null; } }, i); } @Test public void testLeaderAndMemberOfGroupsProperlyFilled() throws Exception { prepareMocksWithUserDetails(false); sec.loadEventContext(false); List<Long> l; l = sec.getEventContext().getLeaderOfGroupsList(); assertTrue(l.containsAll(leaderOfGroups)); assertTrue(leaderOfGroups.containsAll(l)); l = sec.getEventContext().getMemberOfGroupsList(); assertTrue(l.containsAll(memberOfGroups)); assertTrue(memberOfGroups.containsAll(l)); } @Test public void testReadOnlyFunctionality() throws Exception { prepareMocksWithUserDetails(true); sec.loadEventContext(true); assertTrue(sec.getEventContext().isReadOnly()); assertNull(sec.getEventContext().getCurrentEventId()); } }