diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index 048e0942a..cbc8a2654 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -219,7 +219,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { protected void loginInternal(String username, String password, Resourcepart resource) throws XMPPException, SmackException, IOException, InterruptedException { // Authenticate using SASL - saslAuthentication.authenticate(username, password); + saslAuthentication.authenticate(username, password, config.getAuthzid()); bindResourceAndEstablishSession(resource); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index f4b781a56..b4bc0e4e1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -31,6 +31,7 @@ import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.stringprep.XmppStringprepException; @@ -81,6 +82,11 @@ public abstract class ConnectionConfiguration { private final String password; private final Resourcepart resource; + /** + * The optional SASL authorization identity (see RFC 6120 § 6.3.8). + */ + private final EntityBareJid authzid; + /** * Initial presence as of RFC 6121 § 4.2 * @see RFC 6121 § 4.2 Initial Presence @@ -110,6 +116,7 @@ public abstract class ConnectionConfiguration { private final Set enabledSaslMechanisms; protected ConnectionConfiguration(Builder builder) { + authzid = builder.authzid; username = builder.username; password = builder.password; callbackHandler = builder.callbackHandler; @@ -361,6 +368,17 @@ public abstract class ConnectionConfiguration { return resource; } + /** + * Returns the optional XMPP address to be requested as the SASL authorization identity. + * + * @return the authorization identifier. + * @see RFC 6120 § 6.3.8. Authorization Identity + * @since 4.2 + */ + public EntityBareJid getAuthzid() { + return authzid; + } + /** * Returns true if an available presence should be sent when logging in while reconnecting. * @@ -425,6 +443,7 @@ public abstract class ConnectionConfiguration { private String[] enabledSSLProtocols; private String[] enabledSSLCiphers; private HostnameVerifier hostnameVerifier; + private EntityBareJid authzid; private CharSequence username; private String password; private Resourcepart resource; @@ -803,6 +822,25 @@ public abstract class ConnectionConfiguration { return getThis(); } + /** + * Set the XMPP address to be used as authorization identity. + *

+ * In XMPP, authorization identities are bare jids. In general, callers should allow the server to select the + * authorization identifier automatically, and not call this. Note that setting the authzid does not set the XMPP + * service domain, which should typically match. + * Calling this will also SASL CRAM, since this mechanism does not support authzid. + *

+ * + * @param authzid The BareJid to be requested as the authorization identifier. + * @return a reference to this builder. + * @see RFC 6120 § 6.3.8. Authorization Identity + * @since 4.2 + */ + public B setAuthzid(EntityBareJid authzid) { + this.authzid = authzid; + return getThis(); + } + public abstract C build(); protected abstract B getThis(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java index 9c22cce01..faf90a291 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.EntityBareJid; import javax.security.auth.callback.CallbackHandler; @@ -171,26 +172,27 @@ public final class SASLAuthentication { * * @param username the username that is authenticating with the server. * @param password the password to send to the server. - * @throws XMPPErrorException - * @throws SASLErrorException - * @throws IOException - * @throws SmackException - * @throws InterruptedException + * @param authzid the authorization identifier (typically null). + * @throws XMPPErrorException + * @throws SASLErrorException + * @throws IOException + * @throws SmackException + * @throws InterruptedException */ - public void authenticate(String username, String password) + public void authenticate(String username, String password, EntityBareJid authzid) throws XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException { - currentMechanism = selectMechanism(); + currentMechanism = selectMechanism(authzid); final CallbackHandler callbackHandler = configuration.getCallbackHandler(); final String host = connection.getHost(); final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain(); synchronized (this) { if (callbackHandler != null) { - currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler); + currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid); } else { - currentMechanism.authenticate(username, host, xmppServiceDomain, password); + currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid); } final long deadline = System.currentTimeMillis() + connection.getPacketReplyTimeout(); while (!authenticationSuccessful && saslException == null) { @@ -312,7 +314,7 @@ public final class SASLAuthentication { return lastUsedMech.getName(); } - private SASLMechanism selectMechanism() throws SmackException { + private SASLMechanism selectMechanism(EntityBareJid authzid) throws SmackException { Iterator it = REGISTERED_MECHANISMS.iterator(); final List serverMechanisms = getServerMechanisms(); if (serverMechanisms.isEmpty()) { @@ -330,6 +332,12 @@ public final class SASLAuthentication { if (!configuration.isEnabledSaslMechanism(mechanismName)) { continue; } + if (authzid != null) { + if (!mechanism.authzidSupported()) { + LOGGER.fine("Skipping " + mechanism + " because authzid is required by not supported by this SASL mechanism"); + continue; + } + } if (serverMechanisms.contains(mechanismName)) { // Create a new instance of the SASLMechanism for every authentication attempt. return mechanism.instanceForAuthentication(connection); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java index 9dd01c91d..198985e49 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.util.StringTransformer; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.Base64; import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.EntityBareJid; import javax.security.auth.callback.CallbackHandler; @@ -34,9 +35,9 @@ import javax.security.auth.callback.CallbackHandler; *
  • {@link #getName()} -- returns the common name of the SASL mechanism.
  • * * Subclasses will likely want to implement their own versions of these methods: - *
  • {@link #authenticate(String, String, DomainBareJid, String)} -- Initiate authentication stanza using the + *
  • {@link #authenticate(String, String, DomainBareJid, String, EntityBareJid)} -- Initiate authentication stanza using the * deprecated method.
  • - *
  • {@link #authenticate(String, DomainBareJid, CallbackHandler)} -- Initiate authentication stanza + *
  • {@link #authenticate(String, DomainBareJid, CallbackHandler, EntityBareJid)} -- Initiate authentication stanza * using the CallbackHandler method.
  • *
  • {@link #challengeReceived(String, boolean)} -- Handle a challenge from the server.
  • * @@ -102,6 +103,12 @@ public abstract class SASLMechanism implements Comparable { */ protected String authenticationId; + /** + * The authorization identifier (authzid). + * This is always a bare Jid, but can be null. + */ + protected EntityBareJid authorizationId; + /** * The name of the XMPP service */ @@ -116,7 +123,7 @@ public abstract class SASLMechanism implements Comparable { /** * Builds and sends the auth stanza to the server. Note that this method of * authentication is not recommended, since it is very inflexible. Use - * {@link #authenticate(String, DomainBareJid, CallbackHandler)} whenever possible. + * {@link #authenticate(String, DomainBareJid, CallbackHandler, EntityBareJid)} whenever possible. * * Explanation of auth stanza: * @@ -149,24 +156,27 @@ public abstract class SASLMechanism implements Comparable { * called "mail3.example.com"; it's "serv-name" would be "example.com", it's "host" would be * "mail3.example.com". If the service is not replicated, or the serv-name is identical to * the host, then the serv-name component MUST be omitted - * - * digest-uri verification is needed for ejabberd 2.0.3 and higher - * + * + * digest-uri verification is needed for ejabberd 2.0.3 and higher + * * @param username the username of the user being authenticated. * @param host the hostname where the user account resides. * @param serviceName the xmpp service location - used by the SASL client in digest-uri creation * serviceName format is: host [ "/" serv-name ] as per RFC-2831 * @param password the password for this account. + * @param authzid the optional authorization identity. * @throws SmackException If a network error occurs while authenticating. * @throws NotConnectedException * @throws InterruptedException */ - public final void authenticate(String username, String host, DomainBareJid serviceName, String password) + public final void authenticate(String username, String host, DomainBareJid serviceName, String password, EntityBareJid authzid) throws SmackException, NotConnectedException, InterruptedException { this.authenticationId = username; this.host = host; this.serviceName = serviceName; this.password = password; + this.authorizationId = authzid; + assert(authorizationId == null || authzidSupported()); authenticateInternal(); authenticate(); } @@ -184,14 +194,17 @@ public abstract class SASLMechanism implements Comparable { * @param host the hostname where the user account resides. * @param serviceName the xmpp service location * @param cbh the CallbackHandler to obtain user information. + * @param authzid the optional authorization identity. * @throws SmackException * @throws NotConnectedException * @throws InterruptedException */ - public void authenticate(String host, DomainBareJid serviceName, CallbackHandler cbh) + public void authenticate(String host, DomainBareJid serviceName, CallbackHandler cbh, EntityBareJid authzid) throws SmackException, NotConnectedException, InterruptedException { this.host = host; this.serviceName = serviceName; + this.authorizationId = authzid; + assert(authorizationId == null || authzidSupported()); authenticateInternal(cbh); authenticate(); } @@ -283,6 +296,10 @@ public abstract class SASLMechanism implements Comparable { return saslMechansim; } + public boolean authzidSupported() { + return false; + } + protected abstract SASLMechanism newInstance(); protected static byte[] toBytes(String string) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java index 217ace4e9..0a6d6d6f1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1Mechanism.java @@ -38,7 +38,6 @@ public class SCRAMSHA1Mechanism extends SASLMechanism { public static final String NAME = "SCRAM-SHA-1"; private static final int RANDOM_ASCII_BYTE_COUNT = 32; - private static final String DEFAULT_GS2_HEADER = "n,,"; private static final byte[] CLIENT_KEY_BYTES = toBytes("Client Key"); private static final byte[] SERVER_KEY_BYTES = toBytes("Server Key"); private static final byte[] ONE = new byte[] { 0, 0, 0, 1 }; @@ -77,7 +76,7 @@ public class SCRAMSHA1Mechanism extends SASLMechanism { clientRandomAscii = getRandomAscii(); String saslPrepedAuthcId = saslPrep(authenticationId); clientFirstMessageBare = "n=" + escape(saslPrepedAuthcId) + ",r=" + clientRandomAscii; - String clientFirstMessage = DEFAULT_GS2_HEADER + clientFirstMessageBare; + String clientFirstMessage = getGS2Header() + clientFirstMessageBare; state = State.AUTH_TEXT_SENT; return toBytes(clientFirstMessage); } @@ -105,6 +104,11 @@ public class SCRAMSHA1Mechanism extends SASLMechanism { } } + @Override + public boolean authzidSupported() { + return true; + } + @Override protected byte[] evaluateChallenge(byte[] challenge) throws SmackException { final String challengeString = new String(challenge); @@ -148,7 +152,7 @@ public class SCRAMSHA1Mechanism extends SASLMechanism { // Parsing and error checking is done, we can now begin to calculate the values // First the client-final-message-without-proof - String clientFinalMessageWithoutProof = "c=" + Base64.encode(DEFAULT_GS2_HEADER) + ",r=" + rvalue; + String clientFinalMessageWithoutProof = "c=" + Base64.encode(getGS2Header()) + ",r=" + rvalue; // AuthMessage := client-first-message-bare + "," + server-first-message + "," + // client-final-message-without-proof @@ -209,6 +213,14 @@ public class SCRAMSHA1Mechanism extends SASLMechanism { return null; } + private final String getGS2Header() { + String authzidPortion = ""; + if (authorizationId != null) { + authzidPortion = "a=" + authorizationId; + } + return "n," + authzidPortion + ","; + } + private static Map parseAttributes(String string) throws SmackException { if (string.length() == 0) { return Collections.emptyMap(); diff --git a/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java b/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java index 19e3936c7..6cfe83e51 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/sasl/DigestMd5SaslTest.java @@ -27,6 +27,7 @@ import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.util.StringUtils; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; +import org.jxmpp.jid.EntityBareJid; public class DigestMd5SaslTest extends AbstractSaslTest { @@ -37,9 +38,12 @@ public class DigestMd5SaslTest extends AbstractSaslTest { super(saslMechanism); } - protected void runTest() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { - saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret"); - + protected void runTest(boolean useAuthzid) throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { + EntityBareJid authzid = null; + if (useAuthzid) { + authzid = JidCreate.entityBareFrom("shazbat@xmpp.org"); + } + saslMechanism.authenticate("florian", "irrelevant", JidCreate.domainBareFrom("xmpp.org"), "secret", authzid); byte[] response = saslMechanism.evaluateChallenge(challengeBytes); String responseString = new String(response); String[] responseParts = responseString.split(","); @@ -51,6 +55,11 @@ public class DigestMd5SaslTest extends AbstractSaslTest { String value = keyValue[1].replace("\"", ""); responsePairs.put(key, value); } + if (useAuthzid) { + assertMapValue("authzid", "shazbat@xmpp.org", responsePairs); + } else { + assert(!responsePairs.containsKey("authzid")); + } assertMapValue("username", "florian", responsePairs); assertMapValue("realm", "xmpp.org", responsePairs); assertMapValue("digest-uri", "xmpp/xmpp.org", responsePairs); @@ -58,6 +67,6 @@ public class DigestMd5SaslTest extends AbstractSaslTest { } private static void assertMapValue(String key, String value, Map map) { - assertEquals(map.get(key), value); + assertEquals(value, map.get(key)); } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java b/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java index 93c0cb79c..4903b9354 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/sasl/core/SCRAMSHA1MechanismTest.java @@ -48,7 +48,7 @@ public class SCRAMSHA1MechanismTest extends SmackTestSuite { } }; - mech.authenticate(USERNAME, "unusedFoo", JidTestUtil.DOMAIN_BARE_JID_1, PASSWORD); + mech.authenticate(USERNAME, "unusedFoo", JidTestUtil.DOMAIN_BARE_JID_1, PASSWORD, null); AuthMechanism authMechanism = con.getSentPacket(); assertEquals(SCRAMSHA1Mechanism.NAME, authMechanism.getMechanism()); assertEquals(CLIENT_FIRST_MESSAGE, saslLayerString(authMechanism.getAuthenticationText())); diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java index d7038e6ea..39d7dc430 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Mechanism.java @@ -25,6 +25,11 @@ public class SASLDigestMD5Mechanism extends SASLJavaXMechanism { public static final String NAME = DIGESTMD5; + @Override + public boolean authzidSupported() { + return true; + } + public String getName() { return NAME; } diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java index eff4697c4..f36bfd383 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java @@ -46,6 +46,11 @@ public class SASLExternalMechanism extends SASLJavaXMechanism { public static final String NAME = EXTERNAL; + @Override + public boolean authzidSupported() { + return true; + } + @Override public String getName() { return EXTERNAL; diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java index 2a95dd8eb..b82ac46a9 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLGSSAPIMechanism.java @@ -34,6 +34,11 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism { System.setProperty("java.security.auth.login.config","gss.conf"); } + @Override + public boolean authzidSupported() { + return true; + } + @Override public String getName() { return NAME; diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java index 8a2848a3f..d9692b794 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLJavaXMechanism.java @@ -53,8 +53,12 @@ public abstract class SASLJavaXMechanism extends SASLMechanism { throws SmackException { String[] mechanisms = { getName() }; Map props = getSaslProps(); + String authzid = null; + if (authorizationId != null) { + authzid = authorizationId.toString(); + } try { - sc = Sasl.createSaslClient(mechanisms, null, "xmpp", getServerName().toString(), props, + sc = Sasl.createSaslClient(mechanisms, authzid, "xmpp", getServerName().toString(), props, new CallbackHandler() { @Override public void handle(Callback[] callbacks) throws IOException, diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java index 83ee3e437..278d60f1d 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLPlainMechanism.java @@ -29,6 +29,11 @@ public class SASLPlainMechanism extends SASLJavaXMechanism { return NAME; } + @Override + public boolean authzidSupported() { + return true; + } + @Override public int getPriority() { return 400; diff --git a/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java b/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java index 125aa3699..bb2aac7c5 100644 --- a/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java +++ b/smack-sasl-javax/src/test/java/org/jivesoftware/smack/sasl/javax/SASLDigestMD5Test.java @@ -30,6 +30,11 @@ public class SASLDigestMD5Test extends DigestMd5SaslTest { @Test public void testDigestMD5() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { - runTest(); + runTest(false); + } + + @Test + public void testDigestMD5Authzid() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { + runTest(true); } } diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java index 1c0a8370a..e72ba840e 100644 --- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java +++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java @@ -83,6 +83,11 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { return new SASLDigestMD5Mechanism(); } + @Override + public boolean authzidSupported() { + return true; + } + @Override public void checkIfSuccessfulOrThrow() throws SmackException { @@ -141,7 +146,14 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { String responseValue = calcResponse(DigestType.ClientResponse); // @formatter:off // See RFC 2831 2.1.2 digest-response + String authzid; + if (authorizationId == null) { + authzid = ""; + } else { + authzid = ",authzid=\"" + authorizationId + '"'; + } String saslString = "username=\"" + authenticationId + '"' + + authzid + ",realm=\"" + serviceName + '"' + ",nonce=\"" + nonce + '"' + ",cnonce=\"" + cnonce + '"' diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java index 3f6670f49..3869fc7f2 100644 --- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java +++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLExternalMechanism.java @@ -40,6 +40,10 @@ public class SASLExternalMechanism extends SASLMechanism { @Override protected byte[] getAuthenticationText() throws SmackException { + if (authorizationId != null) { + return toBytes(authorizationId.toString()); + } + if (StringUtils.isNullOrEmpty(authenticationId)) { return null; } @@ -67,4 +71,9 @@ public class SASLExternalMechanism extends SASLMechanism { // No check performed } + @Override + public boolean authzidSupported() { + return true; + } + } diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java index 770c29040..5bbf1b35c 100644 --- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java +++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLPlainMechanism.java @@ -34,7 +34,13 @@ public class SASLPlainMechanism extends SASLMechanism { @Override protected byte[] getAuthenticationText() throws SmackException { // concatenate and encode username (authcid) and password - byte[] authcid = toBytes('\u0000' + authenticationId); + String authzid; + if (authorizationId == null) { + authzid = ""; + } else { + authzid = authorizationId.toString(); + } + byte[] authcid = toBytes(authzid + '\u0000' + authenticationId); byte[] passw = toBytes('\u0000' + password); return ByteUtils.concact(authcid, passw); @@ -59,4 +65,9 @@ public class SASLPlainMechanism extends SASLMechanism { public void checkIfSuccessfulOrThrow() throws SmackException { // No check performed } + + @Override + public boolean authzidSupported() { + return true; + } } diff --git a/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java b/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java index 75f1c8d03..c68eb346c 100644 --- a/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java +++ b/smack-sasl-provided/src/test/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Test.java @@ -29,6 +29,11 @@ public class SASLDigestMD5Test extends DigestMd5SaslTest { @Test public void testDigestMD5() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { - runTest(); + runTest(false); + } + + @Test + public void testDigestMD5Authzid() throws NotConnectedException, SmackException, InterruptedException, XmppStringprepException { + runTest(true); } } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 664ce777f..4cc705fc6 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -376,7 +376,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { protected synchronized void loginInternal(String username, String password, Resourcepart resource) throws XMPPException, SmackException, IOException, InterruptedException { // Authenticate using SASL - saslAuthentication.authenticate(username, password); + saslAuthentication.authenticate(username, password, config.getAuthzid()); // If compression is enabled then request the server to use stream compression. XEP-170 // recommends to perform stream compression before resource binding.