/*
* Copyright 2008 - 2014 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.services.sharing;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import ome.annotations.NotNull;
import ome.annotations.RolesAllowed;
import ome.api.IShare;
import ome.api.ServiceInterface;
import ome.api.local.LocalAdmin;
import ome.api.local.LocalShare;
import ome.conditions.ApiUsageException;
import ome.conditions.ValidationException;
import ome.logic.AbstractLevel2Service;
import ome.model.IObject;
import ome.model.annotations.Annotation;
import ome.model.annotations.CommentAnnotation;
import ome.model.annotations.SessionAnnotationLink;
import ome.model.internal.Details;
import ome.model.meta.Event;
import ome.model.meta.Experimenter;
import ome.model.meta.ExperimenterGroup;
import ome.model.meta.Session;
import ome.model.meta.Share;
import ome.parameters.Parameters;
import ome.security.AdminAction;
import ome.security.SecureAction;
import ome.security.basic.BasicSecuritySystem;
import ome.services.mail.MailUtil;
import ome.services.sessions.SessionContext;
import ome.services.sessions.SessionManager;
import ome.services.sharing.data.Obj;
import ome.services.sharing.data.ShareData;
import ome.services.util.Executor;
import ome.services.util.ServiceHandler;
import ome.system.EventContext;
import ome.system.Principal;
import ome.tools.hibernate.QueryBuilder;
import ome.util.ContextFilter;
import ome.util.Filterable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.springframework.mail.MailException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.transaction.annotation.Transactional;
/**
* Note: {@link SessionManager} should not be used to obtain the {@link Share}
* data since it may not be completely in sync. i.e. Don't use SM.find()
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 3.0-Beta4
* @see IShare
*/
@Transactional(readOnly = true)
public class ShareBean extends AbstractLevel2Service implements LocalShare {
public final static Logger log = LoggerFactory.getLogger(ShareBean.class);
public final static String NS_ENABLED = "ome.share.enabled";
public final static String NS_COMMENT = "ome.share.comment/";
final protected LocalAdmin admin;
final protected SessionManager sessionManager;
final protected ShareStore store;
final protected Executor executor;
final protected MailUtil mailUtil;
public final Class<? extends ServiceInterface> getServiceInterface() {
return IShare.class;
}
public ShareBean(LocalAdmin admin, SessionManager mgr, ShareStore store,
Executor executor, MailUtil mailUtil) {
this.admin = admin;
this.sessionManager = mgr;
this.store = store;
this.executor = executor;
this.mailUtil = mailUtil;
}
// ~ Service Methods
// ===================================================
@RolesAllowed("user")
public void activate(long shareId) {
// Check status of the store
ShareData data = getShareIfAccessible(shareId);
if (data == null) {
throw new ValidationException("No accessible share:" + shareId);
}
if (!data.enabled) {
throw new ValidationException("Share disabled.");
}
// Ok, set share
setShareId(shareId);
}
@RolesAllowed("user")
public void deactivate() {
setShareId(null);
}
/**
* Set the share id on the current session context.
*
* Previously this method was used throughout the code base in order to
* "open up" a session. This however, has issues since it can lead to data
* leakage (8037). Using the omero.group functionality (3529), this method
* no longer needs to be public.
*
* @see <a href="http://trac.openmicroscopy.org/ome/ticket/2219">ticket:2219</a>
* @see <a href="http://trac.openmicroscopy.org/ome/ticket/3529">ticket:3529</a>
* @see <a href="http://trac.openmicroscopy.org/ome/ticket/8037">ticket:8037</a>
*/
private Long setShareId(Long shareId) {
String sessId = getSecuritySystem().getEventContext().getCurrentSessionUuid();
SessionContext sc = (SessionContext) sessionManager
.getEventContext(new Principal(sessId));
Long old = sc.getCurrentShareId();
sc.setShareId(shareId);
return old;
}
/**
* @see <a href="http://trac.openmicroscopy.org/ome/ticket/2219">ticket:2219</a>
*/
public void resetReadFilter(org.hibernate.Session s) {
// ticket:2397 and ticket:2219
// Necessary to update the current filter in order to
// have the new shareId be updated
BasicSecuritySystem bss = (BasicSecuritySystem) sec;
bss.loadEventContext(true);
bss.updateReadFilter(s);
}
// ~ Getting shares and objects (READ)
// =========================================================================
@RolesAllowed("user")
public Map<Long, Long> getMemberCount(final Set<Long> shareIds) {
if (shareIds == null || shareIds.size() == 0) {
throw new ApiUsageException("Nothing to do");
}
final QueryBuilder qb = new QueryBuilder();
qb.select("share2.id", "count(distinct links2.id)");
qb.from("ShareMember", "links2");
qb.join("links2.parent","share2", false, false);
qb.where();
qb.paramList("ids", shareIds);
qb.and("share2.id in (:ids) and share2.id in ");
// -- subselect for all accessible shares
{
QueryBuilder sub = new QueryBuilder();
sub.select("share");
sub.from("ShareMember", "memberLinks");
sub.join("memberLinks.parent", "share", false, false);
sub.join("memberLinks.child", "user", false, false);
sub.where();
sub.and("1=1"); // WORKAROUND for ticket:1239 for root
applyIfShareAccessible(sub);
qb.subselect(sub);
}
// -- end subselect
qb.append("group by share2.id");
final Map<Long, Long> rv = new HashMap<Long, Long>(shareIds.size());
sec.runAsAdmin(new AdminAction(){
public void runAsAdmin() {
iQuery.execute(new HibernateCallback<Object>() {
public Object doInHibernate(org.hibernate.Session s)
throws HibernateException, SQLException {
Query q = qb.query(s);
List<Object[]> results = q.list();
if (results.size() != shareIds.size()) {
throw new ValidationException(
"Missing or protected shares specified");
}
for (Object[] values : results) {
Long shareId = (Long) values[0];
Long count = (Long) values[1];
rv.put(shareId, count);
}
return null;
}});
}});
return rv;
}
long getCurrentUserId() {
return getSecuritySystem().getEventContext().getCurrentUserId();
}
@RolesAllowed("user")
public Set<Session> getOwnShares(boolean active) {
long id = getCurrentUserId();
List<ShareData> shares = store.getShares(id, true /* own */, active);
return sharesToSessions(shares);
}
@RolesAllowed("user")
public Set<Session> getMemberShares(boolean active) {
long id = getCurrentUserId();
List<ShareData> shares = store.getShares(id, false /* own */, active);
return sharesToSessions(shares);
}
@RolesAllowed("user")
public Set<Session> getSharesOwnedBy(@NotNull Experimenter user,
boolean active) {
List<ShareData> shares = store.getShares(user.getId(), true /* own */,
active);
return sharesToSessions(shares);
}
@RolesAllowed("user")
public Set<Session> getMemberSharesFor(@NotNull Experimenter user,
boolean active) {
List<ShareData> shares = store.getShares(user.getId(), false /* own */,
active);
return sharesToSessions(shares);
}
@RolesAllowed("user")
public Share getShare(long sessionId) {
Share session = null;
ShareData data = getShareIfAccessible(sessionId);
if (data != null) {
session = shareToSession(data);
}
return session;
}
@RolesAllowed("user")
public <T extends IObject> List<T> getContents(long shareId) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
return list(data.objectList);
}
@RolesAllowed("user")
public <T extends IObject> List<T> getContentSubList(long shareId,
int start, int finish) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
try {
return list(data.objectList.subList(start, finish));
} catch (IndexOutOfBoundsException ioobe) {
throw new ApiUsageException("Invalid range: " + start + " to "
+ finish);
}
}
@RolesAllowed("user")
public <T extends IObject> Map<Class<T>, List<Long>> getContentMap(
long shareId) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
return map(data.objectMap);
}
@RolesAllowed("user")
public int getContentSize(long shareId) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
return data.objectList.size();
}
// ~ Creating share (WRITE)
// =========================================================================
@RolesAllowed("user")
@Transactional(readOnly = false)
public <T extends IObject> long createShare(
@NotNull final String description, Timestamp expiration,
List<T> items, List<Experimenter> exps, List<String> guests,
final boolean enabled) {
//
// Input validation
//
final long time = expirationAsLong(System.currentTimeMillis(),
expiration);
if (exps == null) {
exps = Collections.emptyList();
}
if (guests == null) {
guests = Collections.emptyList();
}
if (items == null) {
items = Collections.emptyList();
}
//
// Setting defaults on new session
//
final EventContext ec = getSecuritySystem().getEventContext();
final String omename = ec.getCurrentUserName();
final Long user = ec.getCurrentUserId();
final Long group = ec.getCurrentGroupId();
final Future<Share> future = executor.submit(new Callable<Share>() {
public Share call() throws Exception {
return sessionManager.createShare(new Principal(omename),
enabled, time, "SHARE", description, group);
}
});
final List<T> _items = items;
final List<String> _guests = guests;
final List<Long> _users = new ArrayList<Long>(exps.size());
for (Experimenter e : exps) {
_users.add(e.getId());
}
final long shareId = executor.get(future).getId();
final Share share = iQuery.find(Share.class, shareId); // Reload!
this.sec.doAction(new SecureShare() {
@Override
void doUpdate(Share share) {
store.set(share, user, _items, _users, _guests, enabled);
}
}, share);
adminFlush();
return share.getId();
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void setDescription(long shareId, String description) {
Share share = (Share) iQuery.find(Share.class, shareId);
ShareData data = store.get(shareId);
share.setMessage(description);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void setExpiration(long shareId, Timestamp expiration) {
Share share = (Share) iQuery.find(Share.class, shareId);
ShareData data = store.get(shareId);
share.setTimeToLive(expirationAsLong(share.getStarted().getTime(),
expiration));
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void setActive(long shareId, boolean active) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.enabled = active;
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void closeShare(long shareId) {
final String uuid = idToUuid(shareId);
Future<Object> future = executor.submit(new Callable<Object>() {
public Object call() throws Exception {
sessionManager.close(uuid);
return null;
}
});
executor.get(future);
}
// ~ Getting items
// =========================================================================
@RolesAllowed("user")
@Transactional(readOnly = false)
public <T extends IObject> void addObjects(long shareId,
@NotNull T... objects) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
Graph graph = new Graph();
for (T object : objects) {
graph.filter("top", object);
}
_addGraph(data, graph);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public <T extends IObject> void addObject(long shareId, @NotNull T object) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
Graph graph = new Graph();
graph.filter("top", object);
_addGraph(data, graph);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public <T extends IObject> void removeObjects(long shareId,
@NotNull T... objects) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
for (T object : objects) {
List<Long> ids = data.objectMap.get(object.getClass().getName());
if (ids != null) {
ids.remove(object.getId());
}
}
List<Obj> toRemove = new ArrayList<Obj>();
for (T object : objects) {
for (Obj obj : data.objectList) {
if (obj.type.equals(object.getClass().getName())) {
if (obj.id == object.getId().longValue()) {
toRemove.add(obj);
}
}
}
}
data.objectList.removeAll(toRemove);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public <T extends IObject> void removeObject(long shareId, @NotNull T object) {
ShareData data = getShareIfAccessible(shareId);
List<Long> ids = data.objectMap.get(object.getClass().getName());
if (ids != null) {
ids.remove(object.getId());
}
List<Obj> toRemove = new ArrayList<Obj>();
for (Obj obj : data.objectList) {
if (obj.type.equals(object.getClass().getName())) {
if (obj.id == object.getId().longValue()) {
toRemove.add(obj);
}
}
}
data.objectList.removeAll(toRemove);
storeShareData(shareId, data);
}
// ~ Getting comments
// =========================================================================
@RolesAllowed("user")
public Map<Long, Long> getCommentCount(final Set<Long> ids) {
if (ids == null || ids.size() == 0) {
throw new ApiUsageException("Nothing to do");
}
final QueryBuilder qb = new QueryBuilder();
qb.select("share.id","count(distinct sal)");
qb.from("ShareMember", "sm");
qb.join("sm.parent", "share", false, false);
qb.join("share.annotationLinks","sal", false, false);
qb.join("sal.child", "comment", false, false);
qb.join("sm.child", "user", false, false);
qb.where();
qb.and("share.id in (:ids)");
qb.paramList("ids", ids);
qb.and("comment.ns like :ns");
qb.param("ns", NS_COMMENT + "%");
applyIfShareAccessible(qb);
qb.append("group by share.id");
final Map<Long, Long> rv = new HashMap<Long, Long>(ids.size());
final Long old = setShareId(-1L); // Allow everything. ticket:2219
try {
iQuery.execute(new HibernateCallback<Object>() {
public Object doInHibernate(org.hibernate.Session s)
throws HibernateException, SQLException {
resetReadFilter(s);
Query q = qb.query(s);
List<Object[]> counts = q.list();
// ticket:1227 - Returning 0 if missing
// if (counts.size() != ids.size()) {
// throw new ValidationException(
// "Missing or protected shares specified");
//}
for (Object[] values : counts) {
Long shareId = (Long) values[0];
Long count = (Long) values[1];
rv.put(shareId, count);
}
return null;
}
});
} finally {
setShareId(old);
}
for (Long id : ids) {
Long value = rv.get(id);
if (value == null) {
rv.put(id, 0L);
}
}
return rv;
}
@RolesAllowed("user")
@SuppressWarnings("unchecked")
public List<Annotation> getComments(final long shareId) {
final List<Annotation> rv = new ArrayList<Annotation>();
if (getShareIfAccessible(shareId) == null) {
return rv; // EARLY EXIT.
}
// Now load the comments with the read filter,
// otherwise it is necessary to add every link
// to the share
Long oldShareId = setShareId(-1L);
try {
List<SessionAnnotationLink> links =
(List<SessionAnnotationLink>) iQuery.execute(new HibernateCallback<Object>(){
public Object doInHibernate(org.hibernate.Session arg0)
throws HibernateException, SQLException {
resetReadFilter(arg0);
return arg0.createQuery(
"select l from SessionAnnotationLink l "
+ "join fetch l.details.owner "
+ "join fetch l.parent as share "
+ "join fetch l.child as comment "
+ "join fetch comment.details.updateEvent "
+ "where share.id = :id and comment.ns like :ns ")
.setParameter("ns", NS_COMMENT + "%")
.setParameter("id", shareId)
.list();
}});
for (SessionAnnotationLink link : links) {
rv.add(link.child());
}
} finally {
setShareId(oldShareId);
}
return rv;
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public CommentAnnotation addComment(final long shareId,
@NotNull final String commentText) {
getShareIfAccessible(shareId);
ExperimenterGroup group = iQuery.get(Share.class, shareId).getGroup();
final CommentAnnotation[] rv = new CommentAnnotation[1];
sec.runAsAdmin(group, new AdminAction(){
public void runAsAdmin() {
final Share share = iQuery.get(Share.class, shareId);
CommentAnnotation comment = new CommentAnnotation();
comment.setTextValue(commentText);
comment.setNs(NS_COMMENT);
share.linkAnnotation(comment);
iUpdate.flush();
rv[0] = iQuery.get(CommentAnnotation.class, comment.getId());
}});
return rv[0];
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public CommentAnnotation addReply(long shareId, @NotNull String comment,
@NotNull CommentAnnotation replyTo) {
throw new UnsupportedOperationException();
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void deleteComment(@NotNull Annotation comment) {
List<SessionAnnotationLink> links = iQuery.findAllByQuery(
"select l from SessionAnnotationLink l "
+ "where l.child.id = :id", new Parameters()
.addId(comment.getId()));
for (SessionAnnotationLink sessionAnnotationLink : links) {
iUpdate.deleteObject(sessionAnnotationLink);
}
iUpdate.deleteObject(comment);
}
// ~ Member administration
// =========================================================================
@RolesAllowed("user")
public Set<Experimenter> getAllMembers(long shareId) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
List<Experimenter> e = loadMembers(data);
return new HashSet<Experimenter>(e);
}
@RolesAllowed("user")
public Set<String> getAllGuests(long shareId) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
return new HashSet<String>(data.guests);
}
@RolesAllowed("user")
public Set<String> getAllUsers(long shareId) throws ValidationException {
ShareData data = getShareIfAccessible(shareId);
List<Experimenter> members = loadMembers(data);
Set<String> names = new HashSet<String>();
for (Experimenter e : members) {
names.add(e.getOmeName());
}
for (String string : data.guests) {
if (names.contains(string)) {
throw new ValidationException(string
+ " is both a guest name and a member name");
} else {
names.add(string);
}
}
return names;
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void addUsers(long shareId, Experimenter... exps) {
List<Experimenter> es = Arrays.asList(exps);
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
for (Experimenter experimenter : es) {
data.members.add(experimenter.getId());
}
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void addGuests(long shareId, String... emailAddresses) {
List<String> addresses = Arrays.asList(emailAddresses);
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.guests.addAll(addresses);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void removeUsers(long shareId, List<Experimenter> exps) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
for (Experimenter experimenter : exps) {
data.members.remove(experimenter.getId());
}
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void removeGuests(long shareId, String... emailAddresses) {
List<String> addresses = Arrays.asList(emailAddresses);
ShareData data = getShareIfAccessible(shareId);
data.guests.removeAll(addresses);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void addUser(long shareId, Experimenter exp) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.members.add(exp.getId());
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void addGuest(long shareId, String emailAddress) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.guests.add(emailAddress);
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void removeUser(long shareId, Experimenter exp) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.members.remove(exp.getId());
storeShareData(shareId, data);
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void removeGuest(long shareId, String emailAddress) {
ShareData data = getShareIfAccessible(shareId);
throwOnNullData(shareId, data);
data.guests.remove(emailAddress);
storeShareData(shareId, data);
}
// Events
// =========================================================================
@RolesAllowed("user")
public Map<String, Experimenter> getActiveConnections(long shareId) {
throw new UnsupportedOperationException();
}
@RolesAllowed("user")
public List<Event> getEvents(long shareId, Experimenter experimenter,
Timestamp from, Timestamp to) {
throw new UnsupportedOperationException();
}
@RolesAllowed("user")
public Map<String, Experimenter> getPastConnections(long shareId) {
throw new UnsupportedOperationException();
}
@RolesAllowed("user")
public void invalidateConnection(long shareId, Experimenter exp) {
throw new UnsupportedOperationException();
}
@RolesAllowed("user")
@Transactional(readOnly = false)
public void notifyMembersOfShare(long shareId, String subject, String message,
boolean html) {
EventContext ec = getSecuritySystem().getEventContext();
Set<Experimenter> exps = getAllMembers(shareId);
exps.add(getShare(shareId).getOwner());
Map<Experimenter, String> errors = new HashMap<Experimenter, String>();
for (final Experimenter e : exps) {
if (e.getId() != ec.getCurrentUserId() && e.getEmail() != null
&& mailUtil.validateEmail(e.getEmail())) {
try {
mailUtil.sendEmail(e.getEmail(), subject, message, html,
null, null);
} catch (MailException me) {
errors.put(e, me.getMessage());
}
}
}
if (!errors.isEmpty()) {
log.error(ServiceHandler.getResultsString(errors, null));
}
}
// Helpers
// =========================================================================
protected String idToUuid(long shareId) {
Session s = iQuery.get(Session.class, shareId);
return s.getUuid();
}
protected List<Experimenter> loadMembers(ShareData data) {
List<Experimenter> members = new ArrayList<Experimenter>();
if (data.members.size() > 0)
members = iQuery.findAllByQuery("select e from Experimenter e "
+ "where e.id in (:ids)", new Parameters()
.addIds(data.members));
return members;
}
/**
* Convert a {@link Timestamp expiration} into a long which can be set on
* {@link Session#setTimeToLive(Long)}.
*
* @return the time in milliseconds that this session can exist.
*/
public static long expirationAsLong(long started, Timestamp expiration) {
long time;
if (expiration != null) {
time = expiration.getTime();
if (time < System.currentTimeMillis()) {
throw new ApiUsageException(
"Expiration time must be in the future.");
}
} else {
time = Long.MAX_VALUE;
}
return time - started;
}
protected Set<Session> sharesToSessions(List<ShareData> datas) {
/*
* TODO: When Share will have details method can be updated: +
* "join fetch sh.details.owner where sh.id in (:ids) ",
*/
/*
* Set<Session> sessions = new HashSet<Session>(); for (ShareData data :
* datas) { sessions.add(shareToSession(data)); } return sessions;
*/
final Set<Long> ids = new HashSet<Long>();
for (ShareData data : datas) {
ids.add(data.id);
}
if (ids.size() == 0) {
return Collections.emptySet();
}
List<Session> list = iQuery.execute(new HibernateCallback<Object>() {
public Object doInHibernate(org.hibernate.Session arg0)
throws HibernateException, SQLException {
BasicSecuritySystem bss = (BasicSecuritySystem) sec;
try {
bss.disableReadFilter(arg0);
return arg0
.createQuery(
"select sh from Session sh "
+ "join fetch sh.owner "
+ "where sh.id in (:ids) ")
.setParameterList("ids", ids).list();
} finally {
bss.enableReadFilter(arg0);
}
}
});
for (Session session : list) {
if (session != null) {
session.putAt("#2733", "ALLOW");
}
}
return new HashSet<Session>(list);
}
protected Share shareToSession(final ShareData data) {
Share share = iQuery.execute(new HibernateCallback<Object>() {
public Object doInHibernate(org.hibernate.Session arg0)
throws HibernateException, SQLException {
BasicSecuritySystem bss = (BasicSecuritySystem) sec;
try {
bss.disableReadFilter(arg0);
return arg0
.createQuery(
"select sh from Share sh "
+ "join fetch sh.owner "
+ "where sh.id = :id")
.setParameter("id", data.id).uniqueResult();
} finally {
bss.enableReadFilter(arg0);
}
}
});
if (share != null) {
share.putAt("#2733", "ALLOW");
}
return share;
}
@SuppressWarnings("unchecked")
protected <T extends IObject> Map<Class<T>, List<Long>> map(
Map<String, List<Long>> map) {
Map<Class<T>, List<Long>> rv = new HashMap<Class<T>, List<Long>>();
for (String key : map.keySet()) {
try {
Class<T> kls = (Class<T>) Class.forName(key);
rv.put(kls, map.get(key));
} catch (Exception e) {
throw new ValidationException("Share contains invalid type: "
+ key);
}
}
return rv;
}
@SuppressWarnings("unchecked")
protected <T extends IObject> List<T> list(List<Obj> objectList) {
List<T> rv = new ArrayList<T>();
for (Obj o : objectList) {
T t;
try {
t = (T) Class.forName(o.type).newInstance();
} catch (Exception e) {
throw new ValidationException("Share contains invalid type: "
+ o.type);
}
t.setId(o.id);
t.unload();
rv.add(t);
}
return rv;
}
protected void adminFlush() {
getSecuritySystem().runAsAdmin(new AdminAction() {
public void runAsAdmin() {
iUpdate.flush();
}
});
}
protected void throwOnNullData(long shareId, ShareData data) {
if (data == null) {
throw new ValidationException("Share not found: " + shareId);
}
}
/**
* If the current user is not an admin, then this methods adds a subclause
* to the HQL:
*
* AND ( share.owner.id = :userId or user.id = :userId )
*
* {@link QueryBuilder#where()} should already have been called.
*/
protected void applyIfShareAccessible(QueryBuilder qb) {
EventContext ec = getSecuritySystem().getEventContext();
if ( ! ec.isCurrentUserAdmin()) {
qb.param("userId", ec.getCurrentUserId());
qb.and("(");
qb.append("share.owner.id = :userId" );
qb.append(" OR ");
qb.append("user.id = :userId" );
qb.append(" ) ");
}
}
/**
* Loads share and checks it's owner and member data against the current
* context (owner/member/admin). This method must be kept in sync with
* {@link #applyIfShareAccessible(QueryBuilder)} which does the same check
* at the database rather than binary data level.
*/
protected ShareData getShareIfAccessible(long shareId) {
EventContext ec = getSecuritySystem().getEventContext();
boolean isAdmin = ec.isCurrentUserAdmin();
long userId = ec.getCurrentUserId();
return store.getShareIfAccessible(shareId, isAdmin, userId);
}
protected void _addGraph(ShareData data, Graph g) {
for (IObject object : g.objects()) {
List<Long> ids = data.objectMap.get(object.getClass().getName());
if (ids == null) {
ids = new ArrayList<Long>();
data.objectMap.put(object.getClass().getName(), ids);
}
if (!ids.contains(object.getId())) {
ids.add(object.getId());
Obj obj = new Obj();
obj.type = object.getClass().getName();
obj.id = object.getId();
data.objectList.add(obj);
}
}
}
// Graph : used for
// =========================================================================
private static class Graph extends ContextFilter {
public List<IObject> objects() {
List<IObject> rv = new ArrayList<IObject>();
for (Object o : _cache.keySet()) {
if (o instanceof IObject) {
IObject obj = (IObject) o;
rv.add(obj);
}
}
return rv;
}
@Override
protected void doFilter(String fieldId, Filterable f) {
if (!(f instanceof Details)) {
super.doFilter(fieldId, f);
}
}
}
// All update access should be performed with these methods to keep
// everything in sync. The other methods which mutate are create & close
// =========================================================================
abstract class SecureShare implements SecureAction {
public final <T extends IObject> T updateObject(T... objs) {
doUpdate((Share) objs[0]);
return null;
}
abstract void doUpdate(Share share);
}
class SecureStore extends SecureShare {
ShareData data;
SecureStore(ShareData data) {
this.data = data;
}
@Override
void doUpdate(Share share) {
store.update(share, data);
}
}
protected void storeShareData(long shareId, ShareData data) {
// This should reload the object already in the first-cache
Share share = iQuery.get(Share.class, shareId);
this.sec.doAction(new SecureStore(data), share);
adminFlush();
}
/*
* private void updateShare(final Share share) { Future<Object> future =
* executor.submit(new Callable<Object>() { public Object call() throws
* Exception { sessionManager.update(share, true); return null; } });
* executor.get(future); }
*/
}