/* * Copyright (C) 2012 Glencoe Software, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package ome.server.itests.sec; import org.testng.annotations.Test; import ome.conditions.SecurityViolation; import ome.model.ILink; import ome.model.IObject; import ome.model.annotations.CommentAnnotation; import ome.model.annotations.ImageAnnotationLink; import ome.model.containers.Dataset; import ome.model.containers.DatasetImageLink; import ome.model.core.Image; import ome.model.core.Pixels; import ome.model.display.RenderingDef; import ome.model.internal.Permissions; import ome.model.meta.ExperimenterGroup; import ome.security.basic.BasicACLVoter; import ome.security.basic.OmeroInterceptor; import ome.server.itests.AbstractManagedContextTest; import ome.system.EventContext; import ome.testing.ObjectFactory; /** * Similar to {@link SecurityFilterTest}, but rather than testing READ * operations tests WRITE (and ANNOTATE) operations, both in the form of the * {@link BasicACLVoter} as well as {@link OmeroInterceptor} which is * responsible for evaluating linkages. */ @Test(groups = { "ticket:8565", "security" }) public class AclVoterTest extends AbstractManagedContextTest { /** * This is the primary testing sequence that is performed on various * data graphs, and various permission settings. All operations take * place as the current user. * * @param p * Prepared {@link Pixels} object which will be tested on. * @param canRender * Whether the current user can view the image, i.e. attach rdefs * and thumbnails * @param canAnnotate * Whether the current user can attach annotations and rois. * @param canEdit * Whether the current user can modify the data. * @param canUse * Where the current user can link the data into other groups. */ void perform(Pixels p, boolean canRender, boolean canAnnotate, boolean canEdit, boolean canUse) { Image i = p.getImage(); // Render RenderingDef rdef = ObjectFactory.createRenderingDef(); rdef.setPixels(p); assertSave(rdef, canRender); // Annotate assertSave(annotate(i), canAnnotate); // use assertSave(use(i), canUse); // edit i = reload(i); i.setName(uuid()); i = assertSave(i, canEdit); // store with new update event. } // ~ User (i.e. owner) write operations // ========================================================================= void assertUserCan(String perms, boolean canRender, boolean canAnnotate, boolean canEdit, boolean canUse) { // The data has to be created in a group that is at least read-write // since otherwise the Pixels object cannot be linked to the Image // object. Pixels p = pixels("rw----"); EventContext user = iAdmin.getEventContext(); ExperimenterGroup group = currentGroup(); loginRootKeepGroup(); iAdmin.changePermissions(group, Permissions.parseString(perms)); login(user); user = iAdmin.getEventContext(); // Refresh perform(p, canRender, canAnnotate, canEdit, canUse); } /** * Validate operations as the data owner */ @Test public void testUser() { assertUserCan("r-----", true, false, false, false); assertUserCan("ra----", true, true, false, false); assertUserCan("rw----", true, true, true, true); } // ~ Group member (i.e. non-owner, non-leader) write operations // ========================================================================= /** * We assume that all the perms passed permit linking for the owner * member and therefore we don't have to perform a chmod. */ void assertMemberCan(String perms, boolean canRender, boolean canAnnotate, boolean canEdit, boolean canUse) { // The data has to be created in a group that is at least read-write // since otherwise the Pixels object cannot be linked to the Image // object. Pixels p = pixels(perms); loginNewUserInOtherUsersGroup(currentUser()); perform(p, canRender, canAnnotate, canEdit, canUse); } /** * Validate operations as a group member (non-data owner) */ @Test public void testMember() { assertMemberCan("rwr---", true, false, false, false); assertMemberCan("rwra--", true, true, false, false); assertMemberCan("rwrw--", true, true, true, true); } // ~ Helpers // ========================================================================= Pixels pixels(String perms) { loginNewUser(Permissions.parseString(perms)); Pixels p = ObjectFactory.createPixelGraph(null); Image i = iUpdate.saveAndReturnObject(p.getImage()); return i.getPrimaryPixels(); } Image image(String perms) { loginNewUser(Permissions.parseString(perms)); Image i = new_Image("ticket:8565"); return iUpdate.saveAndReturnObject(i); } ILink annotate(Image i) { ImageAnnotationLink ial = new ImageAnnotationLink(); ial.link(i, new CommentAnnotation()); return ial; } ILink use(Image i) { Dataset d = new Dataset(); d.setName("tickeT:8565"); DatasetImageLink dil = new DatasetImageLink(); dil.link(d, i); return dil; } @SuppressWarnings("unchecked") <T extends IObject> T reload(T o) { return (T) iQuery.get(o.getClass(), o.getId()); } <T extends IObject> T assertSave(T o, boolean pass) { try { T rv = iUpdate.saveAndReturnObject(o); if (!pass) { fail("No secvio thrown! :" + o); } return rv; } catch (SecurityViolation sv) { if (pass) { throw sv; } return o; // Return original. } } }