/*
* $Id$
*
* Copyright 2009-2014 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.security.auth;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import ome.conditions.ApiUsageException;
import ome.conditions.ValidationException;
import ome.model.IObject;
import ome.model.internal.Permissions;
import ome.model.meta.Experimenter;
import ome.model.meta.ExperimenterGroup;
import ome.model.meta.GroupExperimenterMap;
import ome.security.SecureAction;
import ome.security.SecuritySystem;
import ome.tools.hibernate.HibernateUtils;
import ome.tools.hibernate.SecureMerge;
import ome.tools.hibernate.SessionFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements {@link RoleProvider}.
*
* Note: All implementations were originally copied from AdminImpl for
* ticket:1226.
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 4.0
*/
public class SimpleRoleProvider implements RoleProvider {
final private static Logger log = LoggerFactory.getLogger(SimpleRoleProvider.class);
final protected SecuritySystem sec;
final protected SessionFactory sf;
private AtomicBoolean ignoreCaseLookup;
public SimpleRoleProvider(SecuritySystem sec, SessionFactory sf) {
this(sec, sf, new AtomicBoolean(false));
}
public SimpleRoleProvider(SecuritySystem sec, SessionFactory sf,
AtomicBoolean ignoreCaseLookup) {
this.sec = sec;
this.sf = sf;
this.ignoreCaseLookup = ignoreCaseLookup;
}
public String nameById(long id) {
Session s = sf.getSession();
return (String) s.createQuery(
"select omeName from Experimenter where id = :id")
.setParameter("id", id).uniqueResult();
}
public long createGroup(String name, Permissions perms, boolean strict) {
return this.createGroup(name, perms, strict, false);
}
public long createGroup(String name, Permissions perms, boolean strict,
boolean isLdap) {
Session s = sf.getSession();
ExperimenterGroup g = groupByName(name, s);
if (g == null) {
g = new ExperimenterGroup();
g.setName(name);
g.setLdap(isLdap);
if (perms == null) {
perms = Permissions.USER_PRIVATE; // ticket:1434
}
g.getDetails().setPermissions(perms);
g = (ExperimenterGroup) s.merge(g);
} else {
if (strict) {
throw new ValidationException("Group already exists: " + name);
}
}
return g.getId();
}
public long createGroup(ExperimenterGroup group) {
group = copyGroup(group);
if (group.getDetails().getPermissions() == null) {
group.getDetails().setPermissions(Permissions.USER_PRIVATE);
}
final Session session = sf.getSession();
ExperimenterGroup g = sec.doAction(new SecureMerge(session), group);
return g.getId();
}
public long createExperimenter(Experimenter experimenter,
ExperimenterGroup defaultGroup, ExperimenterGroup... otherGroups) {
Session session = sf.getSession();
SecureAction action = new SecureMerge(session);
Experimenter e = copyUser(experimenter);
if (isIgnoreCaseLookup()) {
e.setOmeName(e.getOmeName().toLowerCase());
}
e.getDetails().copy(sec.newTransientDetails(e));
e = sec.doAction(action, e);
session.flush();
linkGroupAndUser(defaultGroup, e, false);
if (null != otherGroups) {
for (ExperimenterGroup group : otherGroups) {
linkGroupAndUser(group, e, false);
}
}
return e.getId();
}
public void setDefaultGroup(Experimenter user, ExperimenterGroup group) {
Session session = sf.getSession();
Experimenter foundUser = userById(user.getId(), session);
ExperimenterGroup foundGroup = groupById(group.getId(), session);
Set<GroupExperimenterMap> foundMaps = foundUser
.findGroupExperimenterMap(foundGroup);
if (foundMaps.size() < 1) {
throw new ApiUsageException("Group " + group.getId() + " was not "
+ "found for user " + user.getId());
} else if (foundMaps.size() > 1) {
log.warn(foundMaps.size() + " copies of " + foundGroup
+ " found for " + foundUser);
} else {
// May throw an exception
GroupExperimenterMap newDef = foundMaps.iterator().next();
log.info(String.format("Changing default group for user %s to %s",
foundUser.getId(), group.getId()));
foundUser.setPrimaryGroupExperimenterMap(newDef);
}
// TODO: May want to move this outside the loop
// and after the !newDefaultSet check.
sec.doAction(new SecureMerge(session), foundUser);
}
public void setGroupOwner(Experimenter user, ExperimenterGroup group, boolean value) {
Session session = sf.getSession();
Experimenter foundUser = userById(user.getId(), session);
ExperimenterGroup foundGroup = groupById(group.getId(), session);
Set<GroupExperimenterMap> foundMaps = foundUser
.findGroupExperimenterMap(foundGroup);
if (foundMaps.size() < 1) {
throw new ApiUsageException("Group " + group.getId() + " was not "
+ "found for user " + user.getId());
} else if (foundMaps.size() > 1) {
log.warn(foundMaps.size() + " copies of " + foundGroup
+ " found for " + foundUser);
} else {
// May throw an exception
GroupExperimenterMap newDef = foundMaps.iterator().next();
log.info(String.format("Seeting ownership flag on user %s to %s for %s",
foundUser.getId(), value, group.getId()));
newDef.setOwner(value);
sec.doAction(new SecureMerge(session), newDef);
}
}
public void addGroups(final Experimenter user,
final ExperimenterGroup... groups) {
final Session session = sf.getSession();
final List<String> added = new ArrayList<String>();
Experimenter foundUser = userById(user.getId(), session);
for (ExperimenterGroup group : groups) {
ExperimenterGroup foundGroup = groupById(group.getId(), session);
boolean found = false;
for (ExperimenterGroup currentGroup : foundUser
.linkedExperimenterGroupList()) {
found |= HibernateUtils.idEqual(foundGroup, currentGroup);
}
if (!found) {
linkGroupAndUser(foundGroup, foundUser, false);
added.add(foundGroup.getName());
}
}
fixDefaultGroup(foundUser, session);
}
public void removeGroups(Experimenter user, ExperimenterGroup... groups) {
final Session session = sf.getSession();
Experimenter foundUser = userById(user.getId(), session);
List<Long> toRemove = new ArrayList<Long>();
List<String> removed = new ArrayList<String>();
for (ExperimenterGroup g : groups) {
if (g.getId() != null) {
toRemove.add(g.getId());
}
}
for (GroupExperimenterMap map : foundUser
.<GroupExperimenterMap> collectGroupExperimenterMap(null)) {
Long pId = map.parent().getId();
Long cId = map.child().getId();
if (toRemove.contains(pId)) {
ExperimenterGroup p = groupById(pId, session);
Experimenter c = userById(cId, session);
p.unlinkExperimenter(c);
sec.doAction(new SecureAction(){
public <T extends IObject> T updateObject(T... objs) {
for (T t : objs) {
session.delete(t);
}
session.flush();
return null;
}}, map);
// sec.doAction(new SecureMerge(session, true), p);
removed.add(p.getName());
}
}
fixDefaultGroup(foundUser, session);
session.flush();
}
public boolean isIgnoreCaseLookup() {
return ignoreCaseLookup.get();
}
// ~ Helpers
// =========================================================================
protected GroupExperimenterMap linkGroupAndUser(ExperimenterGroup group,
Experimenter e, boolean owned) {
if (group == null || group.getId() == null) {
throw new ApiUsageException("Group must be persistent.");
}
group = new ExperimenterGroup(group.getId(), false);
// ticket:1021 - check for already added groups
for (GroupExperimenterMap link : e.unmodifiableGroupExperimenterMap()) {
ExperimenterGroup test = link.parent();
if (test.getId().equals(group.getId())) {
return link; // EARLY EXIT!
}
}
GroupExperimenterMap link = e.linkExperimenterGroup(group);
// ticket:1434
link.setOwner(owned);
link.getDetails().copy(sec.newTransientDetails(link));
Session session = sf.getSession();
sec.<IObject> doAction(new SecureMerge(session),
userById(e.getId(), session), link);
session.flush();
return link;
}
protected Experimenter copyUser(Experimenter e) {
if (e.getOmeName() == null) {
throw new ValidationException("OmeName may not be null.");
}
Experimenter copy = new Experimenter();
copy.setOmeName(e.getOmeName());
copy.setFirstName(e.getFirstName());
copy.setMiddleName(e.getMiddleName());
copy.setLastName(e.getLastName());
copy.setEmail(e.getEmail());
copy.setInstitution(e.getInstitution());
copy.setLdap(e.getLdap());
if (e.getDetails() != null && e.getDetails().getPermissions() != null) {
copy.getDetails().setPermissions(e.getDetails().getPermissions());
}
// TODO make ShallowCopy-like which ignores collections and details.
// if possible, values should be validated. i.e. iTypes should say what
// is non-null
return copy;
}
protected ExperimenterGroup copyGroup(ExperimenterGroup g) {
if (g.getName() == null) {
throw new ValidationException("Group name may not be null.");
}
ExperimenterGroup copy = new ExperimenterGroup();
copy.setDescription(g.getDescription());
copy.setName(g.getName());
copy.setLdap(g.getLdap());
copy.setConfig(g.getConfig());
copy.getDetails().copy(sec.newTransientDetails(g));
copy.getDetails().setPermissions(g.getDetails().getPermissions());
// TODO see shallow copy comment on copy user
return copy;
}
private ExperimenterGroup groupByName(String name, Session s) {
Query q = s.createQuery("select g from ExperimenterGroup g "
+ "where g.name = :name");
q.setParameter("name", name);
ExperimenterGroup g = (ExperimenterGroup) q.uniqueResult();
return g;
}
private Experimenter userById(long id, Session s) {
return (Experimenter) s.load(Experimenter.class, id);
}
private ExperimenterGroup groupById(long id, Session s) {
return (ExperimenterGroup) s.load(ExperimenterGroup.class, id);
}
private void fixDefaultGroup(Experimenter u, Session s) {
ExperimenterGroup g = shouldBeDefault(u, s);
if (g != null) {
setDefaultGroup(u, g);
}
}
/**
* If the "user" group is the first element of the list of groups that this
* user is a member of, then check if there's a second value which could be
* used as the default instead.
*/
private ExperimenterGroup shouldBeDefault(Experimenter usr, Session s) {
List<ExperimenterGroup> grps = usr.linkedExperimenterGroupList();
if (grps.size() >= 2) {
// If there are either no groups, or no alternatives so there's
// nothing we need to check.
final String USER = sec.getSecurityRoles().getUserGroupName();
if (USER.equals(grps.get(0).getName())) {
return grps.get(1);
}
}
return null;
}
}