mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-22 03:52:06 +01:00
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:
parent
51700400bc
commit
9354e4fb74
10 changed files with 240 additions and 214 deletions
|
@ -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()) {
|
||||
protected void loginInternal(String username, String password, String resource) throws XMPPException,
|
||||
SmackException, IOException, InterruptedException {
|
||||
// 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");
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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,11 +398,6 @@ 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();
|
||||
|
@ -409,7 +405,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
String resource = usedResource != null ? usedResource : config.getResource();
|
||||
login(username, password, resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #login(CharSequence, String, String)}, but takes the resource from the connection
|
||||
|
@ -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;
|
||||
|
|
|
@ -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<String> 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<String> getEnabledSaslMechanisms() {
|
||||
return Collections.unmodifiableSet(enabledSaslMechanisms);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for XMPP connection configurations.
|
||||
* <p>
|
||||
|
@ -387,6 +422,8 @@ public abstract class ConnectionConfiguration {
|
|||
private String host;
|
||||
private int port = 5222;
|
||||
private boolean allowEmptyOrNullUsername = false;
|
||||
private boolean saslMechanismsSealed;
|
||||
private Set<String> 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, <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();
|
||||
|
||||
protected abstract B getThis();
|
||||
|
|
|
@ -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.<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
|
||||
* 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);
|
||||
}
|
||||
if (callbackHandler != null) {
|
||||
currentMechanism.authenticate(host, xmppDomain, callbackHandler);
|
||||
}
|
||||
else {
|
||||
throw new SmackException(
|
||||
"SASL Authentication failed. No known authentication mechanisims.");
|
||||
currentMechanism.authenticate(username, host, xmppDomain, password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
currentMechanism.authenticate(null, null, null, "");
|
||||
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<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
|
||||
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;
|
||||
}
|
||||
|
||||
private List<String> serverMechanisms() {
|
||||
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> 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();
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -171,6 +171,9 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
authenticate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SmackException
|
||||
*/
|
||||
protected void authenticateInternal() throws SmackException {
|
||||
}
|
||||
|
||||
|
@ -248,6 +251,9 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
connection.send(responseStanza);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SmackException
|
||||
*/
|
||||
protected byte[] evaluateChallenge(byte[] challenge) throws SmackException {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
protected synchronized void loginInternal(String username, String password, String resource) throws XMPPException,
|
||||
SmackException, IOException, InterruptedException {
|
||||
// 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");
|
||||
}
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue