diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/META-INF/MANIFEST.MF b/cave/com.raytheon.uf.viz.collaboration.comm/META-INF/MANIFEST.MF index 0e3a079873..776585c02e 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/META-INF/MANIFEST.MF +++ b/cave/com.raytheon.uf.viz.collaboration.comm/META-INF/MANIFEST.MF @@ -19,7 +19,9 @@ Require-Bundle: org.eclipse.core.runtime, com.raytheon.uf.common.localization, com.raytheon.uf.viz.core, com.raytheon.uf.common.time, - com.raytheon.uf.common.util + com.raytheon.uf.common.util, + com.raytheon.uf.common.xmpp, + com.raytheon.uf.common.http Export-Package: com.raytheon.uf.viz.collaboration.comm, com.raytheon.uf.viz.collaboration.comm.compression, com.raytheon.uf.viz.collaboration.comm.identity, @@ -28,6 +30,7 @@ Export-Package: com.raytheon.uf.viz.collaboration.comm, com.raytheon.uf.viz.collaboration.comm.identity.invite, com.raytheon.uf.viz.collaboration.comm.identity.roster, com.raytheon.uf.viz.collaboration.comm.identity.user, + com.raytheon.uf.viz.collaboration.comm.packet, com.raytheon.uf.viz.collaboration.comm.provider, com.raytheon.uf.viz.collaboration.comm.provider.event, com.raytheon.uf.viz.collaboration.comm.provider.info, diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayload.java similarity index 76% rename from cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java rename to cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayload.java index c2d824320f..d25028043d 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayload.java @@ -17,15 +17,22 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.uf.viz.collaboration.comm.provider; +package com.raytheon.uf.viz.collaboration.comm.packet; + +import java.util.Arrays; -import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.util.Base64; import com.raytheon.uf.common.serialization.SerializationUtil; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.xmpp.PacketConstants; +import com.raytheon.uf.common.xmpp.XmlBuilder; +import com.raytheon.uf.common.xmpp.XmlBuilder.Pair; +import com.raytheon.uf.common.xmpp.ext.BaseExtension; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; +import com.raytheon.uf.viz.collaboration.comm.provider.CollaborationXmlManager; +import com.raytheon.uf.viz.collaboration.comm.provider.SerializationMode; /** * XMPP packet extension for collaboration session data @@ -37,13 +44,14 @@ import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Dec 11, 2013 2562 bclement Initial creation + * Feb 27, 2013 2756 bclement extends BaseExtension * * * * @author bclement * @version 1.0 */ -public class SessionPayload implements PacketExtension { +public class SessionPayload extends BaseExtension { private static final IUFStatusHandler log = UFStatus .getHandler(SessionPayload.class); @@ -52,8 +60,6 @@ public class SessionPayload implements PacketExtension { Config, Command, Invitation; }; - public static final String XMLNS = "urn:uf:viz:collaboration"; - public static final String ELEMENT_NAME = "SessionData"; public static final String TYPE_ATTRIBUTE = "payloadtype"; @@ -75,6 +81,7 @@ public class SessionPayload implements PacketExtension { * message object */ public SessionPayload(PayloadType type, SerializationMode mode, Object data) { + super(ELEMENT_NAME, PacketConstants.COLLAB_XMLNS); this.payloadType = type; this.mode = mode; this.data = data; @@ -106,18 +113,16 @@ public class SessionPayload implements PacketExtension { */ public static String createXml(PayloadType type, SerializationMode mode, Object data) throws CollaborationException { - StringBuilder builder = new StringBuilder(); - builder = new StringBuilder(); - builder.append("<").append(ELEMENT_NAME).append(" "); - appendAttribute(builder, "xmlns", XMLNS); - appendAttribute(builder, TYPE_ATTRIBUTE, type.name()); - appendAttribute(builder, ENCODING_ATTRIBUTE, mode.name()); - builder.append(">"); + XmlBuilder builder = new XmlBuilder(); + Pair typeAttr = new Pair(TYPE_ATTRIBUTE, type.name()); + Pair encAttr = new Pair(ENCODING_ATTRIBUTE, mode.name()); + builder.startTag(ELEMENT_NAME, PacketConstants.COLLAB_XMLNS, + Arrays.asList(typeAttr, encAttr)); switch (mode) { case THRIFT: try { byte[] arr = SerializationUtil.transformToThrift(data); - builder.append(Base64.encodeBytes(arr)); + builder.appendText(Base64.encodeBytes(arr)); } catch (Exception e) { throw new CollaborationException( "[THRIFT] Could not serialize object", e); @@ -128,14 +133,14 @@ public class SessionPayload implements PacketExtension { CollaborationXmlManager jaxb = CollaborationXmlManager .getInstance(); String xml = jaxb.marshalToFragment(data); - builder.append(xml); + builder.appendText(xml); } catch (Exception je) { throw new CollaborationException( "[JAXB] Could not serialize object", je); } break; case STRING: - builder.append(data.toString()); + builder.appendText(data.toString()); break; case NONE: throw new CollaborationException("Serialization of " @@ -143,23 +148,10 @@ public class SessionPayload implements PacketExtension { case ISNULL: break; } - builder.append(""); + builder.endTag(ELEMENT_NAME); return builder.toString(); } - /** - * Format XML attribute name/value pair and append to string builder - * - * @param sb - * @param name - * @param value - */ - private static void appendAttribute(StringBuilder sb, String name, - String value) { - sb.append(name).append("='").append(value).append("' "); - } - - /** * @return the payloadType */ @@ -181,26 +173,6 @@ public class SessionPayload implements PacketExtension { return data; } - /* - * (non-Javadoc) - * - * @see org.jivesoftware.smack.packet.PacketExtension#getElementName() - */ - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - /* - * (non-Javadoc) - * - * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace() - */ - @Override - public String getNamespace() { - return XMLNS; - } - /* * (non-Javadoc) * @@ -219,5 +191,4 @@ public class SessionPayload implements PacketExtension { } } - } diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayloadProvider.java similarity index 54% rename from cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java rename to cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayloadProvider.java index 2cb00661ab..d1f204e5b3 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/packet/SessionPayloadProvider.java @@ -17,7 +17,7 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.uf.viz.collaboration.comm.provider; +package com.raytheon.uf.viz.collaboration.comm.packet; import java.io.IOException; @@ -34,8 +34,11 @@ import com.raytheon.uf.common.serialization.SerializationException; import com.raytheon.uf.common.serialization.SerializationUtil; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.xmpp.BaseProvider; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.provider.CollaborationXmlManager; +import com.raytheon.uf.viz.collaboration.comm.provider.SerializationMode; /** * XMPP packet extension parsing provider for collaboration session data @@ -48,6 +51,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadTyp * ------------ ---------- ----------- -------------------------- * Dec 16, 2013 2562 bclement Initial creation * Feb 12, 2014 2793 bclement improved error handling + * Feb 27, 2013 2756 bclement extends BaseProvider * * * @@ -55,98 +59,86 @@ import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadTyp * @version 1.0 */ -public class SessionPayloadProvider implements PacketExtensionProvider { +public class SessionPayloadProvider extends BaseProvider + implements PacketExtensionProvider { private static final IUFStatusHandler log = UFStatus .getHandler(SessionPayloadProvider.class); + public SessionPayloadProvider() { + super(SessionPayload.ELEMENT_NAME); + } - /* (non-Javadoc) - * @see org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension(org.xmlpull.v1.XmlPullParser) + /* + * (non-Javadoc) + * + * @see + * org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension + * (org.xmlpull.v1.XmlPullParser) */ @Override public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + return parse(parser); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.common.xmpp.BaseProvider#parseInternal(org.xmlpull.v1 + * .XmlPullParser) + */ + @Override + protected SessionPayload parseInternal(XmlPullParser parser) + throws XmlPullParserException, IOException { try { - return parseInternal(parser); + String typeString = parser.getAttributeValue(null, + SessionPayload.TYPE_ATTRIBUTE); + String modeString = parser.getAttributeValue(null, + SessionPayload.ENCODING_ATTRIBUTE); + checkAttribute(SessionPayload.TYPE_ATTRIBUTE, typeString); + checkAttribute(SessionPayload.ENCODING_ATTRIBUTE, modeString); + + SerializationMode mode; + try { + mode = SerializationMode.valueOf(modeString); + } catch (IllegalArgumentException e) { + throw new CollaborationException( + "Unsupported payload encoding: " + modeString, e); + } + PayloadType t; + try { + t = PayloadType.valueOf(typeString); + } catch (IllegalArgumentException e) { + throw new CollaborationException( + "Unsupported IQ payload type: " + typeString, e); + } + Object data; + switch (mode) { + case THRIFT: + String text = getText(parser); + data = unmarshalThrift(text); + break; + case JAXB: + data = unmarshalJaxb(parser); + break; + case STRING: + data = getText(parser); + break; + default: + throw new CollaborationException("Could not deserialize object"); + } + return new SessionPayload(t, mode, data); } catch (CollaborationException e) { - // collaboration exceptions are only thrown for problems with our own format + // collaboration exceptions are only thrown for problems with our + // own format log.error("Unable to parse collaboration packet", e); return new SessionPayload(PayloadType.Command, SerializationMode.ISNULL, null); - } finally { - // ensure that we are at the end of the packet so we don't corrupt - // stream - while (!atEndOfPacket(parser)) { - parser.next(); - } } } - /** - * @param parser - * @return true if parser is at the end tag of the payload packet - * @throws XmlPullParserException - */ - private static boolean atEndOfPacket(XmlPullParser parser) - throws XmlPullParserException { - return parser.getEventType() == XmlPullParser.END_TAG - && parser.getName().equals(SessionPayload.ELEMENT_NAME); - } - - /** - * Parse contents of packet extension from XMPP stream. - * - * @param parser - * @return - * @throws CollaborationException - * when error occurs with collaboration format - * @throws XmlPullParserException - * when error occurs with XMPP stream - * @throws IOException - * when error occurs with XMPP stream - */ - private static PacketExtension parseInternal(XmlPullParser parser) - throws CollaborationException, XmlPullParserException, IOException { - String typeString = parser.getAttributeValue(null, - SessionPayload.TYPE_ATTRIBUTE); - String modeString = parser.getAttributeValue(null, - SessionPayload.ENCODING_ATTRIBUTE); - checkAttribute(SessionPayload.TYPE_ATTRIBUTE, typeString); - checkAttribute(SessionPayload.ENCODING_ATTRIBUTE, modeString); - - SerializationMode mode; - try { - mode = SerializationMode.valueOf(modeString); - } catch (IllegalArgumentException e) { - throw new CollaborationException("Unsupported payload encoding: " - + modeString, e); - } - PayloadType t; - try { - t = PayloadType.valueOf(typeString); - } catch (IllegalArgumentException e) { - throw new CollaborationException("Unsupported IQ payload type: " - + typeString, e); - } - Object data; - switch (mode) { - case THRIFT: - String text = getText(parser); - data = unmarshalThrift(text); - break; - case JAXB: - data = unmarshalJaxb(parser); - break; - case STRING: - data = getText(parser); - break; - default: - throw new CollaborationException("Could not deserialize object"); - } - return new SessionPayload(t, mode, data); - } - /** * Unmarshal base64 encoded thrift data * @@ -185,31 +177,6 @@ public class SessionPayloadProvider implements PacketExtensionProvider { return manager.unmarshalFromXPP(parser); } - /** - * Get any text elements under current tag - * - * @param parser - * @return - * @throws XmlPullParserException - * @throws IOException - */ - private static String getText(XmlPullParser parser) - throws XmlPullParserException, IOException { - boolean done = false; - StringBuilder payloadText = new StringBuilder(); - while (!done) { - if (atEndOfPacket(parser)) { - done = true; - continue; - } else if (parser.getEventType() == XmlPullParser.TEXT) { - payloadText.append(parser.getText()); - } - parser.next(); - } - return payloadText.toString(); - } - - /** * Assert that value is not null or empty * diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java index fcb5ccaf7c..ece70e17fa 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java @@ -43,6 +43,7 @@ import com.raytheon.uf.common.serialization.jaxb.JaxbDummyObject; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.common.xmpp.PullParserJaxbAdapter; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.core.procedures.ProcedureXmlManager; import com.raytheon.uf.viz.core.reflect.SubClassLocator; diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ClientAuthManager.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ClientAuthManager.java new file mode 100644 index 0000000000..f529f675ab --- /dev/null +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ClientAuthManager.java @@ -0,0 +1,223 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.viz.collaboration.comm.provider.session; + +import java.net.URI; +import java.security.GeneralSecurityException; +import java.security.SignatureException; + +import org.apache.http.message.AbstractHttpMessage; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.util.SyncPacketSend; + +import com.raytheon.uf.common.http.auth.ClientSignatureAuth; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.xmpp.BaseProvider; +import com.raytheon.uf.common.xmpp.iq.AuthInfo; +import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; + +/** + * Manages collaboration client's authentication credentials for HTTP data + * server which are stored on the XMPP server + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 24, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class ClientAuthManager { + + private static final IUFStatusHandler log = UFStatus + .getHandler(ClientAuthManager.class); + + private final XMPPConnection conn; + + public static enum State { + PENDING, ENABLED, DISABLED, ERROR + } + + private State state = State.PENDING; + + private final Object stateMonitor = new Object(); + + private ClientSignatureAuth sigAuth; + + /** + * @param conn + * @throws CollaborationException + */ + public ClientAuthManager(XMPPConnection conn) throws CollaborationException { + this.conn = conn; + if (isEnabledOnServer(conn)) { + createAndRegisterAuth(); + } else { + this.sigAuth = null; + this.state = State.DISABLED; + } + } + + /** + * @param conn + * @return true if server supports collaboration auth feature + * @throws CollaborationException + */ + public static boolean isEnabledOnServer(XMPPConnection conn) + throws CollaborationException { + try { + return BaseProvider.serverSupportsFeature(conn, + AuthInfo.AUTH_QUERY_XMLNS); + } catch (XMPPException e) { + throw new CollaborationException( + "Unable to query server for supported features", e); + } + } + + /** + * Create a new key pair and register key with server + * + * @return + * @throws CollaborationException + */ + private void createAndRegisterAuth() { + Job job = new Job("Creating and registering collaboration credentials") { + @Override + protected IStatus run(IProgressMonitor monitor) { + State authState = State.ERROR; + try { + ClientSignatureAuth auth = new ClientSignatureAuth(); + String encodedKey = auth.getEncodedPublicKey(); + AuthInfo packet = AuthInfo.createSetPacket(encodedKey, + auth.getKeyAlgorithm()); + SyncPacketSend.getReply(conn, packet); + ClientAuthManager.this.sigAuth = auth; + authState = State.ENABLED; + } catch (GeneralSecurityException e) { + log.error("Problem creating public key auth", e); + } catch (XMPPException e) { + log.error("Problem registering public key", e); + } finally { + synchronized (stateMonitor) { + ClientAuthManager.this.state = authState; + stateMonitor.notifyAll(); + } + } + return Status.OK_STATUS; + } + }; + job.setPriority(Job.SHORT); + job.setSystem(true); + job.schedule(); + } + + /** + * NOTE: this method will block if auth registration has not yet completed + * + * @return the sigAuth, null if {@link ClientAuthManager#getState()} is + * {@link State#DISABLED} + */ + public ClientSignatureAuth getSigAuth() { + if (state.equals(State.PENDING)) { + waitForInit(); + } + return sigAuth; + } + + /** + * Add an Auth header to the message using the credential and a signature + * generated from the URI. + * + * @param msg + * @param credential + * @param uri + * @throws CollaborationException + */ + public void signRequest(AbstractHttpMessage msg, String credential, URI uri) + throws CollaborationException { + signRequest(msg, credential, uri, null); + } + + /** + * Add an Auth header to the message using the credential and a signature + * generated from the URI and body. + * + * @param msg + * @param credential + * @param uri + * @param body + * @throws CollaborationException + */ + public void signRequest(AbstractHttpMessage msg, String credential, + URI uri, byte[] body) throws CollaborationException { + ClientSignatureAuth sigAuth = getSigAuth(); + if (state.equals(State.ENABLED)) { + String sig; + try { + if (body != null) { + sig = sigAuth.sign(uri, body); + } else { + sig = sigAuth.sign(uri); + } + } catch (SignatureException e) { + throw new CollaborationException( + "Problem signing HTTP message", e); + } + String headerValue = ClientSignatureAuth.formatAuthHeader( + credential, sig); + msg.addHeader(ClientSignatureAuth.HTTP_AUTH_HEADER, headerValue); + } + } + + /** + * waits thread until state is no longer pending auth registration + */ + private void waitForInit() { + synchronized (stateMonitor) { + if (state.equals(State.PENDING)) { + try { + stateMonitor.wait(); + } catch (InterruptedException e) { + log.error("Interrupted waiting for auth", e); + } + } + } + } + + /** + * @return the state of the auth registration + */ + public State getState() { + return state; + } + +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java index f92bbd4987..f6ecb9198b 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java @@ -48,6 +48,9 @@ import com.google.common.eventbus.EventBus; import com.google.common.net.HostAndPort; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.xmpp.PacketConstants; +import com.raytheon.uf.common.xmpp.iq.AuthInfo; +import com.raytheon.uf.common.xmpp.iq.AuthInfoProvider; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.collaboration.comm.identity.IAccountManager; import com.raytheon.uf.viz.collaboration.comm.identity.ISession; @@ -59,9 +62,9 @@ import com.raytheon.uf.viz.collaboration.comm.identity.event.IVenueInvitationEve import com.raytheon.uf.viz.collaboration.comm.identity.event.RosterChangeType; import com.raytheon.uf.viz.collaboration.comm.identity.invite.SharedDisplayVenueInvite; import com.raytheon.uf.viz.collaboration.comm.identity.invite.VenueInvite; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayloadProvider; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayloadProvider; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.RosterChangeEvent; import com.raytheon.uf.viz.collaboration.comm.provider.event.ServerDisconnectEvent; @@ -114,6 +117,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant; * Feb 13, 2014 2751 bclement better types for venueid and invitor * Feb 18, 2014 2793 bclement improved disconnection notification and handling * Feb 24, 2014 2632 mpduff Fix roster change type for presence change. + * Feb 28, 2014 2756 bclement added authManager * * * @@ -125,9 +129,11 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant; public class CollaborationConnection implements IEventPublisher { static { - ProviderManager.getInstance().addExtensionProvider( - SessionPayload.ELEMENT_NAME, SessionPayload.XMLNS, - new SessionPayloadProvider()); + ProviderManager pm = ProviderManager.getInstance(); + pm.addExtensionProvider(SessionPayload.ELEMENT_NAME, + PacketConstants.COLLAB_XMLNS, new SessionPayloadProvider()); + pm.addIQProvider(PacketConstants.QUERY_ELEMENT_NAME, + AuthInfo.AUTH_QUERY_XMLNS, new AuthInfoProvider()); } private static final transient IUFStatusHandler statusHandler = UFStatus @@ -155,6 +161,8 @@ public class CollaborationConnection implements IEventPublisher { private XMPPConnection connection; + private final ClientAuthManager authManager; + public static boolean COMPRESS = true; static { @@ -209,6 +217,8 @@ public class CollaborationConnection implements IEventPublisher { setupP2PComm(); getPeerToPeerSession(); + authManager = new ClientAuthManager(connection); + userPresence = initialPresence; if (accountManager != null && initialPresence != null) { accountManager.sendPresence(initialPresence); @@ -632,7 +642,7 @@ public class CollaborationConnection implements IEventPublisher { if (message != null) { SessionPayload payload = (SessionPayload) message - .getExtension(SessionPayload.XMLNS); + .getExtension(PacketConstants.COLLAB_XMLNS); if (payload != null) { handleCollabInvite(venueId, invitor, payload); @@ -770,4 +780,11 @@ public class CollaborationConnection implements IEventPublisher { } } + /** + * @return the authManager + */ + public ClientAuthManager getAuthManager() { + return authManager; + } + } diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java index 4ebba1f156..e3291e8cce 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java @@ -29,6 +29,7 @@ import org.jivesoftware.smack.packet.Packet; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.common.xmpp.PacketConstants; import com.raytheon.uf.viz.collaboration.comm.Activator; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.collaboration.comm.identity.ISession; @@ -36,7 +37,7 @@ import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpXmppMessage; import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpdCollaborationConfigurationEvent; import com.raytheon.uf.viz.collaboration.comm.identity.event.ITextMessageEvent; import com.raytheon.uf.viz.collaboration.comm.identity.user.IUser; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload; import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.ChatMessageEvent; @@ -61,6 +62,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; * Feb 13, 2014 2751 bclement changed IQualifiedID objects to IUser * Feb 17, 2014 2756 bclement null check for message from field * moved url validation from regex to java utility + * Feb 24, 2014 2756 bclement moved xmpp objects to new packages * * * @@ -134,7 +136,7 @@ public class PeerToPeerCommHelper implements PacketListener { } } else { SessionPayload payload = (SessionPayload) msg - .getExtension(SessionPayload.XMLNS); + .getExtension(PacketConstants.COLLAB_XMLNS); if (payload != null) { switch (payload.getPayloadType()) { case Command: diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PubSubOperations.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PubSubOperations.java index 1fd30e4f24..ddc9539c45 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PubSubOperations.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PubSubOperations.java @@ -26,6 +26,8 @@ import org.jivesoftware.smackx.pubsub.packet.PubSub; import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend; +import com.raytheon.uf.common.xmpp.ext.ChangeAffiliationExtension; + /** * PubSub operations not implemented by the Smack PubSub manager. This includes * operations to modify ownership of a topic node. diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java index 63957b1e03..40ab581364 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java @@ -19,11 +19,13 @@ **/ package com.raytheon.uf.viz.collaboration.comm.provider.session; +import java.net.URI; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.http.client.methods.HttpDelete; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; @@ -49,14 +51,17 @@ import org.jivesoftware.smackx.pubsub.listener.ItemEventListener; import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend; import com.google.common.eventbus.EventBus; +import com.raytheon.uf.common.comm.HttpClient; +import com.raytheon.uf.common.comm.HttpClient.HttpClientResponse; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.xmpp.ext.ChangeAffiliationExtension; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.collaboration.comm.identity.ISession; import com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession; import com.raytheon.uf.viz.collaboration.comm.identity.user.SharedDisplayRole; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload.PayloadType; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.LeaderChangeEvent; import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; @@ -82,6 +87,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant; * Feb 18, 2014 2751 bclement implemented room and pubsub ownership transfer * Feb 19, 2014 2751 bclement added isClosed() * Feb 24, 2014 2751 bclement added validation for change leader event + * Feb 28, 2014 2756 bclement added cleanUpHttpStorage() * * * @@ -156,6 +162,7 @@ public class SharedDisplaySession extends VenueSession implements // before cleanup? log.info("Deleting old topic: " + aff.getNodeId()); try { + cleanUpHttpStorage(aff.getNodeId()); pubsubMgr.deleteNode(aff.getNodeId()); } catch (XMPPException e) { log.error( @@ -481,6 +488,7 @@ public class SharedDisplaySession extends VenueSession implements topic.removeItemDeleteListener(this); topic.removeItemEventListener(this); if (hasRole(SharedDisplayRole.SESSION_LEADER)) { + cleanUpHttpStorage(topic.getId()); pubsubMgr.deleteNode(topic.getId()); } topic = null; @@ -494,6 +502,38 @@ public class SharedDisplaySession extends VenueSession implements } } + /** + * Delete session directory on HTTP server. This should only be called if + * this client is the owner of the topic associated with the session and + * only after the session is closed. + * + * TODO this will not be needed if the xmpp server takes care of shared + * display session lifecycle (create, transfer ownership, destroy, etc) + * + * @param sessionid + */ + private void cleanUpHttpStorage(String sessionid) { + try { + String userid = getAccount().getNormalizedId(); + String url = PeerToPeerCommHelper.getCollaborationHttpServer(); + url += sessionid + "/"; + URI uri = new URI(url); + HttpDelete delete = new HttpDelete(uri); + ClientAuthManager authManager = getConnection().getAuthManager(); + authManager.signRequest(delete, userid, uri); + HttpClientResponse response = HttpClient.getInstance() + .executeRequest(delete); + if (!response.isSuccess() && !response.isNotExists()) { + throw new CollaborationException("Session deletion failed for " + + uri + ": " + new String(response.data)); + } + } catch (Exception e) { + log.error( + "Problem cleaning up old HTTP storage objects for session: " + + sessionid, e); + } + } + /* * (non-Javadoc) * diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java index b3e4abfaf1..f2e77b9bd8 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java @@ -52,8 +52,8 @@ import com.raytheon.uf.viz.collaboration.comm.identity.event.ParticipantEventTyp import com.raytheon.uf.viz.collaboration.comm.identity.info.IVenue; import com.raytheon.uf.viz.collaboration.comm.identity.invite.VenueInvite; import com.raytheon.uf.viz.collaboration.comm.identity.user.IUser; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; -import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.packet.SessionPayload.PayloadType; import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.UserNicknameChangedEvent; diff --git a/cave/com.raytheon.uf.viz.collaboration.display/META-INF/MANIFEST.MF b/cave/com.raytheon.uf.viz.collaboration.display/META-INF/MANIFEST.MF index dfa42bf837..2c4342558a 100644 --- a/cave/com.raytheon.uf.viz.collaboration.display/META-INF/MANIFEST.MF +++ b/cave/com.raytheon.uf.viz.collaboration.display/META-INF/MANIFEST.MF @@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.core.runtime, com.raytheon.uf.common.time, com.raytheon.uf.common.geospatial, com.raytheon.uf.common.util, - com.raytheon.viz.core + com.raytheon.viz.core, + com.raytheon.uf.common.http Export-Package: com.raytheon.uf.viz.collaboration.display, com.raytheon.uf.viz.collaboration.display.data, com.raytheon.uf.viz.collaboration.display.editor, diff --git a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/storage/CollaborationObjectEventStorage.java b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/storage/CollaborationObjectEventStorage.java index 589d70bd5d..e3535f1350 100644 --- a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/storage/CollaborationObjectEventStorage.java +++ b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/storage/CollaborationObjectEventStorage.java @@ -40,6 +40,7 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; import com.raytheon.uf.common.comm.HttpClient; import com.raytheon.uf.common.comm.HttpClient.HttpClientResponse; @@ -53,6 +54,7 @@ import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.viz.collaboration.comm.compression.CompressionUtil; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession; +import com.raytheon.uf.viz.collaboration.comm.provider.session.ClientAuthManager; import com.raytheon.uf.viz.collaboration.comm.provider.session.PeerToPeerCommHelper; import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueParticipant; import com.raytheon.uf.viz.remote.graphics.Dispatcher; @@ -75,6 +77,8 @@ import com.raytheon.uf.viz.remote.graphics.events.ICreationEvent; * Feb 17, 2014 2756 bclement added xml parsing for HTTP directory listing * Feb 24, 2014 2751 bclement added separate paths for each provider under session id * Feb 25, 2014 2751 bclement fixed provider id path for webDAV + * Feb 28, 2014 2756 bclement added auth, moved response code checks to response object + * * * * @author mschenke @@ -90,16 +94,18 @@ public class CollaborationObjectEventStorage implements private static NetworkStatistics stats = com.raytheon.uf.viz.collaboration.comm.Activator .getDefault().getNetworkStats(); + private static final String SLASH = "/"; + public static IObjectEventPersistance createPersistanceObject( ISharedDisplaySession session, Dispatcher dispatcher) throws CollaborationException { CollaborationObjectEventStorage persistance = new CollaborationObjectEventStorage( session, dispatcher.getDispatcherId()); persistance.createFolder(URI.create(persistance.sessionDataURL)); - persistance.sessionDataURL += persistance.displayId + "/"; - persistance.createFolder(URI.create(persistance.sessionDataURL)); appendProviderIdToPath(persistance); persistance.createFolder(URI.create(persistance.sessionDataURL)); + persistance.sessionDataURL += persistance.displayId + SLASH; + persistance.createFolder(URI.create(persistance.sessionDataURL)); return persistance; } @@ -112,8 +118,8 @@ public class CollaborationObjectEventStorage implements private static void appendProviderIdToPath( CollaborationObjectEventStorage persistance) { try { - persistance.sessionDataURL += URLEncoder.encode(persistance.providerid, - "UTF-8") + "/"; + persistance.sessionDataURL += URLEncoder.encode( + persistance.providerid, HTTP.UTF_8) + SLASH; } catch (UnsupportedEncodingException e) { log.warn("URL encoding failed, retrying with default encoding: " + e.getLocalizedMessage()); @@ -127,8 +133,8 @@ public class CollaborationObjectEventStorage implements ISharedDisplaySession session, int displayId) { CollaborationObjectEventStorage persistance = new CollaborationObjectEventStorage( session, displayId); - persistance.sessionDataURL += persistance.displayId + "/"; appendProviderIdToPath(persistance); + persistance.sessionDataURL += persistance.displayId + SLASH; return persistance; } @@ -148,14 +154,17 @@ public class CollaborationObjectEventStorage implements private final String providerid; + private final ClientAuthManager authManager; + private CollaborationObjectEventStorage(ISharedDisplaySession session, int displayId) { this.displayId = displayId; this.client = HttpClient.getInstance(); + this.authManager = session.getConnection().getAuthManager(); VenueParticipant dataProvider = session.getCurrentDataProvider(); this.providerid = dataProvider.getUserid().getNormalizedId(); this.sessionDataURL = PeerToPeerCommHelper.getCollaborationHttpServer(); - this.sessionDataURL += session.getSessionId() + "/"; + this.sessionDataURL += session.getSessionId() + SLASH; } /* @@ -187,10 +196,9 @@ public class CollaborationObjectEventStorage implements try { CollaborationHttpPersistedEvent wrapped = new CollaborationHttpPersistedEvent(); - String objectPath = event.getObjectId() + "/" + String objectPath = event.getObjectId() + SLASH + event.getClass().getName() + ".obj"; String eventObjectURL = sessionDataURL + objectPath; - HttpPut put = new HttpPut(eventObjectURL); CollaborationHttpPersistedObject persistObject = new CollaborationHttpPersistedObject(); persistObject.persistTime = System.currentTimeMillis(); @@ -198,9 +206,9 @@ public class CollaborationObjectEventStorage implements byte[] toPersist = CompressionUtil.compress(SerializationUtil .transformToThrift(persistObject)); stats.log(event.getClass().getSimpleName(), toPersist.length, 0); - put.setEntity(new ByteArrayEntity(toPersist)); + HttpPut put = createPut(eventObjectURL, toPersist); HttpClientResponse response = executeRequest(put); - if (isSuccess(response.code) == false) { + if (response.isSuccess() == false) { throw new CollaborationException( "Error uploading event object (" + event.getObjectId() + ") to server @ " + eventObjectURL + " : " @@ -216,6 +224,35 @@ public class CollaborationObjectEventStorage implements } } + /** + * Create http put method object. Adds auth header if needed. + * + * @param url + * @param body + * @return + * @throws CollaborationException + */ + private HttpPut createPut(String url, byte[] body) throws CollaborationException{ + URI uri = URI.create(url); + HttpPut put = new HttpPut(uri); + authManager.signRequest(put, providerid, uri, body); + put.setEntity(new ByteArrayEntity(body)); + return put; + } + + /** + * Create http delete method object. Adds auth header if needed. + * + * @param uri + * @return + * @throws CollaborationException + */ + private HttpDelete createDelete(URI uri) throws CollaborationException { + HttpDelete delete = new HttpDelete(uri); + authManager.signRequest(delete, providerid, uri); + return delete; + } + /* * (non-Javadoc) * @@ -273,7 +310,7 @@ public class CollaborationObjectEventStorage implements String objectPath = event.getResourcePath(); HttpGet get = new HttpGet(sessionDataURL + objectPath); HttpClientResponse response = executeRequest(get); - if (isSuccess(response.code)) { + if (response.isSuccess()) { try { CollaborationHttpPersistedObject dataObject = SerializationUtil .transformFromThrift( @@ -286,7 +323,7 @@ public class CollaborationObjectEventStorage implements } catch (SerializationException e) { throw new CollaborationException(e); } - } else if (isNotExists(response.code)) { + } else if (response.isNotExists()) { // Object was deleted return null; } else { @@ -310,8 +347,8 @@ public class CollaborationObjectEventStorage implements get.addHeader(new BasicHeader(ACCEPTS_HEADER, XML_CONTENT_TYPE + "," + HTML_CONTENT_TYPE)); HttpClientResponse response = executeRequest(get); - if (isSuccess(response.code) == false) { - if (isNotExists(response.code)) { + if (response.isSuccess() == false) { + if (response.isNotExists()) { return new AbstractDispatchingObjectEvent[0]; } throw new CollaborationException("Error retrieving object (" @@ -475,7 +512,7 @@ public class CollaborationObjectEventStorage implements }; mkcol.setURI(folderPath); HttpClientResponse rsp = executeRequest(mkcol); - if (isSuccess(rsp.code) == false && isDirExists(rsp.code) == false) { + if (rsp.isSuccess() == false && isDirExists(rsp.code) == false) { throw new CollaborationException("Folder creation failed for " + folderPath + ": " + new String(rsp.data)); } @@ -488,9 +525,9 @@ public class CollaborationObjectEventStorage implements * @throws CollaborationException */ private void deleteResource(URI uri) throws CollaborationException { - HttpClientResponse rsp = executeRequest(new HttpDelete(uri)); + HttpClientResponse rsp = executeRequest(createDelete(uri)); // If request was success or resource doesn't exist, we are good - if (isSuccess(rsp.code) == false && isNotExists(rsp.code) == false) { + if (rsp.isSuccess() == false && rsp.isNotExists() == false) { throw new CollaborationException("Folder creation failed for " + uri + ": " + new String(rsp.data)); } @@ -512,22 +549,6 @@ public class CollaborationObjectEventStorage implements } } - /** - * @param code - * @return true if code is a 200 level return code - */ - private boolean isSuccess(int code) { - return code >= 200 && code < 300; - } - - /** - * @param code - * @return true if resource does not exist on server - */ - private boolean isNotExists(int code) { - return code == 404 || code == 410; - } - /** * @deprecated this error is related to MKCOL. MKCOL is only required for * DAV (pre 14.3) diff --git a/cave/com.raytheon.uf.viz.collaboration.feature/feature.xml b/cave/com.raytheon.uf.viz.collaboration.feature/feature.xml index c16fe0ed5c..33f77dbca1 100644 --- a/cave/com.raytheon.uf.viz.collaboration.feature/feature.xml +++ b/cave/com.raytheon.uf.viz.collaboration.feature/feature.xml @@ -77,8 +77,7 @@ id="org.jivesoftware.smack" download-size="0" install-size="0" - version="0.0.0" - unpack="true"/> + version="0.0.0"/> + + + + diff --git a/edexOsgi/com.raytheon.uf.common.comm/src/com/raytheon/uf/common/comm/HttpClient.java b/edexOsgi/com.raytheon.uf.common.comm/src/com/raytheon/uf/common/comm/HttpClient.java index 09aec7cddf..749c48426f 100644 --- a/edexOsgi/com.raytheon.uf.common.comm/src/com/raytheon/uf/common/comm/HttpClient.java +++ b/edexOsgi/com.raytheon.uf.common.comm/src/com/raytheon/uf/common/comm/HttpClient.java @@ -107,6 +107,7 @@ import com.raytheon.uf.common.util.ByteArrayOutputStreamPool.ByteArrayOutputStre * Feb 04, 2014 2704 njensen Better error message with bad address * Https authentication failures notify handler * Feb 17, 2014 2756 bclement added content type to response object + * Feb 28, 2014 2756 bclement added isSuccess() and isNotExists() to response * * * @@ -126,6 +127,20 @@ public class HttpClient { this.data = data != null ? data : new byte[0]; this.contentType = contentType; } + + /** + * @return true if code is a 200 level return code + */ + public boolean isSuccess() { + return code >= 200 && code < 300; + } + + /** + * @return true if resource does not exist on server + */ + public boolean isNotExists() { + return code == 404 || code == 410; + } } /** diff --git a/edexOsgi/com.raytheon.uf.common.http/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.http/META-INF/MANIFEST.MF index 59585c3fa7..3dcded65af 100644 --- a/edexOsgi/com.raytheon.uf.common.http/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.http/META-INF/MANIFEST.MF @@ -5,5 +5,8 @@ Bundle-SymbolicName: com.raytheon.uf.common.http Bundle-Version: 1.14.0 Bundle-Vendor: RAYTHEON Bundle-RequiredExecutionEnvironment: JavaSE-1.6 -Export-Package: com.raytheon.uf.common.http -Require-Bundle: org.apache.commons.lang;bundle-version="2.3.0" +Export-Package: com.raytheon.uf.common.http, + com.raytheon.uf.common.http.auth +Require-Bundle: org.apache.commons.lang, + org.apache.http, + org.apache.commons.codec diff --git a/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ClientSignatureAuth.java b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ClientSignatureAuth.java new file mode 100644 index 0000000000..611b11ba97 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ClientSignatureAuth.java @@ -0,0 +1,149 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.http.auth; + +import java.net.URI; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; + +import org.apache.http.HttpHost; +import org.apache.http.client.utils.URIUtils; + +/** + * Implements signature authentication for HTTP clients + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 25, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class ClientSignatureAuth extends SignatureAuthScheme { + + private final KeyPair keypair; + + private final Signature sig; + + /** + * Default constructor, creates a new keypair + * + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public ClientSignatureAuth() throws NoSuchAlgorithmException, + InvalidKeyException { + this(generateKeys()); + } + + /** + * @param keypair + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public ClientSignatureAuth(KeyPair keypair) + throws NoSuchAlgorithmException, InvalidKeyException { + this.keypair = keypair; + this.sig = Signature.getInstance(SIG_ALG); + this.sig.initSign(this.keypair.getPrivate()); + } + + /** + * Create a base64 encoded signature from URI + * + * @param uri + * @return + * @throws SignatureException + */ + public String sign(URI uri) throws SignatureException { + return sign(uri, null); + } + + /** + * Create a base64 encoded signature from URI and body + * + * @param uri + * @param bytes + * @return + * @throws SignatureException + */ + public String sign(URI uri, byte[] bytes) throws SignatureException { + HttpHost host = URIUtils.extractHost(uri); + synchronized (sig) { + sig.update(host.toHostString().getBytes()); + sig.update(uri.getPath().getBytes()); + if (bytes != null) { + sig.update(bytes); + } + return base64Encode(sig.sign()); + } + } + + /** + * Generate a new public/private key pair using the default algorithms + * + * @return + * @throws NoSuchAlgorithmException + */ + public static final KeyPair generateKeys() throws NoSuchAlgorithmException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KEY_ALG); + SecureRandom random = SecureRandom.getInstance(RANDOM_ALG); + keyGen.initialize(KEY_SIZE, random); + return keyGen.generateKeyPair(); + } + + /** + * @return the keypair + */ + public KeyPair getKeypair() { + return keypair; + } + + /** + * Get the default public key algorithm + * + * @return + */ + public String getKeyAlgorithm() { + return KEY_ALG; + } + + /** + * Get the base64 X509 encoded public key + * + * @return + */ + public String getEncodedPublicKey() { + PublicKey pub = keypair.getPublic(); + return base64Encode(pub.getEncoded()); + } +} diff --git a/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ServerSignatureAuth.java b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ServerSignatureAuth.java new file mode 100644 index 0000000000..8a888ab4c8 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/ServerSignatureAuth.java @@ -0,0 +1,126 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.http.auth; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +/** + * Implements signature authentication for HTTP servers + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 25, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class ServerSignatureAuth extends SignatureAuthScheme { + + private final Map factoryMap = new HashMap(); + + /** + * Get the KeyFactory associated with the provided algorithm, a new factory + * is created and cached if not found + * + * @param keyAlg + * @return + * @throws NoSuchAlgorithmException + */ + public KeyFactory getFactory(String keyAlg) throws NoSuchAlgorithmException { + KeyFactory rval; + synchronized (factoryMap) { + rval = factoryMap.get(keyAlg); + if (rval == null) { + rval = KeyFactory.getInstance(keyAlg); + factoryMap.put(keyAlg, rval); + } + } + return rval; + } + + /** + * Decode a base64 X509 encoded public key using the provided algorithm. + * + * @param encodedKey + * @param algorithm + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + public PublicKey decodePublicKey(String encodedKey, String algorithm) + throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] bytes = base64Decode(encodedKey); + X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(bytes); + KeyFactory factory = getFactory(algorithm); + return factory.generatePublic(pubKeySpec); + } + + /** + * @param sig + * @param hostHeader + * host used in request including port + * @param path + * path used in request + * @return true if the signature matches the URI parts provided + * @throws SignatureException + */ + public boolean verify(Signature sig, String hostHeader, String path) + throws SignatureException { + return verify(sig, hostHeader, path, null); + } + + /** + * @param sig + * @param hostHeader + * host used in request including port + * @param path + * path used in request + * @param bytes + * body of request + * @return true if the signature matches the URI parts and body provided + * @throws SignatureException + */ + public boolean verify(Signature sig, String hostHeader, String path, + byte[] bytes) throws SignatureException { + synchronized (sig) { + sig.update(hostHeader.getBytes()); + sig.update(path.getBytes()); + if (bytes != null) { + sig.update(bytes); + } + return sig.verify(bytes); + } + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignatureAuthScheme.java b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignatureAuthScheme.java new file mode 100644 index 0000000000..d93bdf8a7a --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignatureAuthScheme.java @@ -0,0 +1,160 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.http.auth; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.codec.binary.Base64; + +/** + * Base class for signature authentication scheme + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 25, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public abstract class SignatureAuthScheme { + + protected static final String RANDOM_ALG = "SHA1PRNG"; + + protected static final int KEY_SIZE = 256; + + protected static final String KEY_ALG = "EC"; + + protected static final String SIG_ALG = "SHA256WITHECDSA"; + + public static final String HTTP_AUTH_HEADER = "Authorization"; + + public static final String AUTH_PARAMETER_DELIM = ","; + + public static final String CRED_FIELD_NAME = "Credential"; + + public static final String SIG_FIELD_NAME = "Signature"; + + /** + * matches '[alorithm-name] [credentials-and-signature]' + */ + private static final Pattern AUTH_HEADER_PATTERN = Pattern + .compile("^(\\S+)\\s+(\\S.+)$"); + + /** + * matches header parameter key/value pairs '[key1]=[val1],[key2]=[val2]' + */ + private static final Pattern AUTH_PARAMETER_PATTERN = Pattern + .compile("([^,=]+)=([^,=]+)"); + + /** + * Create an Authorization header in the form + * + *
+     * '[algorithm-name] Credential=[userid],Signature=[signature]'
+     * 
+ * + * Using the default signature algorithm. + * + * @param userid + * @param signature + * @return + */ + public static String formatAuthHeader(String userid, String signature) { + return formatAuthHeader(new SignedCredential(userid, signature, SIG_ALG)); + } + + /** + * Create an Authorization header in the form + * + *
+     * '[algorithm-name] Credential=[userid],Signature=[signature]'
+     * 
+ * + * @param sc + * @return + */ + public static String formatAuthHeader(SignedCredential sc) { + StringBuilder builder = new StringBuilder(); + builder.append(sc.getAlgorithm()).append(" "); + builder.append(CRED_FIELD_NAME).append("="); + builder.append(sc.getUserid()).append(AUTH_PARAMETER_DELIM); + builder.append(SIG_FIELD_NAME).append("="); + builder.append(sc.getSignature()); + return builder.toString(); + } + + /** + * Parse an Authorization header in the form + * + *
+     * '[algorithm-name] Credential=[userid],Signature=[signature]'
+     * 
+ * + * @param authHeader + * @return + */ + public static SignedCredential parseAuthHeader(String authHeader) { + Matcher m = AUTH_HEADER_PATTERN.matcher(authHeader.trim()); + if (!m.matches()) { + return null; + } + String algorithm = m.group(1).trim(); + String paramStr = m.group(2).trim(); + Map parameters = new HashMap(2); + m = AUTH_PARAMETER_PATTERN.matcher(paramStr); + while (m.find()) { + parameters.put(m.group(1).trim().toLowerCase(), m.group(2).trim()); + } + String userid = parameters.get(CRED_FIELD_NAME.toLowerCase()); + String signature = parameters.get(SIG_FIELD_NAME.toLowerCase()); + return new SignedCredential(userid, signature, algorithm); + } + + /** + * Create a non-chuncked (no newlines) base64 string from bytes + * + * @param bytes + * @return + */ + public static final String base64Encode(byte[] bytes) { + bytes = Base64.encodeBase64(bytes, false); + return org.apache.commons.codec.binary.StringUtils.newStringUtf8(bytes); + } + + /** + * Decode a base64 encoded string + * + * @param encString + * @return + */ + public static final byte[] base64Decode(String encString) { + return Base64.decodeBase64(encString); + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignedCredential.java b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignedCredential.java new file mode 100644 index 0000000000..613875c7f6 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.http/src/com/raytheon/uf/common/http/auth/SignedCredential.java @@ -0,0 +1,78 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.http.auth; + +/** + * Credentials used in an authorization header + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 25, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class SignedCredential { + + private final String userid; + + private final String signature; + + private final String algorithm; + + /** + * @param userid + * @param signature + * @param algorithm + */ + public SignedCredential(String userid, String signature, String algorithm) { + this.userid = userid; + this.signature = signature; + this.algorithm = algorithm; + } + + /** + * @return the userid + */ + public String getUserid() { + return userid; + } + + /** + * @return the signature + */ + public String getSignature() { + return signature; + } + + /** + * @return the algorithm + */ + public String getAlgorithm() { + return algorithm; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/.classpath b/edexOsgi/com.raytheon.uf.common.xmpp/.classpath new file mode 100644 index 0000000000..ad32c83a78 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/.project b/edexOsgi/com.raytheon.uf.common.xmpp/.project new file mode 100644 index 0000000000..5479401fd1 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/.project @@ -0,0 +1,28 @@ + + + com.raytheon.uf.common.xmpp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/.settings/org.eclipse.jdt.core.prefs b/edexOsgi/com.raytheon.uf.common.xmpp/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..c537b63063 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.xmpp/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..9492e89b41 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Common Xmpp +Bundle-SymbolicName: com.raytheon.uf.common.xmpp +Bundle-Version: 1.14 +Bundle-Vendor: RAYTHEON +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: org.jivesoftware.smack, + org.apache.commons.lang +Export-Package: com.raytheon.uf.common.xmpp, + com.raytheon.uf.common.xmpp.ext, + com.raytheon.uf.common.xmpp.iq diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/build.properties b/edexOsgi/com.raytheon.uf.common.xmpp/build.properties new file mode 100644 index 0000000000..34d2e4d2da --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/BaseProvider.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/BaseProvider.java new file mode 100644 index 0000000000..d545fb46de --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/BaseProvider.java @@ -0,0 +1,167 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp; + +import java.io.IOException; +import java.util.Iterator; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.jivesoftware.smackx.packet.DiscoverInfo.Feature; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Base class for XMPP IQ and extension providers which are used to parse XMPP + * packets. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 24, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public abstract class BaseProvider { + + protected final String tagName; + + /** + * @param tagName + * packet tag name, used to determine when provider is done + * parsing + */ + public BaseProvider(String tagName) { + this.tagName = tagName; + } + + /** + * Parse contents of packet from XMPP stream. + * + * @param parser + * @return + * @throws XmlPullParserException + * @throws IOException + * when errors occurs with XMPP stream + */ + abstract protected T parseInternal(XmlPullParser parser) + throws XmlPullParserException, IOException; + + /** + * Parse contents of packet from XMPP stream. Ensures that parser is left in + * a good state so the stream isn't corrupted. + * + * @param parser + * @return + * @throws XmlPullParserException + * @throws IOException + */ + protected T parse(XmlPullParser parser) throws XmlPullParserException, + IOException { + try { + return parseInternal(parser); + } finally { + // ensure that we are at the end of the packet so we don't corrupt + // stream + while (!atEndOfPacket(parser)) { + parser.next(); + } + } + } + + /** + * @param parser + * @return true if parser is at the end tag of the packet + * @throws XmlPullParserException + */ + protected boolean atEndOfPacket(XmlPullParser parser) + throws XmlPullParserException { + return atEndOfTag(parser, tagName); + } + + /** + * @param parser + * @param tag + * @return true if parser is at the end of the specified tag + * @throws XmlPullParserException + */ + protected boolean atEndOfTag(XmlPullParser parser, String tag) + throws XmlPullParserException { + return parser.getEventType() == XmlPullParser.END_TAG + && parser.getName().equals(tag); + } + + /** + * Get any text elements under current tag + * + * @param parser + * @return + * @throws XmlPullParserException + * @throws IOException + */ + protected String getText(XmlPullParser parser) + throws XmlPullParserException, IOException { + if (parser.getEventType() == XmlPullParser.TEXT) { + return parser.getText(); + } else if (parser.getEventType() != XmlPullParser.START_TAG) { + return null; + } + String tag = parser.getName(); + StringBuilder allText = new StringBuilder(); + while (!atEndOfTag(parser, tag)) { + if (parser.getEventType() == XmlPullParser.TEXT) { + allText.append(parser.getText()); + } + parser.next(); + } + return allText.toString(); + } + + /** + * @param conn + * @return true if server has feature listed in supported features + * @throws XMPPException + */ + public static boolean serverSupportsFeature(XMPPConnection conn, + String feature) throws XMPPException { + ServiceDiscoveryManager sdm = ServiceDiscoveryManager + .getInstanceFor(conn); + boolean rval = false; + DiscoverInfo discoverInfo = sdm.discoverInfo(null); + Iterator iter = discoverInfo.getFeatures(); + while (iter.hasNext()) { + String supportedFeature = iter.next().getVar(); + if (supportedFeature.equals(feature)) { + rval = true; + break; + } + } + return rval; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PacketConstants.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PacketConstants.java new file mode 100644 index 0000000000..5ac2321c3e --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PacketConstants.java @@ -0,0 +1,49 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp; + +/** + * Custom XMPP packet constants + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 24, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class PacketConstants { + + public static final String COLLAB_XMLNS = "urn:uf:viz:collaboration"; + + public static final String XMLNS_ATTRIBUTE = "xmlns"; + + public static final String QUERY_ELEMENT_NAME = "query"; + + private PacketConstants() { + } + +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PullParserJaxbAdapter.java similarity index 98% rename from cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java rename to edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PullParserJaxbAdapter.java index a4944047be..af928441d8 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/PullParserJaxbAdapter.java @@ -17,7 +17,7 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.uf.viz.collaboration.comm.provider; +package com.raytheon.uf.common.xmpp; import java.io.IOException; import java.util.HashSet; @@ -43,6 +43,7 @@ import org.xmlpull.v1.XmlPullParserException; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Dec 17, 2013 2562 bclement Initial creation + * Feb 27, 2013 2756 bclement moved to common.xmpp from collaboration * * * diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/XmlBuilder.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/XmlBuilder.java new file mode 100644 index 0000000000..8cc64df199 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/XmlBuilder.java @@ -0,0 +1,187 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp; + +import java.util.Collections; +import java.util.List; + +/** + * Simple XML string builder utility + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class XmlBuilder { + + private final StringBuilder builder = new StringBuilder(); + + /** + * Key value pair object + */ + public static class Pair { + public final String name; + + public final String value; + + public Pair(String name, String value) { + this.name = name; + this.value = value; + } + } + + /** + * Append open tag to XML + * + * @param name + * @return + */ + public XmlBuilder startTag(String name){ + return startTag(name, Collections. emptyList()); + } + + /** + * Append open tag to XML + * + * @param name + * @param namespace + * xmlns attribute to be included in tag + * @return + */ + public XmlBuilder startTag(String name, String namespace) { + return startTag(name, namespace, Collections. emptyList()); + } + + /** + * Append open tag to XML + * + * @param name + * @param attributes + * @return + */ + public XmlBuilder startTag(String name, List attributes) { + return startTag(name, null, attributes); + } + + /** + * Append open tag to XML + * + * @param name + * @param namespace + * xmlns attribute to be included in tag + * @param attributes + * @return + */ + public XmlBuilder startTag(String name, String namespace, + List attributes) { + return appendTag(name, namespace, attributes, false); + } + + /** + * Append self closing tag to XML + * + * @param name + * @param attributes + * @return + */ + public XmlBuilder selfClosingTag(String name, List attributes) { + return appendTag(name, null, attributes, true); + } + + /** + * Append tag to XML + * + * @param name + * @param namespace + * xmlns attribute to be included in tag + * @param attributes + * @param selfClose + * if true, tag will self close + * @return + */ + public XmlBuilder appendTag(String name, String namespace, + List attributes, boolean selfClose) { + builder.append("<").append(name); + if (namespace != null) { + builder.append(" ").append(PacketConstants.XMLNS_ATTRIBUTE) + .append("=\""); + builder.append(namespace).append("\""); + } + for (Pair attrib : attributes) { + builder.append(" ").append(attrib.name).append("=\""); + builder.append(attrib.value).append("\""); + } + if (selfClose) { + builder.append("/>"); + } else { + builder.append(">"); + } + return this; + } + + /** + * Append closing tag to XML + * + * @param name + * @return + */ + public XmlBuilder endTag(String name) { + builder.append(""); + return this; + } + + /** + * Append text to XML + * + * @param text + * @return + */ + public XmlBuilder appendText(String text) { + builder.append(text); + return this; + } + + /** + * @return a string containing the XML added to builder + */ + public String toXml() { + return builder.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return toXml(); + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/BaseExtension.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/BaseExtension.java new file mode 100644 index 0000000000..67caebd5ba --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/BaseExtension.java @@ -0,0 +1,71 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp.ext; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Base packet extension class. Used for custom XMPP packet payloads. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 24, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public abstract class BaseExtension implements PacketExtension { + + protected final String elementName; + + protected final String namespace; + + /** + * @param elementName + * @param namespace + */ + public BaseExtension(String elementName, String namespace) { + this.elementName = elementName; + this.namespace = namespace; + } + + /* (non-Javadoc) + * @see org.jivesoftware.smack.packet.PacketExtension#getElementName() + */ + @Override + public String getElementName() { + return elementName; + } + + /* (non-Javadoc) + * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace() + */ + @Override + public String getNamespace() { + return namespace; + } + +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ChangeAffiliationExtension.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/ChangeAffiliationExtension.java similarity index 77% rename from cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ChangeAffiliationExtension.java rename to edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/ChangeAffiliationExtension.java index 3faceb794d..831b5a9eeb 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/ChangeAffiliationExtension.java +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/ext/ChangeAffiliationExtension.java @@ -17,13 +17,18 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.uf.viz.collaboration.comm.provider.session; +package com.raytheon.uf.common.xmpp.ext; + +import java.util.Arrays; import org.jivesoftware.smackx.pubsub.Affiliation; import org.jivesoftware.smackx.pubsub.Node; import org.jivesoftware.smackx.pubsub.NodeExtension; import org.jivesoftware.smackx.pubsub.PubSubElementType; +import com.raytheon.uf.common.xmpp.XmlBuilder; +import com.raytheon.uf.common.xmpp.XmlBuilder.Pair; + /** * Packet extension for changing a user's affiliation with a pubsub topic. * Follows specification at @@ -36,6 +41,7 @@ import org.jivesoftware.smackx.pubsub.PubSubElementType; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 18, 2014 2751 bclement Initial creation + * Feb 27, 2013 2756 bclement moved to common.xmpp from collaboration * * * @@ -74,19 +80,14 @@ public class ChangeAffiliationExtension extends NodeExtension { */ @Override public String toXML() { - StringBuilder builder = new StringBuilder("<"); - builder.append(getElementName()); - builder.append(" node='"); - builder.append(getNode()); - builder.append("'><").append(affiliationName).append(" "); - builder.append(jidAttribute).append("='"); - builder.append(this.id); - builder.append("' ").append(affiliationName).append("='"); - builder.append(this.type.toString()); - builder.append("'/>"); - return builder.toString(); + XmlBuilder builder = new XmlBuilder(); + builder.startTag(getElementName(), + Arrays.asList(new Pair("node", getNode()))); + builder.selfClosingTag(affiliationName, Arrays.asList(new Pair( + jidAttribute, this.id), + new Pair(affiliationName, this.type.toString()))); + builder.endTag(getElementName()); + return builder.toXml(); } } diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfo.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfo.java new file mode 100644 index 0000000000..1212069b63 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfo.java @@ -0,0 +1,225 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp.iq; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; + +import com.raytheon.uf.common.xmpp.PacketConstants; +import com.raytheon.uf.common.xmpp.XmlBuilder; +import com.raytheon.uf.common.xmpp.XmlBuilder.Pair; + +/** + * Custom IQ packet for public key authentication + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class AuthInfo extends IQ { + + public static final String AUTH_QUERY_XMLNS = "urn:uf:viz:collaboration:iq:auth"; + + public static final String INFO_ELEMENT_NAME = "authinfo"; + + public static final String PUBKEY_ELEMENT_NAME = "publickey"; + + public static final String ALG_ATTRIBUTE = "algorithm"; + + public static final String ID_ATTRIBUTE = "jid"; + + public static final String SESSION_ATTRIBUTE = "sessionid"; + + private String encodedKey; + + private String algorithm; + + private String userid; + + private String sessionid; + + /** + * + */ + public AuthInfo() { + } + + /** + * @param iq + */ + public AuthInfo(IQ iq) { + super(iq); + } + + /** + * @param encodedKey + * @param algorithm + * @param userid + * @param sessionid + */ + public AuthInfo(String encodedKey, String algorithm, String userid, + String sessionid) { + this.encodedKey = encodedKey; + this.algorithm = algorithm; + this.userid = userid; + this.sessionid = sessionid; + } + + /** + * Construct a setter packet used to store a new public key on server + * + * @param encodedKey + * @param algorithm + * @return + */ + public static AuthInfo createSetPacket(String encodedKey, String algorithm) { + AuthInfo rval = new AuthInfo(); + rval.setEncodedKey(encodedKey); + rval.setAlgorithm(algorithm); + rval.setType(Type.SET); + return rval; + } + + /** + * Create a getter packet used to query the server for the current public + * key + * + * @param userid + * @return + */ + public static AuthInfo createGetPacket(String userid) { + return createGetPacket(userid, null); + } + + /** + * Create a getter packet used to query the server for the current public + * key and check authorization for session ownership. + * + * @param userid + * @param sessionid + * @return + */ + public static AuthInfo createGetPacket(String userid, String sessionid) { + AuthInfo rval = new AuthInfo(); + rval.setUserid(userid); + rval.setSessionid(sessionid); + rval.setType(Type.GET); + return rval; + } + + + /* (non-Javadoc) + * @see org.jivesoftware.smack.packet.IQ#getChildElementXML() + */ + @Override + public String getChildElementXML() { + XmlBuilder builder = new XmlBuilder(); + List queryAttributes = new ArrayList(2); + if (userid != null) { + queryAttributes.add(new Pair(ID_ATTRIBUTE, userid)); + } + if (sessionid != null) { + queryAttributes.add(new Pair(SESSION_ATTRIBUTE, sessionid)); + } + builder.startTag(PacketConstants.QUERY_ELEMENT_NAME, AUTH_QUERY_XMLNS, + queryAttributes); + builder.startTag(INFO_ELEMENT_NAME, PacketConstants.COLLAB_XMLNS); + builder.startTag(PUBKEY_ELEMENT_NAME, + Arrays.asList(new Pair(ALG_ATTRIBUTE, algorithm))); + builder.appendText(encodedKey); + builder.endTag(PUBKEY_ELEMENT_NAME); + builder.endTag(INFO_ELEMENT_NAME); + builder.endTag(PacketConstants.QUERY_ELEMENT_NAME); + return builder.toXml(); + } + + /** + * @return the encodedKey + */ + public String getEncodedKey() { + return encodedKey; + } + + /** + * @return the algorithm + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * @return the userid + */ + public String getUserid() { + return userid; + } + + /** + * @return the sessionid + */ + public String getSessionid() { + return sessionid; + } + + /** + * @param encodedKey + * the encodedKey to set + */ + public void setEncodedKey(String encodedKey) { + this.encodedKey = encodedKey; + } + + /** + * @param algorithm + * the algorithm to set + */ + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + /** + * @param userid + * the userid to set + */ + public void setUserid(String userid) { + this.userid = userid; + } + + /** + * @param sessionid + * the sessionid to set + */ + public void setSessionid(String sessionid) { + this.sessionid = sessionid; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfoProvider.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfoProvider.java new file mode 100644 index 0000000000..a5e6dba9af --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/AuthInfoProvider.java @@ -0,0 +1,105 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp.iq; + +import java.io.IOException; + +import org.jivesoftware.smack.provider.IQProvider; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.raytheon.uf.common.xmpp.BaseProvider; +import com.raytheon.uf.common.xmpp.PacketConstants; + +/** + * Custom XMPP IQ packet parsing for public key authentication + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class AuthInfoProvider extends BaseProvider implements + IQProvider { + + /** + * @param extensionTagName + */ + public AuthInfoProvider() { + super(PacketConstants.QUERY_ELEMENT_NAME); + } + + /* + * (non-Javadoc) + * + * @see org.jivesoftware.smack.provider.IQProvider#parseIQ(org.xmlpull.v1. + * XmlPullParser) + */ + @Override + public AuthInfo parseIQ(XmlPullParser parser) throws Exception { + return parse(parser); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.common.xmpp.BaseProvider#parseInternal(org.xmlpull.v1 + * .XmlPullParser) + */ + @Override + protected AuthInfo parseInternal(XmlPullParser parser) + throws XmlPullParserException, IOException { + String encodedKey = null; + String algorithm = null; + String userid = null; + String sessionid = null; + + + do { + String tagName = parser.getName(); + switch (parser.getEventType()) { + case XmlPullParser.START_TAG: + if (PacketConstants.QUERY_ELEMENT_NAME.equals(tagName)) { + sessionid = parser.getAttributeValue(null, + AuthInfo.SESSION_ATTRIBUTE); + userid = parser.getAttributeValue(null, + AuthInfo.ID_ATTRIBUTE); + } else if (AuthInfo.PUBKEY_ELEMENT_NAME.equals(tagName)) { + algorithm = parser.getAttributeValue(null, + AuthInfo.ALG_ATTRIBUTE); + encodedKey = getText(parser); + } + break; + } + parser.next(); + } while (!atEndOfPacket(parser)); + return new AuthInfo(encodedKey, algorithm, userid, sessionid); + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfo.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfo.java new file mode 100644 index 0000000000..8221a7d466 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfo.java @@ -0,0 +1,101 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp.iq; + +import org.jivesoftware.smack.packet.IQ; + +import com.raytheon.uf.common.xmpp.PacketConstants; +import com.raytheon.uf.common.xmpp.XmlBuilder; + +/** + * Custom XMPP IQ packet for HTTP server configuration + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class HttpInfo extends IQ { + + public static final String INFO_ELEMENT = "httpinfo"; + + public static final String URL_ELEMENT = "url"; + + public static final String QUERY_XMLNS = "urn:uf:viz:collaboration:iq:http"; + + private String url; + + /** + * + */ + public HttpInfo() { + } + + /** + * @param iq + */ + public HttpInfo(IQ iq) { + super(iq); + } + + /** + * @param url + * bare HTTP url of data server + */ + public HttpInfo(String url) { + this.url = url; + } + + @Override + public String getChildElementXML() { + XmlBuilder builder = new XmlBuilder(); + builder.startTag(PacketConstants.QUERY_ELEMENT_NAME, QUERY_XMLNS); + builder.startTag(INFO_ELEMENT, PacketConstants.COLLAB_XMLNS); + builder.startTag(URL_ELEMENT).appendText(url); + builder.endTag(URL_ELEMENT); + builder.endTag(INFO_ELEMENT); + builder.endTag(PacketConstants.QUERY_ELEMENT_NAME); + return null; + } + + /** + * @return the url + */ + public String getUrl() { + return url; + } + + /** + * @param url + * the url to set + */ + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfoProvider.java b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfoProvider.java new file mode 100644 index 0000000000..df316e66b0 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.xmpp/src/com/raytheon/uf/common/xmpp/iq/HttpInfoProvider.java @@ -0,0 +1,91 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.xmpp.iq; + +import java.io.IOException; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.IQProvider; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.raytheon.uf.common.xmpp.BaseProvider; + +/** + * Custom XMPP IQ packet parser for HTTP configuration + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class HttpInfoProvider extends BaseProvider implements + IQProvider { + + /** + * @param extensionTagName + */ + public HttpInfoProvider() { + super(HttpInfo.QUERY_XMLNS); + } + + /* (non-Javadoc) + * @see org.jivesoftware.smack.provider.IQProvider#parseIQ(org.xmlpull.v1.XmlPullParser) + */ + @Override + public IQ parseIQ(XmlPullParser parser) throws Exception { + return parse(parser); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.common.xmpp.BaseProvider#parseInternal(org.xmlpull.v1 + * .XmlPullParser) + */ + @Override + protected HttpInfo parseInternal(XmlPullParser parser) + throws XmlPullParserException, IOException { + String url = null; + + do { + String tagName = parser.getName(); + switch (parser.getEventType()) { + case XmlPullParser.START_TAG: + if (HttpInfo.URL_ELEMENT.equals(tagName)) { + url = getText(parser); + } + break; + } + parser.next(); + } while (!atEndOfPacket(parser)); + return new HttpInfo(url); + } + +} diff --git a/javaUtilities/collaboration.dataserver/META-INF/MANIFEST.MF b/javaUtilities/collaboration.dataserver/META-INF/MANIFEST.MF index d6f3591a3d..23b382c40b 100644 --- a/javaUtilities/collaboration.dataserver/META-INF/MANIFEST.MF +++ b/javaUtilities/collaboration.dataserver/META-INF/MANIFEST.MF @@ -7,4 +7,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Require-Bundle: org.eclipse.jetty, com.raytheon.uf.common.util, org.jivesoftware.smack, - com.raytheon.uf.common.http + com.raytheon.uf.common.http, + com.raytheon.uf.common.xmpp, + org.apache.http, + org.apache.commons.collections diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/Config.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/Config.java index 391ac66579..f444bc5b05 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/Config.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/Config.java @@ -34,7 +34,8 @@ import java.util.Properties; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Feb 5, 2014 2756 bclement Initial creation + * Feb 5, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement added auth cache size * * * @@ -77,6 +78,10 @@ public class Config{ public static final String XMPP_SERVER_DEFAULT = "localhost"; + public static final String AUTH_CACHE_SIZE_KEY = "auth.key.cache.size"; + + public static final int AUTH_CACHE_SIZE_DEFAULT = 64; + public static final String config = System.getProperty( "collaboration.dataserver.config", "config/settings.properties"); diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataService.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataService.java index bacebcca64..8dd4b17996 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataService.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataService.java @@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import com.raytheon.collaboration.dataserver.storage.FileManager; import com.raytheon.uf.common.http.AcceptHeaderParser; import com.raytheon.uf.common.http.AcceptHeaderValue; @@ -162,7 +163,6 @@ public class DataService extends HttpServlet { @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // TODO auth try { File file = getFile(req); manager.writeFile(req.getInputStream(), file); @@ -196,7 +196,6 @@ public class DataService extends HttpServlet { @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // TODO auth try { File file = getFile(req); if (!file.exists()) { diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataserverMain.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataserverMain.java index 41268f2fbc..ac7226a844 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataserverMain.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/DataserverMain.java @@ -26,6 +26,8 @@ import java.util.TimeZone; import org.eclipse.jetty.util.RolloverFileOutputStream; +import com.raytheon.collaboration.dataserver.auth.ServerAuthManager; + /** * Entry class for dataserver * @@ -35,7 +37,8 @@ import org.eclipse.jetty.util.RolloverFileOutputStream; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Feb 5, 2014 2756 bclement Initial creation + * Feb 5, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement added authManager * * * @@ -62,15 +65,17 @@ public class DataserverMain { .println("Continuing using standard out and standard error"); } final XmppServerConnection xmppConnection; + final ServerAuthManager authManager; try { xmppConnection = new XmppServerConnection(); + authManager = new ServerAuthManager(xmppConnection); } catch (Exception e) { System.err .println("Unable to connect to XMPP server, shutting down"); e.printStackTrace(); return; } - final WebServerRunner webServer = new WebServerRunner(); + final WebServerRunner webServer = new WebServerRunner(authManager); new Thread(webServer).start(); wait(CONNECTION_DELAY); new Thread(xmppConnection).start(); diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/WebServerRunner.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/WebServerRunner.java index 08f251d796..0adfc9d7fd 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/WebServerRunner.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/WebServerRunner.java @@ -20,11 +20,22 @@ package com.raytheon.collaboration.dataserver; import java.io.File; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; +import com.raytheon.collaboration.dataserver.auth.AuthFilter; +import com.raytheon.collaboration.dataserver.auth.DeleteAuthHandler; +import com.raytheon.collaboration.dataserver.auth.MethodAuthHandler; +import com.raytheon.collaboration.dataserver.auth.PutAuthHandler; +import com.raytheon.collaboration.dataserver.auth.ServerAuthManager; + /** * Start and run jetty webserver * @@ -35,6 +46,7 @@ import org.eclipse.jetty.servlet.ServletHolder; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 14, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement added authManager * * * @@ -45,6 +57,12 @@ public class WebServerRunner implements Runnable { private Server server; + private final ServerAuthManager authManager; + + public WebServerRunner(ServerAuthManager authManager) { + this.authManager = authManager; + } + /* (non-Javadoc) * @see java.lang.Runnable#run() */ @@ -66,8 +84,14 @@ public class WebServerRunner implements Runnable { String datapath = Config.getPath(Config.DATAPATH_KEY, Config.DATAPATH_DEFAULT); - context.addServlet(new ServletHolder(new DataService(base)), datapath - + "*"); + String pathspec = datapath + + "*"; + context.addServlet(new ServletHolder(new DataService(base)), pathspec); + + List methods = Arrays.asList(new PutAuthHandler( + authManager), new DeleteAuthHandler(authManager)); + context.addFilter(new FilterHolder(new AuthFilter(methods)), pathspec, + EnumSet.allOf(DispatcherType.class)); try { server.start(); System.out.println("Server started"); diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/XmppServerConnection.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/XmppServerConnection.java index 42413b23a6..666e6d642a 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/XmppServerConnection.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/XmppServerConnection.java @@ -23,15 +23,18 @@ import java.net.UnknownHostException; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.filter.PacketFilter; -import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.util.SyncPacketSend; +import com.raytheon.uf.common.xmpp.PacketConstants; +import com.raytheon.uf.common.xmpp.iq.AuthInfo; +import com.raytheon.uf.common.xmpp.iq.AuthInfoProvider; +import com.raytheon.uf.common.xmpp.iq.HttpInfo; + /** * Starts and runs XMPP client thread for communication with XMPP server * @@ -42,6 +45,7 @@ import org.jivesoftware.smack.util.SyncPacketSend; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 14, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement added custom IQ packet support * * * @@ -50,6 +54,12 @@ import org.jivesoftware.smack.util.SyncPacketSend; */ public class XmppServerConnection implements Runnable { + static { + ProviderManager pm = ProviderManager.getInstance(); + pm.addIQProvider(PacketConstants.QUERY_ELEMENT_NAME, + AuthInfo.AUTH_QUERY_XMLNS, new AuthInfoProvider()); + } + private static final int PACKET_SEND_TIMEOUT = 5000; // 5 seconds private final XMPPConnection conn; @@ -87,25 +97,7 @@ public class XmppServerConnection implements Runnable { */ @Override public void run() { - conn.addPacketListener(new PacketListener() { - @Override - public void processPacket(Packet packet) { - log.debug(packet.toXML()); - } - }, new PacketFilter() { - @Override - public boolean accept(Packet packet) { - return true; - } - }); - IQ packet = new IQ() { - @Override - public String getChildElementXML() { - return "" - + "" - + "" + dataServerUrl + ""; - } - }; + HttpInfo packet = new HttpInfo(dataServerUrl); packet.setType(Type.SET); try { Packet reply = SyncPacketSend.getReply(conn, packet, @@ -132,4 +124,11 @@ public class XmppServerConnection implements Runnable { conn.disconnect(); } + /** + * @return the conn + */ + public XMPPConnection getConnection() { + return conn; + } + } diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/AuthFilter.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/AuthFilter.java new file mode 100644 index 0000000000..bb4c11b51d --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/AuthFilter.java @@ -0,0 +1,144 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.uf.common.http.auth.SignatureAuthScheme; +import com.raytheon.uf.common.http.auth.SignedCredential; + +/** + * Servlet filter for authentication and authorization of requests + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class AuthFilter implements Filter { + + private final Map handlerMap; + + /** + * If a method handler isn't provided for a method, method requests will be + * forwarded on to the servlet + * + * @param methodHandlers + */ + public AuthFilter(List methodHandlers) { + Map map = new HashMap( + methodHandlers.size()); + for (MethodAuthHandler handler : methodHandlers) { + map.put(handler.getMethod(), handler); + } + this.handlerMap = Collections.unmodifiableMap(map); + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#destroy() + */ + @Override + public void destroy() { + // do nothing + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) + */ + @Override + public void doFilter(ServletRequest req, ServletResponse resp, + FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpReq = (HttpServletRequest) req; + MethodAuthHandler handler = handlerMap.get(httpReq.getMethod() + .toLowerCase()); + if (handler == null || !handler.getAuthManager().isEnabled()) { + chain.doFilter(req, resp); + return; + } + try { + SignedCredential credential = parseCredentials(httpReq); + ParsedUrl urlparts = ParsedUrl.parse(httpReq); + HttpServletResponse httpResp = (HttpServletResponse) resp; + handler.authorize(httpReq, httpResp, chain, credential, urlparts); + }catch(RestException e){ + HttpServletResponse httpResp = (HttpServletResponse) resp; + httpResp.sendError(e.getCode(), e.getLocalizedMessage()); + } + } + + /** + * Parse Authorization header + * + * @param httpReq + * @return + * @throws RestException + * if header does not exist or is malformed + */ + private SignedCredential parseCredentials(HttpServletRequest httpReq) + throws RestException { + String header = httpReq.getHeader(SignatureAuthScheme.HTTP_AUTH_HEADER); + if (header == null || header.trim().isEmpty()) { + throw new RestException(HttpServletResponse.SC_UNAUTHORIZED, + "Missing header: " + SignatureAuthScheme.HTTP_AUTH_HEADER); + } + SignedCredential credential = SignatureAuthScheme + .parseAuthHeader(header); + if (credential == null || credential.getAlgorithm() == null + || credential.getSignature() == null + || credential.getUserid() == null) { + throw new RestException(HttpServletResponse.SC_BAD_REQUEST, + "Malformed header: " + SignatureAuthScheme.HTTP_AUTH_HEADER); + } + return credential; + } + + + + /* (non-Javadoc) + * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) + */ + @Override + public void init(FilterConfig arg0) throws ServletException { + // do nothing + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/DeleteAuthHandler.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/DeleteAuthHandler.java new file mode 100644 index 0000000000..0baca2b1bd --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/DeleteAuthHandler.java @@ -0,0 +1,127 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.io.IOException; +import java.security.Signature; +import java.security.SignatureException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.uf.common.http.auth.SignatureAuthScheme; +import com.raytheon.uf.common.http.auth.SignedCredential; +import com.raytheon.uf.common.util.StringUtil; + +/** + * Auth handler for HTTP DELETE requests + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 27, 2014            bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class DeleteAuthHandler extends MethodAuthHandler { + + private final Logger log = Log.getLogger(this.getClass()); + + /** + * @param authManager + */ + public DeleteAuthHandler(ServerAuthManager authManager) { + super(authManager, "delete"); + } + + /* (non-Javadoc) + * @see com.raytheon.collaboration.dataserver.auth.MethodAuthHandler#authorize(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain, com.raytheon.uf.common.http.auth.SignedCredential, com.raytheon.collaboration.dataserver.auth.ParsedUrl) + */ + @Override + public void authorize(HttpServletRequest httpReq, + HttpServletResponse httpResp, FilterChain chain, + SignedCredential credential, ParsedUrl urlParts) + throws RestException, ServletException, IOException { + String userid = urlParts.getUserid(); + Signature sig; + if (StringUtil.isEmptyString(userid)) { + // deleting entire session, verify that user is owner + sig = authManager.getAuthorizedSignatureDirect(credential, + urlParts.getSessionid()); + } else { + // verify that user owns resource to delete + if (!userid.equals(credential.getUserid())) { + throw new RestException(HttpServletResponse.SC_FORBIDDEN, + "Credential does not own resource"); + } + sig = authManager.getAuthorizedSignature(credential); + } + try { + if (verify(sig, credential, urlParts)) { + chain.doFilter(httpReq, httpResp); + } else { + // possible that we have an outdated public key cached + sig = authManager.getAuthorizedSignatureDirect(credential); + if (verify(sig, credential, urlParts)) { + chain.doFilter(httpReq, httpResp); + } else { + throw new RestException(HttpServletResponse.SC_FORBIDDEN, + "Credentials not authenticated"); + } + } + } catch (SignatureException e) { + log.warn("Problem using signature object", e); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error using authentication server"); + } + } + + /** + * @param sig + * @param credential + * @param urlParts + * @return true if signature matches credentials + * @throws SignatureException + */ + private boolean verify(Signature sig, SignedCredential credential, + ParsedUrl urlParts) throws SignatureException { + synchronized (sig) { + sig.update(urlParts.getHostString().getBytes()); + sig.update(urlParts.getPathString().getBytes()); + byte[] sigBytes = SignatureAuthScheme.base64Decode(credential + .getSignature()); + return sig.verify(sigBytes); + } + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/MethodAuthHandler.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/MethodAuthHandler.java new file mode 100644 index 0000000000..da46e1413f --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/MethodAuthHandler.java @@ -0,0 +1,97 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.uf.common.http.auth.SignedCredential; + +/** + * Base auth handler for HTTP methods + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 27, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public abstract class MethodAuthHandler { + + protected final String method; + + protected final ServerAuthManager authManager; + + + /** + * @param authManager + * @param method + */ + public MethodAuthHandler(ServerAuthManager authManager, String method) { + this.authManager = authManager; + this.method = method; + } + + /** + * Authorize request using provided credentials. Sends the request down the + * FilterChain if authorized. + * + * @param httpReq + * @param httpResp + * @param chain + * @param credential + * @param urlParts + * @throws RestException + * if request is not authorized or invalid + * @throws ServletException + * @throws IOException + */ + abstract public void authorize(HttpServletRequest httpReq, + HttpServletResponse httpResp, FilterChain chain, + SignedCredential credential, ParsedUrl urlParts) + throws RestException, ServletException, IOException; + + /** + * @return the method + */ + public String getMethod() { + return method; + } + + /** + * @return the authManager + */ + public ServerAuthManager getAuthManager() { + return authManager; + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ParsedUrl.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ParsedUrl.java new file mode 100644 index 0000000000..ba18793c1b --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ParsedUrl.java @@ -0,0 +1,168 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.net.URI; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.client.utils.URIUtils; + +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.uf.common.util.StringUtil; + +/** + * Parsed HTTP request url + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 27, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class ParsedUrl { + + /** + *
+     * /session_data/[sessionid]/[userid]
+     * 
+ */ + private static final Pattern PATH_PATTERN = Pattern + .compile("^/?([^/]+)/([^/]+)(/([^/]+))?.*$"); + + /** + * parent directory reference (..) anywhere in path + */ + private static final Pattern REL_PATH_PATTERN = Pattern + .compile("^\\.\\./|/\\.\\./|/\\.\\.$|^\\.\\.$"); + + private final String hostString; + + private final String pathString; + + private final String sessionid; + + private final String userid; + + /** + * @param hostString + * @param pathString + * @param sessionid + * @param userid + */ + private ParsedUrl(String hostString, String pathString, String sessionid, + String userid) { + this.hostString = hostString; + this.pathString = pathString; + this.sessionid = sessionid; + this.userid = userid; + } + + /** + * Parse URL of request + * + * @param httpReq + * @return + * @throws RestException + */ + public static ParsedUrl parse(HttpServletRequest httpReq) + throws RestException { + URI uri = URI.create(httpReq.getRequestURL().toString()); + String hostHeader = URIUtils.extractHost(uri).toHostString(); + if (StringUtil.isEmptyString(hostHeader)) { + throw new RestException(HttpServletResponse.SC_BAD_REQUEST, + "Unable to determine host string"); + } + String path = uri.getPath(); + Matcher m = REL_PATH_PATTERN.matcher(path); + if (m.find()) { + throw new RestException(HttpServletResponse.SC_BAD_REQUEST, + "Relative path inderection is not allowed"); + } + m = PATH_PATTERN.matcher(path); + if (!m.matches()) { + throw new RestException(HttpServletResponse.SC_BAD_REQUEST, + "Malformed session data path"); + } + /* + * first part of url will always be the path to the servlet + * (session_data). The next is enforced as the sessionid and the data + * provider's userid after that. The userid is optional because topic + * owners can delete the entire session tree + */ + String sessionid = getSafeGroup(m, 2); + String userid = getSafeGroup(m, 4); + return new ParsedUrl(hostHeader, path, sessionid, userid); + } + + /** + * Get trimmed matcher group + * + * @param m + * @param group + * @return null if group was empty + */ + public static String getSafeGroup(Matcher m, int group) { + String rval = m.group(group); + if (rval != null) { + rval = rval.trim(); + } + return rval; + } + + /** + * @return the hostString + */ + public String getHostString() { + return hostString; + } + + /** + * @return the pathString + */ + public String getPathString() { + return pathString; + } + + /** + * @return the sessionid + */ + public String getSessionid() { + return sessionid; + } + + /** + * @return the userid + */ + public String getUserid() { + return userid; + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/PutAuthHandler.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/PutAuthHandler.java new file mode 100644 index 0000000000..2717bc3db5 --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/PutAuthHandler.java @@ -0,0 +1,133 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.io.IOException; +import java.security.Signature; +import java.security.SignatureException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.uf.common.http.auth.SignatureAuthScheme; +import com.raytheon.uf.common.http.auth.SignedCredential; + +/** + * Auth handler for HTTP PUT requests + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 27, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class PutAuthHandler extends MethodAuthHandler { + + private final Logger log = Log.getLogger(this.getClass()); + + /** + * @param method + */ + public PutAuthHandler(ServerAuthManager authManager) { + super(authManager, "put"); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.collaboration.dataserver.auth.MethodAuthHandler#authorize + * (javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain, + * com.raytheon.uf.common.http.auth.SignedCredential, + * com.raytheon.collaboration.dataserver.auth.ParsedUrl) + */ + @Override + public void authorize(HttpServletRequest httpReq, + HttpServletResponse httpResp, FilterChain chain, + SignedCredential credential, ParsedUrl urlParts) + throws RestException, ServletException { + if (!urlParts.getUserid().equals(credential.getUserid())) { + throw new RestException(HttpServletResponse.SC_FORBIDDEN, + "Credential does not own resource"); + } + Signature sig = authManager.getAuthorizedSignature(credential); + try { + VerifyingRequest wrapper = new VerifyingRequest(httpReq); + if (verify(sig, wrapper, credential, urlParts)) { + chain.doFilter(wrapper, httpResp); + } else { + // possible that we have an outdated public key cached + sig = authManager.getAuthorizedSignatureDirect(credential); + if (verify(sig, wrapper, credential, urlParts)) { + chain.doFilter(wrapper, httpResp); + } else { + throw new RestException(HttpServletResponse.SC_FORBIDDEN, + "Credentials not authenticated"); + } + } + } catch (SignatureException e) { + log.warn("Problem using signature object", e); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error using authentication server"); + } catch (IOException e) { + log.warn("Problem transferring stream", e); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Problem reading from stream"); + } + } + + /** + * @param sig + * @param wrapper + * @param credential + * @param urlParts + * @return true if signature matches the credentials + * @throws SignatureException + * @throws IOException + */ + private boolean verify(Signature sig, VerifyingRequest wrapper, + SignedCredential credential, ParsedUrl urlParts) + throws SignatureException, IOException { + synchronized (sig) { + sig.update(urlParts.getHostString().getBytes()); + sig.update(urlParts.getPathString().getBytes()); + byte[] sigBytes = SignatureAuthScheme.base64Decode(credential + .getSignature()); + return wrapper.verify(sig, sigBytes); + } + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ServerAuthManager.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ServerAuthManager.java new file mode 100644 index 0000000000..3b3a428b87 --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/ServerAuthManager.java @@ -0,0 +1,230 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.security.Signature; +import java.util.Collections; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.collections.map.LRUMap; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.packet.XMPPError.Condition; +import org.jivesoftware.smack.util.SyncPacketSend; + +import com.raytheon.collaboration.dataserver.Config; +import com.raytheon.collaboration.dataserver.RestException; +import com.raytheon.collaboration.dataserver.XmppServerConnection; +import com.raytheon.uf.common.http.auth.ServerSignatureAuth; +import com.raytheon.uf.common.http.auth.SignedCredential; +import com.raytheon.uf.common.xmpp.BaseProvider; +import com.raytheon.uf.common.xmpp.iq.AuthInfo; + +/** + * Manages authentication credentials for HTTP data server which are stored on + * the XMPP server + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 25, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class ServerAuthManager { + + private final Logger log = Log.getLogger(this.getClass()); + + private final XmppServerConnection xmppServer; + + private final ServerSignatureAuth sigAuth = new ServerSignatureAuth(); + + private final boolean isEnabled; + + private final Map sigCache; + + /** + * @param xmppServer + * @throws XMPPException + */ + @SuppressWarnings("unchecked") + public ServerAuthManager(XmppServerConnection xmppServer) + throws XMPPException { + isEnabled = BaseProvider.serverSupportsFeature( + xmppServer.getConnection(), AuthInfo.AUTH_QUERY_XMLNS); + this.xmppServer = xmppServer; + int cacheSize = Config.getInt(Config.AUTH_CACHE_SIZE_KEY, + Config.AUTH_CACHE_SIZE_DEFAULT); + this.sigCache = Collections.synchronizedMap(new LRUMap(cacheSize)); + } + + /** + * Returns cached signature verification for credential. + * + * @param credential + * @return + * @throws RestException + * if there was an internal error or credentials are not + * authorized for request + */ + public Signature getAuthorizedSignature(SignedCredential credential) + throws RestException { + Signature rval; + rval = sigCache.get(credential.getUserid()); + if (rval == null) { + rval = getAuthorizedSignatureDirect(credential); + } + return rval; + } + + /** + * Returns signature verification for credential from server. Caches result. + * + * @param credential + * @return + * @throws RestException + * if there was an internal error or credentials are not + * authorized for request + */ + public Signature getAuthorizedSignatureDirect(SignedCredential credential) + throws RestException { + return getAuthorizedSignatureDirect(credential, null); + } + + /** + * Returns signature verification for credential from server. Caches result. + * + * @param credential + * @param sessionid + * @return + * @throws RestException + * if there was an internal error or credentials are not + * authorized for request + */ + public Signature getAuthorizedSignatureDirect(SignedCredential credential, + String sessionid) throws RestException { + AuthInfo query; + if (sessionid == null) { + query = AuthInfo.createGetPacket(credential.getUserid()); + } else { + query = AuthInfo.createGetPacket(credential.getUserid(), + sessionid); + } + AuthInfo info = queryServerForAuth(query); + Signature rval = getSignature(credential, info); + sigCache.put(credential.getUserid(), rval); + return rval; + } + + /** + * Query XMPP server for auth information + * + * @param query + * @return + * @throws RestException + * if there was an internal error or credentials are not + * authorized for request + */ + private AuthInfo queryServerForAuth(AuthInfo query) throws RestException { + AuthInfo rval; + try { + XMPPConnection conn = xmppServer.getConnection(); + Packet response = SyncPacketSend.getReply(conn, query); + if (response instanceof AuthInfo) { + rval = (AuthInfo) response; + } else { + log.warn("Unexpected return type from XMPP server: " + + response.toXML()); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Problem communicating with authentication server"); + } + } catch (XMPPException e) { + XMPPError xmppError = e.getXMPPError(); + if ( xmppError != null &&xmppError.getCondition().equals(Condition.not_allowed)){ + log.debug("Credential not authorized for sessionid: " + + xmppError.getMessage()); + throw new RestException(HttpServletResponse.SC_FORBIDDEN, + "Credential not authorized for session"); + } else { + log.warn("Problem getting signature from server", e); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Problem communicating with authentication server"); + } + } + return rval; + } + + /** + * Construct signature validation object using credentials and private key + * from XMPP server. + * + * @param credential + * @param info + * @return + * @throws RestException + * if XMPP server contains malformed auth info + */ + private Signature getSignature(SignedCredential credential, AuthInfo info) + throws RestException { + if (info.getAlgorithm() == null || info.getEncodedKey() == null) { + log.warn("Missing elements from auth info packet: " + info.toXML()); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Authentication server contains malformed authorization information"); + } + Signature rval; + try { + PublicKey key = sigAuth.decodePublicKey(info.getEncodedKey(), + info.getAlgorithm()); + rval = Signature.getInstance(credential.getAlgorithm()); + rval.initVerify(key); + } catch (GeneralSecurityException e) { + log.warn("Unable to create signature verification", e); + throw new RestException( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Authentication server contains malformed authorization information"); + } + return rval; + } + + /** + * @return true if XMPP server supports public key auth scheme + */ + public boolean isEnabled() { + return isEnabled; + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/VerifyingRequest.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/VerifyingRequest.java new file mode 100644 index 0000000000..137f2e7cbf --- /dev/null +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/auth/VerifyingRequest.java @@ -0,0 +1,153 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.collaboration.dataserver.auth; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.Signature; +import java.security.SignatureException; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +import com.raytheon.collaboration.dataserver.storage.FileManager; + +/** + * HTTP Servlet Request Wrapper for public key signature verification. Allows + * body of request to be read multiple times. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Feb 26, 2014 2756       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class VerifyingRequest extends HttpServletRequestWrapper { + + private final Logger log = Log.getLogger(this.getClass()); + + private byte[] body = null; + + /** + * @param request + */ + public VerifyingRequest(HttpServletRequest request) throws IOException { + super(request); + } + + /* + * (non-Javadoc) + * + * @see javax.servlet.ServletRequestWrapper#getInputStream() + */ + @Override + public ServletInputStream getInputStream() throws IOException { + if (body == null) { + ByteArrayOutputStream out = new ByteArrayOutputStream( + FileManager.BUFFER_SIZE); + FileManager.copy(super.getInputStream(), out); + body = out.toByteArray(); + } + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return bais.read(); + } + }; + } + + /** + * @param verification + * @param signature + * @return true if signature matches request + * @throws IOException + */ + private boolean verifyInternal(final Signature verification, + byte[] signature) throws IOException { + final boolean[] errorFlag = { false }; + ServletInputStream in = super.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream( + FileManager.BUFFER_SIZE) { + + @Override + public synchronized void write(byte[] b, int off, int len) { + super.write(b, off, len); + try { + verification.update(b, off, len); + } catch (SignatureException e) { + log.warn("Problem verifying request", e); + errorFlag[0] = true; + } + } + + @Override + public synchronized void write(int b) { + super.write(b); + try { + verification.update((byte) b); + } catch (SignatureException e) { + log.warn("Problem verifying request", e); + errorFlag[0] = true; + } + } + }; + FileManager.copy(in, out); + this.body = out.toByteArray(); + boolean rval = false; + try { + rval = errorFlag[0] ? false : verification.verify(signature); + } catch (SignatureException e) { + log.warn("Problem verifying request", e); + rval = false; + } + return rval; + } + + /** + * @param verification + * @param signature + * @return true if signature matches request + * @throws IOException + * @throws SignatureException + */ + public boolean verify(Signature verification, byte[] signature) + throws IOException, SignatureException { + if (this.body == null) { + return verifyInternal(verification, signature); + } else { + verification.update(body); + return verification.verify(signature); + } + } + +} diff --git a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/FileManager.java b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/storage/FileManager.java similarity index 97% rename from javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/FileManager.java rename to javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/storage/FileManager.java index 9e7387b5a4..38fa32808b 100644 --- a/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/FileManager.java +++ b/javaUtilities/collaboration.dataserver/src/com/raytheon/collaboration/dataserver/storage/FileManager.java @@ -17,7 +17,7 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.collaboration.dataserver; +package com.raytheon.collaboration.dataserver.storage; import java.io.File; import java.io.FileInputStream; @@ -33,6 +33,7 @@ import java.util.ListIterator; import javax.servlet.http.HttpServletResponse; +import com.raytheon.collaboration.dataserver.RestException; import com.raytheon.uf.common.util.concurrent.KeyLock; import com.raytheon.uf.common.util.concurrent.KeyLocker; @@ -47,7 +48,8 @@ import com.raytheon.uf.common.util.concurrent.KeyLocker; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Feb 6, 2014 2756 bclement Initial creation + * Feb 6, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement moved to storage package, made buffer size public * * * @@ -56,7 +58,7 @@ import com.raytheon.uf.common.util.concurrent.KeyLocker; */ public class FileManager { - private static final int BUFFER_SIZE = 1024 * 256; // 256K + public static final int BUFFER_SIZE = 1024 * 256; // 256K private static final ThreadLocal localBuffer = new ThreadLocal() { diff --git a/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/AbstractConfigHandler.java b/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/AbstractConfigHandler.java index 447ba60350..bcfc5fe5ce 100644 --- a/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/AbstractConfigHandler.java +++ b/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/AbstractConfigHandler.java @@ -51,6 +51,7 @@ import org.xmpp.packet.PacketError.Condition; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 12, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement reordered retrieve method operations for clarity * * * @@ -264,12 +265,15 @@ public abstract class AbstractConfigHandler extends IQHandler implements */ protected IQ retrieve(IQ requestPacket, String id, Element key) { Element queryElem = requestPacket.getChildElement(); - Element respElem = storage.get(id, key); IQ rval = IQ.createResultIQ(requestPacket); - queryElem.setParent(null); + Element respElem = storage.get(id, key); + respElem.setParent(null); + removeChildren(queryElem); - queryElem.add(respElem); + queryElem.setParent(null); rval.setChildElement(queryElem); + queryElem.add(respElem); + log.info(rval.toXML()); return rval; } diff --git a/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/DataAuthHandler.java b/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/DataAuthHandler.java index 8b39394a7d..d5f0cf9696 100644 --- a/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/DataAuthHandler.java +++ b/javaUtilities/com.raytheon.openfire.plugin.configuration.collaboration/java/com/raytheon/openfire/plugin/configuration/collaboration/iq/DataAuthHandler.java @@ -46,6 +46,7 @@ import org.xmpp.packet.PacketError.Condition; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 10, 2014 2756 bclement Initial creation + * Feb 28, 2014 2756 bclement if sessionid is not provided, skip authorization step * * * @@ -105,25 +106,25 @@ public class DataAuthHandler extends AbstractConfigHandler { } JID target = new JID(jid); String sessionId = queryElem.attributeValue(SESSIONID_ATTRIBUTE); - if (StringUtils.isBlank(sessionId)) { - String msg = "Missing attribute: " + SESSIONID_ATTRIBUTE; - log.debug(msg); - return createError(packet, new PacketError(Condition.bad_request, - PacketError.Type.modify, msg)); - } - Node node = pubsub.getNode(sessionId); - if (node == null) { - String msg = "No topic found for session: " + sessionId; - log.debug(msg); - return createError(packet, new PacketError(Condition.not_allowed, - PacketError.Type.cancel, msg)); - } - if (!isOwner(jid, node)){ - String msg = "User '" + jid + "' is not an owner of session '" - + sessionId + "'"; - log.debug(msg); - return createError(packet, new PacketError(Condition.not_allowed, - PacketError.Type.cancel, msg)); + if (!StringUtils.isBlank(sessionId)) { + /* + * only check authorization if session id is specified, otherwise we + * are just doing authentication + */ + Node node = pubsub.getNode(sessionId); + if (node == null) { + String msg = "No topic found for session: " + sessionId; + log.debug(msg); + return createError(packet, new PacketError( + Condition.not_allowed, PacketError.Type.cancel, msg)); + } + if (!isOwner(jid, node)) { + String msg = "User '" + jid + "' is not an owner of session '" + + sessionId + "'"; + log.debug(msg); + return createError(packet, new PacketError( + Condition.not_allowed, PacketError.Type.cancel, msg)); + } } return retrieve(packet, target.getNode(), createElement(INFO_ELEMENT_NAME, COLLAB_XMLNS));