From 9354e4fb74b0b331b64aff54d127136d802dd085 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 3 May 2015 22:00:41 +0200 Subject: [PATCH] Rework and improve authentication API and internals Add - performSaslAnonymousAuthentication() - performSaslExternalAuthentication(SSLContext) - addEnabledSaslMechanism(String) - addEnabledSaslMechanisms(Collection) to ConnectionConfiguration.Builder. Instead of providing a special API call for anonymous authentication, Smack now has a configuration builder method to set anonymous/external authentication. This also removes a lot of duplicate code within Smack. Also move SASLAnonymous into o.j.smack.sasl.core. Fixes SMACK-629. --- .../smack/bosh/XMPPBOSHConnection.java | 34 +--- .../smack/AbstractXMPPConnection.java | 27 +-- .../smack/ConnectionConfiguration.java | 125 ++++++++++++- .../smack/SASLAuthentication.java | 173 ++++++------------ .../smack/SmackInitialization.java | 2 + .../smack/sasl/SASLMechanism.java | 6 + .../smack/sasl/{ => core}/SASLAnonymous.java | 3 +- .../smack/util/CollectionUtil.java | 33 ++++ .../jivesoftware/smack/DummyConnection.java | 13 +- .../smack/tcp/XMPPTCPConnection.java | 38 +--- 10 files changed, 240 insertions(+), 214 deletions(-) rename smack-core/src/main/java/org/jivesoftware/smack/sasl/{ => core}/SASLAnonymous.java (94%) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java 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 4151ef086..ae0dcc10b 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 @@ -210,42 +210,16 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { } @Override - protected void loginNonAnonymously(String username, String password, String resource) - throws XMPPException, SmackException, IOException, InterruptedException { - if (saslAuthentication.hasNonAnonymousAuthentication()) { - // Authenticate using SASL - if (password != null) { - saslAuthentication.authenticate(username, password, resource); - } else { - saslAuthentication.authenticate(resource, config.getCallbackHandler()); - } - } else { - throw new SmackException("No non-anonymous SASL authentication mechanism available"); - } + protected void loginInternal(String username, String password, String resource) throws XMPPException, + SmackException, IOException, InterruptedException { + // Authenticate using SASL + saslAuthentication.authenticate(username, password); bindResourceAndEstablishSession(resource); afterSuccessfulLogin(false); } - @Override - protected void loginAnonymously() throws XMPPException, SmackException, IOException, InterruptedException { - // Wait with SASL auth until the SASL mechanisms have been received - saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); - - if (saslAuthentication.hasAnonymousAuthentication()) { - saslAuthentication.authenticateAnonymously(); - } - else { - // Authenticate using Non-SASL - throw new SmackException("No anonymous SASL authentication mechanism available"); - } - - bindResourceAndEstablishSession(null); - - afterSuccessfulLogin(false); - } - @Override public void send(PlainStreamElement element) throws NotConnectedException { if (done) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index b86343028..d6d14aae3 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -204,7 +204,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { /** * The SASLAuthentication manager that is responsible for authenticating with the server. */ - protected SASLAuthentication saslAuthentication = new SASLAuthentication(this); + protected final SASLAuthentication saslAuthentication; /** * A number to uniquely identify connections that are created. This is distinct from the @@ -291,6 +291,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * @param configuration The configuration which is used to establish the connection. */ protected AbstractXMPPConnection(ConnectionConfiguration configuration) { + saslAuthentication = new SASLAuthentication(this, configuration); config = configuration; // Notify listeners that a new connection has been established for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) { @@ -397,18 +398,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * @throws InterruptedException */ public synchronized void login() throws XMPPException, SmackException, IOException, InterruptedException { - if (isAnonymous()) { - throwNotConnectedExceptionIfAppropriate(); - throwAlreadyLoggedInExceptionIfAppropriate(); - loginAnonymously(); - } else { - // The previously used username, password and resource take over precedence over the - // ones from the connection configuration - CharSequence username = usedUsername != null ? usedUsername : config.getUsername(); - String password = usedPassword != null ? usedPassword : config.getPassword(); - String resource = usedResource != null ? usedResource : config.getResource(); - login(username, password, resource); - } + // The previously used username, password and resource take over precedence over the + // ones from the connection configuration + CharSequence username = usedUsername != null ? usedUsername : config.getUsername(); + String password = usedPassword != null ? usedPassword : config.getPassword(); + String resource = usedResource != null ? usedResource : config.getResource(); + login(username, password, resource); } /** @@ -451,14 +446,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { usedUsername = username != null ? username.toString() : null; usedPassword = password; usedResource = resource; - loginNonAnonymously(usedUsername, usedPassword, usedResource); + loginInternal(usedUsername, usedPassword, usedResource); } - protected abstract void loginNonAnonymously(String username, String password, String resource) + protected abstract void loginInternal(String username, String password, String resource) throws XMPPException, SmackException, IOException, InterruptedException; - protected abstract void loginAnonymously() throws XMPPException, SmackException, IOException, InterruptedException; - @Override public final boolean isConnected() { return connected; 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 3131dc46e..92779be5d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -17,8 +17,19 @@ package org.jivesoftware.smack; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import org.jivesoftware.smack.packet.Session; import org.jivesoftware.smack.proxy.ProxyInfo; +import org.jivesoftware.smack.sasl.SASLMechanism; +import org.jivesoftware.smack.sasl.core.SASLAnonymous; +import org.jivesoftware.smack.util.CollectionUtil; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.StringUtils; import org.jxmpp.jid.DomainBareJid; import javax.net.SocketFactory; @@ -93,6 +104,8 @@ public abstract class ConnectionConfiguration { protected final boolean allowNullOrEmptyUsername; + private final Set enabledSaslMechanisms; + protected ConnectionConfiguration(Builder builder) { username = builder.username; password = builder.password; @@ -130,6 +143,10 @@ public abstract class ConnectionConfiguration { legacySessionDisabled = builder.legacySessionDisabled; debuggerEnabled = builder.debuggerEnabled; allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername; + enabledSaslMechanisms = builder.enabledSaslMechanisms; + + // If the enabledSaslmechanisms are set, then they must not be empty + assert(enabledSaslMechanisms != null ? !enabledSaslMechanisms.isEmpty() : true); } /** @@ -350,6 +367,24 @@ public abstract class ConnectionConfiguration { return false; } + /** + * Check if the given SASL mechansism is enabled in this connection configuration. + * + * @param saslMechanism + * @return true if the given SASL mechanism is enabled, false otherwise. + */ + public boolean isEnabledSaslMechanism(String saslMechanism) { + // If enabledSaslMechanisms is not set, then all mechanisms are enabled per default + if (enabledSaslMechanisms == null) { + return true; + } + return enabledSaslMechanisms.contains(saslMechanism); + } + + public Set getEnabledSaslMechanisms() { + return Collections.unmodifiableSet(enabledSaslMechanisms); + } + /** * A builder for XMPP connection configurations. *

@@ -387,6 +422,8 @@ public abstract class ConnectionConfiguration { private String host; private int port = 5222; private boolean allowEmptyOrNullUsername = false; + private boolean saslMechanismsSealed; + private Set enabledSaslMechanisms; protected Builder() { } @@ -518,7 +555,7 @@ public abstract class ConnectionConfiguration { * @return a reference to this builder. */ public B setCustomSSLContext(SSLContext context) { - this.customSSLContext = context; + this.customSSLContext = Objects.requireNonNull(context, "The SSLContext must not be null"); return getThis(); } @@ -628,6 +665,92 @@ public abstract class ConnectionConfiguration { return getThis(); } + /** + * Perform anonymous authentication using SASL ANONYMOUS. Your XMPP service must support this authentication + * mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "ANONYMOUS" as argument. + * + * @return a reference to this builder. + */ + public B performSaslAnonymousAuthentication() { + if (!SASLAuthentication.isSaslMechanismRegistered(SASLAnonymous.NAME)) { + throw new IllegalArgumentException("SASL " + SASLAnonymous.NAME + " is not registered"); + } + throwIfEnabledSaslMechanismsSet(); + + allowEmptyOrNullUsernames(); + addEnabledSaslMechanism(SASLAnonymous.NAME); + saslMechanismsSealed = true; + return getThis(); + } + + /** + * Perform authentication using SASL EXTERNAL. Your XMPP service must support this + * authentication mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "EXTERNAL" as + * argument. It also calls {@link #allowEmptyOrNullUsernames()} and {@link #setSecurityMode(SecurityMode)} to + * {@link SecurityMode#required}. + * + * @return a reference to this builder. + */ + public B performSaslExternalAuthentication(SSLContext sslContext) { + if (!SASLAuthentication.isSaslMechanismRegistered(SASLMechanism.EXTERNAL)) { + throw new IllegalArgumentException("SASL " + SASLMechanism.EXTERNAL + " is not registered"); + } + setCustomSSLContext(sslContext); + throwIfEnabledSaslMechanismsSet(); + + allowEmptyOrNullUsernames(); + setSecurityMode(SecurityMode.required); + addEnabledSaslMechanism(SASLMechanism.EXTERNAL); + saslMechanismsSealed = true; + return getThis(); + } + + private void throwIfEnabledSaslMechanismsSet() { + if (enabledSaslMechanisms != null) { + throw new IllegalStateException("Enabled SASL mechanisms found"); + } + } + + /** + * Add the given mechanism to the enabled ones. See {@link #addEnabledSaslMechanism(Collection)} for a discussion about enabled SASL mechanisms. + * + * @param saslMechanism the name of the mechanism to enable. + * @return a reference to this builder. + */ + public B addEnabledSaslMechanism(String saslMechanism) { + return addEnabledSaslMechanism(Arrays.asList(StringUtils.requireNotNullOrEmpty(saslMechanism, + "saslMechanism must not be null or empty"))); + } + + /** + * Enable the given SASL mechanisms. If you never add a mechanism to the set of enabled ones, all mechanisms + * known to Smack will be enabled. Only explicitly enable particular SASL mechanisms if you want to limit + * the used mechanisms to the enabled ones. + * + * @param saslMechanisms a collection of names of mechanisms to enable. + * @return a reference to this builder. + */ + public B addEnabledSaslMechanism(Collection saslMechanisms) { + if (saslMechanismsSealed) { + throw new IllegalStateException("The enabled SASL mechanisms are sealed, you can not add new ones"); + } + CollectionUtil.requireNotEmpty(saslMechanisms, "saslMechanisms"); + Set blacklistedMechanisms = SASLAuthentication.getBlacklistedSASLMechanisms(); + for (String mechanism : saslMechanisms) { + if (!SASLAuthentication.isSaslMechanismRegistered(mechanism)) { + throw new IllegalArgumentException("SASL " + mechanism + " is not avaiable. Consider registering it with Smack"); + } + if (blacklistedMechanisms.contains(mechanism)) { + throw new IllegalArgumentException("SALS " + mechanism + " is blacklisted."); + } + } + if (enabledSaslMechanisms == null) { + enabledSaslMechanisms = new HashSet<>(saslMechanisms.size()); + } + enabledSaslMechanisms.addAll(saslMechanisms); + 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 fff9b8fe2..d23b6661f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -20,11 +20,11 @@ package org.jivesoftware.smack; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Mechanisms; -import org.jivesoftware.smack.sasl.SASLAnonymous; import org.jivesoftware.smack.sasl.SASLErrorException; 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 javax.security.auth.callback.CallbackHandler; @@ -91,6 +91,17 @@ public class SASLAuthentication { return answer; } + public static boolean isSaslMechanismRegistered(String saslMechanism) { + synchronized (REGISTERED_MECHANISMS) { + for (SASLMechanism mechanism : REGISTERED_MECHANISMS) { + if (mechanism.getName().equals(saslMechanism)) { + return true; + } + } + } + return false; + } + /** * Unregister a SASLMechanism by it's full class name. For example * "org.jivesoftware.smack.sasl.javax.SASLCramMD5Mechanism". @@ -131,6 +142,7 @@ public class SASLAuthentication { } private final AbstractXMPPConnection connection; + private final ConnectionConfiguration configuration; private SASLMechanism currentMechanism = null; /** @@ -143,68 +155,12 @@ public class SASLAuthentication { */ private Exception saslException; - SASLAuthentication(AbstractXMPPConnection connection) { + SASLAuthentication(AbstractXMPPConnection connection, ConnectionConfiguration configuration) { + this.configuration = configuration; this.connection = connection; this.init(); } - /** - * Returns true if the server offered ANONYMOUS SASL as a way to authenticate users. - * - * @return true if the server offered ANONYMOUS SASL as a way to authenticate users. - */ - public boolean hasAnonymousAuthentication() { - return serverMechanisms().contains("ANONYMOUS"); - } - - /** - * Returns true if the server offered SASL authentication besides ANONYMOUS SASL. - * - * @return true if the server offered SASL authentication besides ANONYMOUS SASL. - */ - public boolean hasNonAnonymousAuthentication() { - return !serverMechanisms().isEmpty() && (serverMechanisms().size() != 1 || !hasAnonymousAuthentication()); - } - - /** - * Performs SASL authentication of the specified user. If SASL authentication was successful - * then resource binding and session establishment will be performed. This method will return - * the full JID provided by the server while binding a resource to the connection.

- * - * The server may assign a full JID with a username or resource different than the requested - * by this method. - * - * @param resource the desired resource. - * @param cbh the CallbackHandler used to get information from the user - * @throws IOException - * @throws XMPPErrorException - * @throws SASLErrorException - * @throws SmackException - * @throws InterruptedException - */ - public void authenticate(String resource, CallbackHandler cbh) throws IOException, - XMPPErrorException, SASLErrorException, SmackException, InterruptedException { - SASLMechanism selectedMechanism = selectMechanism(); - if (selectedMechanism != null) { - currentMechanism = selectedMechanism; - synchronized (this) { - currentMechanism.authenticate(connection.getHost(), connection.getServiceName(), cbh); - // Wait until SASL negotiation finishes - wait(connection.getPacketReplyTimeout()); - } - - maybeThrowException(); - - if (!authenticationSuccessful) { - throw NoResponseException.newWith(connection); - } - } - else { - throw new SmackException( - "SASL Authentication failed. No known authentication mechanisims."); - } - } - /** * Performs SASL authentication of the specified user. If SASL authentication was successful * then resource binding and session establishment will be performed. This method will return @@ -215,70 +171,31 @@ public class SASLAuthentication { * * @param username the username that is authenticating with the server. * @param password the password to send to the server. - * @param resource the desired resource. * @throws XMPPErrorException * @throws SASLErrorException * @throws IOException * @throws SmackException * @throws InterruptedException */ - public void authenticate(String username, String password, String resource) + public void authenticate(String username, String password) throws XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException { - SASLMechanism selectedMechanism = selectMechanism(); - if (selectedMechanism != null) { - currentMechanism = selectedMechanism; + currentMechanism = selectMechanism(); + final CallbackHandler callbackHandler = configuration.getCallbackHandler(); + final String host = connection.getHost(); + final DomainBareJid xmppDomain = connection.getServiceName(); - synchronized (this) { - currentMechanism.authenticate(username, connection.getHost(), - connection.getServiceName(), password); - // Wait until SASL negotiation finishes - wait(connection.getPacketReplyTimeout()); - } - - maybeThrowException(); - - if (!authenticationSuccessful) { - throw NoResponseException.newWith(connection); - } - } - else { - throw new SmackException( - "SASL Authentication failed. No known authentication mechanisims."); - } - } - - /** - * Performs ANONYMOUS SASL authentication. If SASL authentication was successful - * then resource binding and session establishment will be performed. This method will return - * the full JID provided by the server while binding a resource to the connection.

- * - * The server will assign a full JID with a randomly generated resource and possibly with - * no username. - * - * @throws SASLErrorException - * @throws XMPPErrorException if an error occures while authenticating. - * @throws SmackException if there was no response from the server. - * @throws InterruptedException - */ - public void authenticateAnonymously() throws SASLErrorException, - SmackException, XMPPErrorException, InterruptedException { - currentMechanism = (new SASLAnonymous()).instanceForAuthentication(connection); - - // Wait until SASL negotiation finishes synchronized (this) { - currentMechanism.authenticate(null, null, null, ""); + if (callbackHandler != null) { + currentMechanism.authenticate(host, xmppDomain, callbackHandler); + } + else { + currentMechanism.authenticate(username, host, xmppDomain, password); + } + // Wait until SASL negotiation finishes wait(connection.getPacketReplyTimeout()); } - maybeThrowException(); - - if (!authenticationSuccessful) { - throw NoResponseException.newWith(connection); - } - } - - private void maybeThrowException() throws SmackException, SASLErrorException { if (saslException != null){ if (saslException instanceof SmackException) { throw (SmackException) saslException; @@ -288,6 +205,10 @@ public class SASLAuthentication { throw new IllegalStateException("Unexpected exception type" , saslException); } } + + if (!authenticationSuccessful) { + throw NoResponseException.newWith(connection); + } } /** @@ -378,10 +299,12 @@ public class SASLAuthentication { saslException = null; } - private SASLMechanism selectMechanism() { - // Locate the SASLMechanism to use - SASLMechanism selectedMechanism = null; + private SASLMechanism selectMechanism() throws SmackException { Iterator it = REGISTERED_MECHANISMS.iterator(); + final List serverMechanisms = getServerMechanisms(); + if (serverMechanisms.isEmpty()) { + LOGGER.warning("Server did not report any SASL mechanisms"); + } // Iterate in SASL Priority order over registered mechanisms while (it.hasNext()) { SASLMechanism mechanism = it.next(); @@ -391,19 +314,31 @@ public class SASLAuthentication { continue; } } - if (serverMechanisms().contains(mechanismName)) { + if (!configuration.isEnabledSaslMechanism(mechanismName)) { + continue; + } + if (serverMechanisms.contains(mechanismName)) { // Create a new instance of the SASLMechanism for every authentication attempt. - selectedMechanism = mechanism.instanceForAuthentication(connection); - break; + return mechanism.instanceForAuthentication(connection); } } - return selectedMechanism; + + synchronized (BLACKLISTED_MECHANISMS) { + // @formatter:off + throw new SmackException( + "No supported and enabled SASL Mechanism provided by server. " + + "Server announced mechanisms: " + serverMechanisms + ". " + + "Registerd SASL mechanisms with Smack: " + REGISTERED_MECHANISMS + ". " + + "Enabled SASL mechansisms for this connection: " + configuration.getEnabledSaslMechanisms() + ". " + + "Blacklisted SASL mechanisms: " + BLACKLISTED_MECHANISMS + '.' + ); + // @formatter;on + } } - private List serverMechanisms() { + private List getServerMechanisms() { Mechanisms mechanisms = connection.getFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE); if (mechanisms == null) { - LOGGER.warning("Server did not report any SASL mechanisms"); return Collections.emptyList(); } return mechanisms.getMechanisms(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java index 3bef58be2..a674c3629 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java @@ -32,6 +32,7 @@ import org.jivesoftware.smack.initializer.SmackInitializer; import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.provider.BindIQProvider; import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.sasl.core.SASLAnonymous; import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism; import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism; import org.jivesoftware.smack.util.FileUtils; @@ -137,6 +138,7 @@ public final class SmackInitialization { SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism()); SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism()); + SASLAuthentication.registerSASLMechanism(new SASLAnonymous()); ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider()); 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 b476186fa..ab2ac3ea8 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 @@ -171,6 +171,9 @@ public abstract class SASLMechanism implements Comparable { authenticate(); } + /** + * @throws SmackException + */ protected void authenticateInternal() throws SmackException { } @@ -248,6 +251,9 @@ public abstract class SASLMechanism implements Comparable { connection.send(responseStanza); } + /** + * @throws SmackException + */ protected byte[] evaluateChallenge(byte[] challenge) throws SmackException { return null; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java similarity index 94% rename from smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java rename to smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java index 94194cf41..7f99b5ef8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLAnonymous.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLAnonymous.java @@ -14,9 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.sasl; +package org.jivesoftware.smack.sasl.core; import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.sasl.SASLMechanism; import javax.security.auth.callback.CallbackHandler; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java new file mode 100644 index 000000000..6b3aa9d87 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2015 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util; + +import java.util.Collection; + +public class CollectionUtil { + + public static Collection requireNotEmpty(Collection collection, String collectionName) { + if (collection == null) { + throw new NullPointerException(collectionName + " must not be null."); + } + if (collection.isEmpty()) { + throw new IllegalArgumentException(collectionName + " must not be empty."); + } + return collection; + } + +} diff --git a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java index 844fc0d5c..94238c971 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java @@ -116,23 +116,12 @@ public class DummyConnection extends AbstractXMPPConnection { } @Override - protected void loginNonAnonymously(String username, String password, String resource) + protected void loginInternal(String username, String password, String resource) throws XMPPException { user = getUserJid(); authenticated = true; } - @Override - public void loginAnonymously() throws XMPPException { - if (!isConnected()) { - throw new IllegalStateException("Not connected to server."); - } - if (isAuthenticated()) { - throw new IllegalStateException("Already logged in to server."); - } - authenticated = true; - } - @Override public void send(PlainStreamElement element) { queue.add(element); 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 9507d7dfa..1db54dfd1 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 @@ -361,18 +361,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } @Override - protected synchronized void loginNonAnonymously(String username, String password, String resource) throws XMPPException, SmackException, IOException, InterruptedException { - if (saslAuthentication.hasNonAnonymousAuthentication()) { - // Authenticate using SASL - if (password != null) { - saslAuthentication.authenticate(username, password, resource); - } - else { - saslAuthentication.authenticate(resource, config.getCallbackHandler()); - } - } else { - throw new SmackException("No non-anonymous SASL authentication mechanism available"); - } + protected synchronized void loginInternal(String username, String password, String resource) throws XMPPException, + SmackException, IOException, InterruptedException { + // Authenticate using SASL + saslAuthentication.authenticate(username, password); // If compression is enabled then request the server to use stream compression. XEP-170 // recommends to perform stream compression before resource binding. @@ -430,28 +422,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { afterSuccessfulLogin(false); } - @Override - public synchronized void loginAnonymously() throws XMPPException, SmackException, IOException, InterruptedException { - // Wait with SASL auth until the SASL mechanisms have been received - saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); - - if (saslAuthentication.hasAnonymousAuthentication()) { - saslAuthentication.authenticateAnonymously(); - } - else { - throw new SmackException("No anonymous SASL authentication mechanism available"); - } - - // If compression is enabled then request the server to use stream compression - if (config.isCompressionEnabled()) { - useCompression(); - } - - bindResourceAndEstablishSession(null); - - afterSuccessfulLogin(false); - } - @Override public boolean isSecureConnection() { return usingTLS;