Rework and improve authentication API and internals

Add

- performSaslAnonymousAuthentication()
- performSaslExternalAuthentication(SSLContext)
- addEnabledSaslMechanism(String)
- addEnabledSaslMechanisms(Collection<String>)

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.
This commit is contained in:
Florian Schmaus 2015-05-03 22:00:41 +02:00
parent 51700400bc
commit 9354e4fb74
10 changed files with 240 additions and 214 deletions

View File

@ -210,42 +210,16 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
} }
@Override @Override
protected void loginNonAnonymously(String username, String password, String resource) protected void loginInternal(String username, String password, String resource) throws XMPPException,
throws XMPPException, SmackException, IOException, InterruptedException { SmackException, IOException, InterruptedException {
if (saslAuthentication.hasNonAnonymousAuthentication()) { // Authenticate using SASL
// Authenticate using SASL saslAuthentication.authenticate(username, password);
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");
}
bindResourceAndEstablishSession(resource); bindResourceAndEstablishSession(resource);
afterSuccessfulLogin(false); 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 @Override
public void send(PlainStreamElement element) throws NotConnectedException { public void send(PlainStreamElement element) throws NotConnectedException {
if (done) { if (done) {

View File

@ -204,7 +204,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
/** /**
* The SASLAuthentication manager that is responsible for authenticating with the server. * 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 * 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. * @param configuration The configuration which is used to establish the connection.
*/ */
protected AbstractXMPPConnection(ConnectionConfiguration configuration) { protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
saslAuthentication = new SASLAuthentication(this, configuration);
config = configuration; config = configuration;
// Notify listeners that a new connection has been established // Notify listeners that a new connection has been established
for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) { for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) {
@ -397,18 +398,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* @throws InterruptedException * @throws InterruptedException
*/ */
public synchronized void login() throws XMPPException, SmackException, IOException, InterruptedException { public synchronized void login() throws XMPPException, SmackException, IOException, InterruptedException {
if (isAnonymous()) { // The previously used username, password and resource take over precedence over the
throwNotConnectedExceptionIfAppropriate(); // ones from the connection configuration
throwAlreadyLoggedInExceptionIfAppropriate(); CharSequence username = usedUsername != null ? usedUsername : config.getUsername();
loginAnonymously(); String password = usedPassword != null ? usedPassword : config.getPassword();
} else { String resource = usedResource != null ? usedResource : config.getResource();
// The previously used username, password and resource take over precedence over the login(username, password, resource);
// 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; usedUsername = username != null ? username.toString() : null;
usedPassword = password; usedPassword = password;
usedResource = resource; 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; throws XMPPException, SmackException, IOException, InterruptedException;
protected abstract void loginAnonymously() throws XMPPException, SmackException, IOException, InterruptedException;
@Override @Override
public final boolean isConnected() { public final boolean isConnected() {
return connected; return connected;

View File

@ -17,8 +17,19 @@
package org.jivesoftware.smack; 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.packet.Session;
import org.jivesoftware.smack.proxy.ProxyInfo; 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 org.jxmpp.jid.DomainBareJid;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@ -93,6 +104,8 @@ public abstract class ConnectionConfiguration {
protected final boolean allowNullOrEmptyUsername; protected final boolean allowNullOrEmptyUsername;
private final Set<String> enabledSaslMechanisms;
protected ConnectionConfiguration(Builder<?,?> builder) { protected ConnectionConfiguration(Builder<?,?> builder) {
username = builder.username; username = builder.username;
password = builder.password; password = builder.password;
@ -130,6 +143,10 @@ public abstract class ConnectionConfiguration {
legacySessionDisabled = builder.legacySessionDisabled; legacySessionDisabled = builder.legacySessionDisabled;
debuggerEnabled = builder.debuggerEnabled; debuggerEnabled = builder.debuggerEnabled;
allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername; 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; 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<String> getEnabledSaslMechanisms() {
return Collections.unmodifiableSet(enabledSaslMechanisms);
}
/** /**
* A builder for XMPP connection configurations. * A builder for XMPP connection configurations.
* <p> * <p>
@ -387,6 +422,8 @@ public abstract class ConnectionConfiguration {
private String host; private String host;
private int port = 5222; private int port = 5222;
private boolean allowEmptyOrNullUsername = false; private boolean allowEmptyOrNullUsername = false;
private boolean saslMechanismsSealed;
private Set<String> enabledSaslMechanisms;
protected Builder() { protected Builder() {
} }
@ -518,7 +555,7 @@ public abstract class ConnectionConfiguration {
* @return a reference to this builder. * @return a reference to this builder.
*/ */
public B setCustomSSLContext(SSLContext context) { public B setCustomSSLContext(SSLContext context) {
this.customSSLContext = context; this.customSSLContext = Objects.requireNonNull(context, "The SSLContext must not be null");
return getThis(); return getThis();
} }
@ -628,6 +665,92 @@ public abstract class ConnectionConfiguration {
return getThis(); 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, <b>all mechanisms
* known to Smack</b> 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<String> saslMechanisms) {
if (saslMechanismsSealed) {
throw new IllegalStateException("The enabled SASL mechanisms are sealed, you can not add new ones");
}
CollectionUtil.requireNotEmpty(saslMechanisms, "saslMechanisms");
Set<String> 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(); public abstract C build();
protected abstract B getThis(); protected abstract B getThis();

View File

@ -20,11 +20,11 @@ package org.jivesoftware.smack;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Mechanisms; import org.jivesoftware.smack.packet.Mechanisms;
import org.jivesoftware.smack.sasl.SASLAnonymous;
import org.jivesoftware.smack.sasl.SASLErrorException; import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jxmpp.jid.DomainBareJid;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;
@ -91,6 +91,17 @@ public class SASLAuthentication {
return answer; 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 * Unregister a SASLMechanism by it's full class name. For example
* "org.jivesoftware.smack.sasl.javax.SASLCramMD5Mechanism". * "org.jivesoftware.smack.sasl.javax.SASLCramMD5Mechanism".
@ -131,6 +142,7 @@ public class SASLAuthentication {
} }
private final AbstractXMPPConnection connection; private final AbstractXMPPConnection connection;
private final ConnectionConfiguration configuration;
private SASLMechanism currentMechanism = null; private SASLMechanism currentMechanism = null;
/** /**
@ -143,68 +155,12 @@ public class SASLAuthentication {
*/ */
private Exception saslException; private Exception saslException;
SASLAuthentication(AbstractXMPPConnection connection) { SASLAuthentication(AbstractXMPPConnection connection, ConnectionConfiguration configuration) {
this.configuration = configuration;
this.connection = connection; this.connection = connection;
this.init(); 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.<p>
*
* 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 * 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 * 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 username the username that is authenticating with the server.
* @param password the password to send to the server. * @param password the password to send to the server.
* @param resource the desired resource.
* @throws XMPPErrorException * @throws XMPPErrorException
* @throws SASLErrorException * @throws SASLErrorException
* @throws IOException * @throws IOException
* @throws SmackException * @throws SmackException
* @throws InterruptedException * @throws InterruptedException
*/ */
public void authenticate(String username, String password, String resource) public void authenticate(String username, String password)
throws XMPPErrorException, SASLErrorException, IOException, throws XMPPErrorException, SASLErrorException, IOException,
SmackException, InterruptedException { SmackException, InterruptedException {
SASLMechanism selectedMechanism = selectMechanism(); currentMechanism = selectMechanism();
if (selectedMechanism != null) { final CallbackHandler callbackHandler = configuration.getCallbackHandler();
currentMechanism = selectedMechanism; 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.<p>
*
* 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) { 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()); wait(connection.getPacketReplyTimeout());
} }
maybeThrowException();
if (!authenticationSuccessful) {
throw NoResponseException.newWith(connection);
}
}
private void maybeThrowException() throws SmackException, SASLErrorException {
if (saslException != null){ if (saslException != null){
if (saslException instanceof SmackException) { if (saslException instanceof SmackException) {
throw (SmackException) saslException; throw (SmackException) saslException;
@ -288,6 +205,10 @@ public class SASLAuthentication {
throw new IllegalStateException("Unexpected exception type" , saslException); throw new IllegalStateException("Unexpected exception type" , saslException);
} }
} }
if (!authenticationSuccessful) {
throw NoResponseException.newWith(connection);
}
} }
/** /**
@ -378,10 +299,12 @@ public class SASLAuthentication {
saslException = null; saslException = null;
} }
private SASLMechanism selectMechanism() { private SASLMechanism selectMechanism() throws SmackException {
// Locate the SASLMechanism to use
SASLMechanism selectedMechanism = null;
Iterator<SASLMechanism> it = REGISTERED_MECHANISMS.iterator(); Iterator<SASLMechanism> it = REGISTERED_MECHANISMS.iterator();
final List<String> serverMechanisms = getServerMechanisms();
if (serverMechanisms.isEmpty()) {
LOGGER.warning("Server did not report any SASL mechanisms");
}
// Iterate in SASL Priority order over registered mechanisms // Iterate in SASL Priority order over registered mechanisms
while (it.hasNext()) { while (it.hasNext()) {
SASLMechanism mechanism = it.next(); SASLMechanism mechanism = it.next();
@ -391,19 +314,31 @@ public class SASLAuthentication {
continue; 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. // Create a new instance of the SASLMechanism for every authentication attempt.
selectedMechanism = mechanism.instanceForAuthentication(connection); return mechanism.instanceForAuthentication(connection);
break;
} }
} }
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<String> serverMechanisms() { private List<String> getServerMechanisms() {
Mechanisms mechanisms = connection.getFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE); Mechanisms mechanisms = connection.getFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE);
if (mechanisms == null) { if (mechanisms == null) {
LOGGER.warning("Server did not report any SASL mechanisms");
return Collections.emptyList(); return Collections.emptyList();
} }
return mechanisms.getMechanisms(); return mechanisms.getMechanisms();

View File

@ -32,6 +32,7 @@ import org.jivesoftware.smack.initializer.SmackInitializer;
import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.provider.BindIQProvider; import org.jivesoftware.smack.provider.BindIQProvider;
import org.jivesoftware.smack.provider.ProviderManager; 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.SASLXOauth2Mechanism;
import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism; import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism;
import org.jivesoftware.smack.util.FileUtils; import org.jivesoftware.smack.util.FileUtils;
@ -137,6 +138,7 @@ public final class SmackInitialization {
SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism()); SASLAuthentication.registerSASLMechanism(new SCRAMSHA1Mechanism());
SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism()); SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism());
SASLAuthentication.registerSASLMechanism(new SASLAnonymous());
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider()); ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());

View File

@ -171,6 +171,9 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
authenticate(); authenticate();
} }
/**
* @throws SmackException
*/
protected void authenticateInternal() throws SmackException { protected void authenticateInternal() throws SmackException {
} }
@ -248,6 +251,9 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
connection.send(responseStanza); connection.send(responseStanza);
} }
/**
* @throws SmackException
*/
protected byte[] evaluateChallenge(byte[] challenge) throws SmackException { protected byte[] evaluateChallenge(byte[] challenge) throws SmackException {
return null; return null;
} }

View File

@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.sasl; package org.jivesoftware.smack.sasl.core;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;

View File

@ -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 <T> Collection<T> requireNotEmpty(Collection<T> 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;
}
}

View File

@ -116,23 +116,12 @@ public class DummyConnection extends AbstractXMPPConnection {
} }
@Override @Override
protected void loginNonAnonymously(String username, String password, String resource) protected void loginInternal(String username, String password, String resource)
throws XMPPException { throws XMPPException {
user = getUserJid(); user = getUserJid();
authenticated = true; 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 @Override
public void send(PlainStreamElement element) { public void send(PlainStreamElement element) {
queue.add(element); queue.add(element);

View File

@ -361,18 +361,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
@Override @Override
protected synchronized void loginNonAnonymously(String username, String password, String resource) throws XMPPException, SmackException, IOException, InterruptedException { protected synchronized void loginInternal(String username, String password, String resource) throws XMPPException,
if (saslAuthentication.hasNonAnonymousAuthentication()) { SmackException, IOException, InterruptedException {
// Authenticate using SASL // Authenticate using SASL
if (password != null) { saslAuthentication.authenticate(username, password);
saslAuthentication.authenticate(username, password, resource);
}
else {
saslAuthentication.authenticate(resource, config.getCallbackHandler());
}
} else {
throw new SmackException("No non-anonymous SASL authentication mechanism available");
}
// If compression is enabled then request the server to use stream compression. XEP-170 // If compression is enabled then request the server to use stream compression. XEP-170
// recommends to perform stream compression before resource binding. // recommends to perform stream compression before resource binding.
@ -430,28 +422,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
afterSuccessfulLogin(false); 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 @Override
public boolean isSecureConnection() { public boolean isSecureConnection() {
return usingTLS; return usingTLS;