org.jgroups.auth.sasl.SimpleAuthorizingCallbackHandler Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.auth.sasl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.util.Util;
/**
* SimpleAuthorizingCallbackHandler. This class implements a simple callback handler which can be
* used to configure cluster authentication for the JGroups transport. It is configured via system
* properties. The following properties are available:
*
*
* - sasl.credentials.properties - the path to a property file which contains principal/credential
* mappings represented as principal=password
* - sasl.local.principal - the name of the principal that is used to identify the local node. It
* must exist in the sasl.credentials.properties file
* - sasl.roles.properties - (optional) the path to a property file which contains principal/roles
* mappings represented as principal=role1,role2,role3
* - sasl.role - (optional) if present, authorizes joining nodes only if their principal is
*
- sasl.realm - (optional) the name of the realm to use for the SASL mechanisms that require it
*
*
*
* @author Tristan Tarrant
* @since 3.6
*/
public class SimpleAuthorizingCallbackHandler implements CallbackHandler {
private static final Log log = LogFactory.getLog(SimpleAuthorizingCallbackHandler.class);
private final Properties credentials;
private final Properties roles;
private final Timer timer;
private final String localPrincipal;
private final String role;
private final String realm;
public SimpleAuthorizingCallbackHandler() {
this(SecurityActions.getSystemProperties());
}
public SimpleAuthorizingCallbackHandler(Properties properties) {
this.credentials = new Properties();
this.roles = new Properties();
localPrincipal = requireProperty(properties, "sasl.local.principal");
String credentialsFile = requireProperty(properties, "sasl.credentials.properties");
timer = new Timer();
File fCredentials = new File(credentialsFile);
timer.scheduleAtFixedRate(
new FileWatchTask(fCredentials, new PropertiesReloadFileObserver(fCredentials, credentials)), 0,
TimeUnit.SECONDS.toMillis(10));
role = properties.getProperty("sasl.role");
String rolesFile = properties.getProperty("sasl.roles.properties");
if (role != null) {
if (rolesFile == null) {
throw new IllegalStateException(
"To enable role authorization, both sasl.role and sasl.roles.properties system properties must be set");
} else {
File fRoles = new File(rolesFile);
timer.scheduleAtFixedRate(
new FileWatchTask(fRoles, new PropertiesReloadFileObserver(fRoles, roles)), 0,
TimeUnit.SECONDS.toMillis(10));
}
}
realm = properties.getProperty("sasl.realm");
}
private static String requireProperty(Properties properties, String propertyName) {
String value = properties.getProperty(propertyName);
if (value == null) {
throw new IllegalStateException("The required system property " + propertyName + " has not been set");
} else {
return value;
}
}
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
List responseCallbacks = new LinkedList<>();
String remotePrincipal = null;
boolean remotePrincipalFound = false;
for (Callback current : callbacks) {
if (current instanceof AuthorizeCallback) {
responseCallbacks.add(current);
} else if (current instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) current;
remotePrincipal = nameCallback.getDefaultName();
if (remotePrincipal != null) { // server
remotePrincipalFound = credentials.containsKey(remotePrincipal);
} else { // client, we need to respond
responseCallbacks.add(current);
}
} else if (current instanceof PasswordCallback) {
responseCallbacks.add(current);
} else if (current instanceof RealmCallback) {
String realmLocal = ((RealmCallback) current).getDefaultText();
if (realmLocal != null && !realmLocal.equals(this.realm)) {
throw new IOException("Invalid realm " + realmLocal);
}
responseCallbacks.add(current);
} else {
throw new UnsupportedCallbackException(current);
}
}
for (Callback current : responseCallbacks) {
if (current instanceof NameCallback) {
((NameCallback) current).setName(localPrincipal);
} else if (current instanceof AuthorizeCallback) {
AuthorizeCallback acb = (AuthorizeCallback) current;
String authenticationId = acb.getAuthenticationID();
String authorizationId = acb.getAuthorizationID();
acb.setAuthorized(authenticationId.equals(authorizationId));
if (role != null) {
String principalRoleNames = roles.getProperty(acb.getAuthorizationID());
List principalRoles = (List) (principalRoleNames != null
? Arrays.asList(principalRoleNames.split("\\s*,\\s*")) : Collections.emptyList());
if (!principalRoles.contains(role)) {
throw new IOException("Unauthorized user " + authorizationId);
}
}
} else if (current instanceof PasswordCallback) {
String password;
if (remotePrincipal == null) { // client, send our password
password = credentials.getProperty(localPrincipal);
} else if (remotePrincipalFound) { // server, validate incoming password
password = credentials.getProperty(remotePrincipal);
} else {
throw new IOException("Unauthorized user " + remotePrincipal);
}
((PasswordCallback) current).setPassword(password.toCharArray());
} else if (current instanceof RealmCallback) {
((RealmCallback)current).setText(realm);
}
}
}
public static class PropertiesReloadFileObserver implements FileObserver {
private final Properties properties;
PropertiesReloadFileObserver(File file, Properties properties) {
this.properties = properties;
loadProperties(file);
}
private void loadProperties(File file) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
properties.load(fis);
} catch (IOException e) {
log.error(Util.getMessage("AnErrorOccurredWhileLoadingPropertiesFrom") + file, e);
} finally {
Util.close(fis);
}
}
@Override
public void fileChanged(File file) {
loadProperties(file);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy