Issue #2822 only allow transfer leader if participant is using shared display

Former-commit-id: af9577548e40397c67f72eb60485ee8de48f169b
This commit is contained in:
Brian Clements 2014-04-07 12:07:37 -05:00
parent 8ab1bab974
commit 5250f7a442
5 changed files with 162 additions and 8 deletions

View file

@ -36,6 +36,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant;
* Feb 13, 2014 2751 bclement changed sendObjectToPeer id to VenueParticipant
* Feb 13, 2014 2751 njensen Added changeLeader()
* Feb 19, 2014 2751 bclement Added isClosed()
* Apr 15, 2014 2822 bclement added isSharedDisplayClient()
*
* </pre>
*
@ -124,4 +125,10 @@ public interface ISharedDisplaySession extends IVenueSession {
*/
public boolean isClosed();
/**
* @param participant
* @return true if the participant is viewing the shared display
*/
public boolean isSharedDisplayClient(VenueParticipant participant);
}

View file

@ -33,6 +33,10 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.pubsub.PubSubElementType;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider;
import org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider;
import com.google.common.eventbus.EventBus;
import com.google.common.net.HostAndPort;
@ -108,6 +112,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant;
* Apr 09, 2014 2785 mpduff Throw error when not connected and the connection should exist.
* Apr 14, 2014 2903 bclement moved from session subpackage to connection, removed password from memory,
* moved listeners to own classes, reworked connect/register listeners/login order
* Apr 15, 2014 2822 bclement added pubsub owner subscriptions provider registration
*
* </pre>
*
@ -124,6 +129,19 @@ public class CollaborationConnection implements IEventPublisher {
PacketConstants.COLLAB_XMLNS, new SessionPayloadProvider());
pm.addIQProvider(PacketConstants.QUERY_ELEMENT_NAME,
AuthInfo.AUTH_QUERY_XMLNS, new AuthInfoProvider());
/*
* smack doesn't support some of the OWNER operations such as getting
* all subscriptions on a node. PubSubOperations creates the request
* objects for these operations, but the response needs to be parsed.
* Here we register the existing smack parsers using the OWNER
* namespace.
*/
pm.addExtensionProvider(
PubSubElementType.SUBSCRIPTION.getElementName(),
PubSubNamespace.OWNER.getXmlns(), new SubscriptionProvider());
pm.addExtensionProvider(
PubSubElementType.SUBSCRIPTIONS.getElementName(),
PubSubNamespace.OWNER.getXmlns(), new SubscriptionsProvider());
}
private static final transient IUFStatusHandler statusHandler = UFStatus

View file

@ -19,9 +19,18 @@
**/
package com.raytheon.uf.viz.collaboration.comm.provider.session;
import java.util.List;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.pubsub.Node;
import org.jivesoftware.smackx.pubsub.NodeExtension;
import org.jivesoftware.smackx.pubsub.PubSubElementType;
import org.jivesoftware.smackx.pubsub.Subscription;
import org.jivesoftware.smackx.pubsub.SubscriptionsExtension;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
@ -38,7 +47,8 @@ import com.raytheon.uf.common.xmpp.ext.ChangeAffiliationExtension;
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 18, 2014 2751 bclement Initial creation
* Feb 18, 2014 2751 bclement Initial creation
* Apr 15, 2014 2822 bclement added getAllSubscriptions()
*
* </pre>
*
@ -47,11 +57,14 @@ import com.raytheon.uf.common.xmpp.ext.ChangeAffiliationExtension;
*/
public class PubSubOperations {
public static final String PUBSUB_SUBDOMAIN_PREFIX = "pubsub.";
private PubSubOperations() {
}
/**
* Send packet to change affiliation of user on a pubsub topic node
* Send packet to change affiliation of user on a pubsub topic node. Calling
* client must be owner of node.
*
* @param conn
* @param affiliation
@ -59,12 +72,56 @@ public class PubSubOperations {
*/
public static void sendAffiliationPacket(XMPPConnection conn,
ChangeAffiliationExtension affiliation) throws XMPPException {
PubSub packet = new PubSub();
packet.setType(IQ.Type.SET);
packet.setTo("pubsub." + conn.getServiceName());
packet.setPubSubNamespace(PubSubNamespace.OWNER);
packet.addExtension(affiliation);
PubSub packet = createOwnerPacket(conn, affiliation, IQ.Type.SET);
SyncPacketSend.getReply(conn, packet);
}
/**
* List all subscriptions on node. Calling client must be owner of node.
*
* @param conn
* @param n
* @return
* @throws XMPPException
*/
public static List<Subscription> getAllSubscriptions(XMPPConnection conn,
Node n) throws XMPPException {
PubSubElementType type = PubSubElementType.SUBSCRIPTIONS;
/*
* we need to use the OWNER namespace when we make the request, but we
* reuse the provider (parser) for the default namespace for the return.
* Use the default namespace to get the extension object from the packet
*/
String namespace = type.getNamespace().getXmlns();
NodeExtension ext = new NodeExtension(type, n.getId());
PubSub packet = createOwnerPacket(conn, ext, Type.GET);
Packet reply = SyncPacketSend.getReply(conn, packet);
SubscriptionsExtension resp = (SubscriptionsExtension) reply
.getExtension(type.getElementName(), namespace);
if (resp == null){
throw new XMPPException(
"Subscriptions response missing content for topic: "
+ n.getId());
}
return resp.getSubscriptions();
}
/**
* Create pubsub packet object with owner namespace
*
* @param conn
* @param ext
* @param type
* @return
*/
private static PubSub createOwnerPacket(XMPPConnection conn,
NodeExtension ext, Type type) {
PubSub packet = new PubSub();
packet.setType(type);
packet.setTo(PUBSUB_SUBDOMAIN_PREFIX + conn.getServiceName());
packet.setPubSubNamespace(PubSubNamespace.OWNER);
packet.addExtension(ext);
return packet;
}
}

View file

@ -23,7 +23,9 @@ import java.net.URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.http.client.methods.HttpDelete;
import org.jivesoftware.smack.XMPPConnection;
@ -67,6 +69,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.account.ClientAuthManager
import com.raytheon.uf.viz.collaboration.comm.provider.connection.CollaborationConnection;
import com.raytheon.uf.viz.collaboration.comm.provider.connection.PeerToPeerCommHelper;
import com.raytheon.uf.viz.collaboration.comm.provider.event.LeaderChangeEvent;
import com.raytheon.uf.viz.collaboration.comm.provider.user.IDConverter;
import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId;
import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant;
@ -95,6 +98,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant;
* Mar 07, 2014 2848 bclement moved pubsub close logic to closePubSub()
* ensure that subscription is setup before joining room
* Mar 31, 2014 2899 mpduff Improve error messages.
* Apr 15, 2014 2822 bclement added check for other participants being subscribed to topic
*
* </pre>
*
@ -116,6 +120,8 @@ public class SharedDisplaySession extends VenueSession implements
private LeafNode topic;
private final Map<UserId, Boolean> topicSubscribers = new ConcurrentHashMap<UserId, Boolean>();
private XMPPConnection conn;
private boolean closed = false;
@ -624,6 +630,36 @@ public class SharedDisplaySession extends VenueSession implements
PubSubOperations.sendAffiliationPacket(conn, affiliation);
}
/**
* @param user
* @return true if user has a subscription to the session topic
* @throws XMPPException
*/
private boolean isSubscribedToTopic(UserId user)
throws XMPPException {
Boolean rval = topicSubscribers.get(user);
if (rval == null) {
synchronized (topicSubscribers) {
List<Subscription> subs = PubSubOperations.getAllSubscriptions(
conn, topic);
for (Subscription sub : subs) {
topicSubscribers.put(IDConverter.convertFrom(sub.getJid()),
true);
}
}
rval = topicSubscribers.get(user);
if (rval == null) {
/*
* userid object hash includes resource, cache as a client that
* doesn't use topic
*/
topicSubscribers.put(user, false);
rval = false;
}
}
return rval;
}
@Override
public void changeLeader(VenueParticipant newLeader)
throws CollaborationException {
@ -645,6 +681,15 @@ public class SharedDisplaySession extends VenueSession implements
boolean othersNotified = false;
String revokeTarget = null;
try {
/*
* make sure that the new leader is not just in the room, but also
* subscribed to the pubsub topic
*/
if (!isSubscribedToTopic(actualId)) {
throw new CollaborationException(
"Unable to grant ownership because new leader is not subscribed to session topic");
}
// was formerly the data provider, so hand off pubsub ownership
grantTopicOwnership(newLeaderId);
topicOwnershipGranted = true;
@ -670,6 +715,8 @@ public class SharedDisplaySession extends VenueSession implements
// 'member' instead of just down to 'admin'
revokeTarget = "room";
muc.revokeAdmin(account.getNormalizedId());
// clear cache of topic subscribers
topicSubscribers.clear();
} catch (XMPPException e) {
if (!othersNotified) {
// transaction, attempt to roll back the ownership changes
@ -713,4 +760,27 @@ public class SharedDisplaySession extends VenueSession implements
return closed;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession
* #isSharedDisplayClient
* (com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant)
*/
@Override
public boolean isSharedDisplayClient(VenueParticipant participant) {
UserId actualId = getVenue().getParticipantUserid(participant);
boolean rval = false;
if (actualId != null) {
try {
rval = isSubscribedToTopic(actualId);
} catch (XMPPException e) {
log.error("Error checking if user is a shared display client",
e);
}
}
return rval;
}
}

View file

@ -100,6 +100,7 @@ import com.raytheon.viz.ui.input.EditableManager;
* Mar 06, 2014 2848 bclement moved colormanager update code to session container
* Mar 11, 2014 2865 lvenable Added null checks in threads
* Mar 18, 2014 2895 njensen Fix lockAction enable/disable logic
* Apr 15, 2014 2822 bclement only allow transfer leader if participant is using shared display
*
* </pre>
*
@ -539,7 +540,8 @@ public class CollaborationSessionView extends SessionView implements
.getSelection();
VenueParticipant entry = (VenueParticipant) selection
.getFirstElement();
if (!entry.isSameUser(session.getUserID())) {
if (!entry.isSameUser(session.getUserID())
&& session.isSharedDisplayClient(entry)) {
manager.add(leaderChangeAction);
}
}