/*
* Copyright 2012 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.server.utests.sec;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import ome.model.core.Image;
import ome.model.internal.Details;
import ome.model.internal.Permissions;
import ome.model.meta.Experimenter;
import ome.model.meta.ExperimenterGroup;
import ome.model.meta.Session;
import ome.security.SystemTypes;
import ome.security.basic.BasicACLVoter;
import ome.security.basic.BasicEventContext;
import ome.security.basic.CurrentDetails;
import ome.security.basic.TokenHolder;
import ome.security.policy.DefaultPolicyService;
import ome.services.sessions.SessionContext;
import ome.services.sessions.SessionContextImpl;
import ome.services.sessions.state.SessionCache;
import ome.services.sessions.stats.NullSessionStats;
import ome.system.Principal;
import org.jmock.MockObjectTestCase;
import org.testng.annotations.Test;
/**
* Intended to test the "write-ability" granted to users based on the current
* context and the object in question. These permissions should be passed
* back via the "disallowAnnotate" and "disallowEdit" flags.
*
* @since 4.4.0
* @see ticket 8277
*/
@Test(groups = { "unit", "permissions", "ticket:8277" })
public class WritePermissionsTest extends MockObjectTestCase {
final static Long ROOT = 0L;
final static Long THE_GROUP = 2L;
final static Long THE_OWNER = 2L;
final static Long GROUP_MEMBER = 3L;
final SessionCache cache = new SessionCache();
final CurrentDetails cd = new CurrentDetails(cache);
final BasicACLVoter voter = new BasicACLVoter(cd, new SystemTypes(),
new TokenHolder(), null, new DefaultPolicyService());
protected Session login(String perms, long user, boolean leader) {
Session s = sess(perms, user, THE_GROUP);
SessionContext sc = context(s, leader);
cache.putSession(s.getUuid(), sc);
BasicEventContext bec = new BasicEventContext(new Principal(s.getUuid()),
new NullSessionStats(), sc);
ExperimenterGroup g = s.getDetails().getGroup();
bec.setGroup(g, g.getDetails().getPermissions());
bec.setOwner(s.getDetails().getOwner());
cd.login(bec);
return s;
}
protected Details objectBelongingTo(Session session, long user) {
return objectBelongingTo(session, user,
session.getDetails().getPermissions());
}
protected Details objectBelongingTo(Session session, long user, String s) {
return objectBelongingTo(session, user, Permissions.parseString(s));
}
/**
* Creates an object which is in the group given by the {@link Session}
* object, but which belongs to the given {@link Experimenter} and has
* the given {@link Permissions}
*
* @param session
* Session which the object was created during.
* @param user
* User who owns this object.
* @param p
* Permissions to set on the object details.
*/
protected Details objectBelongingTo(Session session, long user, Permissions p) {
Image i = new Image();
Details d = i.getDetails();
d.setOwner(new Experimenter(user, true));
d.setGroup(session.getDetails().getGroup());
d.setPermissions(p);
voter.postProcess(i);
return d;
}
// object setting differs from group
// =========================================================================
// Since in 4.4, it's possible for permissions settings of an object to
// differ from those of the group, we need to make sure that post-processing
// properly maps to the group permissions.
public void testDifferentPerms() {
Session s = login("rwr---", THE_OWNER, false);
Details d = objectBelongingTo(s, THE_OWNER, "r-r-r-");
assertCanAnnotate(d);
assertCanEdit(d);
assertEquals("rwr---", d.getPermissions().toString());
}
// rwr, non-system owner
// =========================================================================
class Data {
final String name;
final String perms;
final Long user;
final boolean leader;
final Long owner;
boolean annotate, delete, edit, link;
Data(String name, String perms, Long user, boolean leader, Long owner,
boolean annotate, boolean delete, boolean edit, boolean link) {
this.name = name;
this.perms = perms;
this.user = user;
this.leader = leader;
this.owner = owner;
this.annotate = annotate;
this.delete = delete;
this.edit = edit;
this.link = link;
}
void run() {
Session s = login(perms, user, leader);
Details d = objectBelongingTo(s, owner);
Permissions p = d.getPermissions();
assertPerms("Annotate", annotate, !p.isDisallowAnnotate());
assertPerms("Delete", delete, !p.isDisallowDelete());
assertPerms("Edit", edit, !p.isDisallowEdit());
assertPerms("Link", link, !p.isDisallowLink());
}
void assertPerms(String type, boolean expected, boolean found) {
String msg = String.format("%s: allow%s broken!", name, type);
assertEquals(msg, expected, found);
}
}
final Data[] data = new Data[] {
// rw
new Data("rw: owner can all", "rw----", THE_OWNER, false, THE_OWNER,
true, true, true, true),
new Data("rw: admin cannot link", "rw----", ROOT, false, THE_OWNER,
false, true, true, false),
new Data("rw: member can do nothing", "rw----", GROUP_MEMBER, false, THE_OWNER,
false, false, false, false),
new Data("rw: leader cannot link", "rw----", GROUP_MEMBER, true, THE_OWNER,
false, true, true, false),
// rwr
new Data("rwr: owner can all", "rwr---", THE_OWNER, false, THE_OWNER,
true, true, true, true),
new Data("rwr: admin can all", "rwr---", ROOT, false, THE_OWNER,
true, true, true, true),
new Data("rwr: member cannot all", "rwr---", GROUP_MEMBER, false, THE_OWNER,
false, false, false, false),
new Data("rwr: leader can all", "rwr---", GROUP_MEMBER, true, THE_OWNER,
true, true, true, true)
};
public void testData() {
for (Data entry : data) {
entry.run();
}
}
// Helpers
// =========================================================================
void assertCanAnnotate(Details d) {
assertFalse(d.getPermissions().isDisallowAnnotate());
}
void assertCanEdit(Details d) {
assertFalse(d.getPermissions().isDisallowEdit());
}
void assertCanLink(Details d) {
assertFalse(d.getPermissions().isDisallowLink());
}
void assertCanDelete(Details d) {
assertFalse(d.getPermissions().isDisallowDelete());
}
void assertCannotAnnotate(Details d) {
assertTrue(d.getPermissions().isDisallowAnnotate());
}
void assertCannotEdit(Details d) {
assertTrue(d.getPermissions().isDisallowEdit());
}
void assertCannotDelete(Details d) {
assertTrue(d.getPermissions().isDisallowDelete());
}
void assertCannotLink(Details d) {
assertTrue(d.getPermissions().isDisallowLink());
}
Session sess(String perms, long user, long group) {
Permissions p = Permissions.parseString(perms);
Session s = new Session();
s.setStarted(new Timestamp(System.currentTimeMillis()));
s.setTimeToIdle(0L);
s.setTimeToLive(0L);
s.setUuid(UUID.randomUUID().toString());
s.getDetails().setPermissions(p);
// group
ExperimenterGroup g = new ExperimenterGroup(group, true);
g.getDetails().setPermissions(Permissions.parseString(perms));
s.getDetails().setGroup(g);
// user
Experimenter e = new Experimenter(user, true);
s.getDetails().setOwner(e);
return s;
}
SessionContext context(Session s, boolean leader) {
final Long user = s.getDetails().getOwner().getId();
final Long group = s.getDetails().getGroup().getId();
List<String> roles = new ArrayList<String>();
List<Long> memberOf = new ArrayList<Long>();
List<Long> leaderOf = new ArrayList<Long>();
roles.add("user");
memberOf.add(1L);
memberOf.add(group);
if (user.equals(0L)) { // use "root" as proxy for "admin"
memberOf.add(0L); // system
roles.add("system");
}
if (leader) {
leaderOf = Arrays.asList(group);
}
return new SessionContextImpl(s, leaderOf, memberOf, roles,
new NullSessionStats(), null);
}
}