package tigase.db.ldap;
import java.util.Hashtable;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.sasl.SaslException;
import javax.security.sasl.SaslServer;
import tigase.db.AuthRepository;
import tigase.db.AuthorizationException;
import tigase.db.DBInitException;
import tigase.db.TigaseDBException;
import tigase.db.UserExistsException;
import tigase.db.UserNotFoundException;
import tigase.util.Base64;
import tigase.xmpp.BareJID;
public class LdapAuthProvider implements AuthRepository {
private class SaslPLAINLdap implements SaslServer {
private boolean authOk = false;
private final String serverName;
private BareJID userId;
public SaslPLAINLdap(String serverName) {
this.serverName = serverName;
}
@Override
public void dispose() throws SaslException {
}
@Override
public byte[] evaluateResponse(byte[] byteArray) throws SaslException {
int auth_idx = 0;
while ((byteArray[auth_idx] != 0) && (auth_idx < byteArray.length)) {
++auth_idx;
}
int user_idx = ++auth_idx;
while ((byteArray[user_idx] != 0) && (user_idx < byteArray.length)) {
++user_idx;
}
final String user_id = new String(byteArray, auth_idx, user_idx - auth_idx);
if (log.isLoggable(Level.FINEST)) {
log.finest("SASL userId: " + user_id);
}
++user_idx;
final String passwd = new String(byteArray, user_idx, byteArray.length - user_idx);
if (log.isLoggable(Level.FINEST)) {
log.finest("SASL password: " + passwd);
}
try {
userId = BareJID.bareJIDInstance(user_id, serverName);
authOk = doBindAuthentication(userId, passwd);
} catch (Exception e) {
log.log(Level.WARNING, "Can't authenticate user", e);
authOk = false;
}
return null;
}
@Override
public String getAuthorizationID() {
return null;
}
@Override
public String getMechanismName() {
return "PLAIN";
}
@Override
public Object getNegotiatedProperty(String propName) {
return null;
}
public BareJID getUser_id() {
return userId;
}
@Override
public boolean isComplete() {
return authOk;
}
@Override
public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException {
return null;
}
@Override
public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {
return null;
}
}
private static final Logger log = Logger.getLogger(LdapAuthProvider.class.getName());
protected static final String[] non_sasl_mechs = { "password" };
protected static final String[] sasl_mechs = { "PLAIN" };
/**
* Example: <code>uid=%s,ou=people,dc=xmpp-test,dc=org</code>
*/
public static final String USER_DN_PATTERN_KEY = "user-dn-pattern";
private String providerUrl;
private String userDnPattern;
@Override
public void addUser(BareJID user, String password) throws UserExistsException, TigaseDBException {
throw new TigaseDBException("Not available");
}
@Override
@Deprecated
public boolean digestAuth(BareJID user, String digest, String id, String alg) throws UserNotFoundException,
TigaseDBException, AuthorizationException {
throw new AuthorizationException("Not supported.");
}
private boolean doBindAuthentication(BareJID userId, final String password) throws UserNotFoundException,
TigaseDBException, AuthorizationException {
try {
Hashtable<Object, Object> env = new Hashtable<Object, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.providerUrl);
if (log.isLoggable(Level.FINE))
log.fine("Authenticating user '" + userId + "' with password ******");
final String dn = String.format(this.userDnPattern, userId.getLocalpart(), userId.getDomain(), userId.toString());
if (log.isLoggable(Level.FINER))
log.finer("Using DN:" + dn);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, dn);
env.put(Context.SECURITY_CREDENTIALS, password);
// Create the initial context
DirContext ctx = new InitialDirContext(env);
ctx.close();
if (log.isLoggable(Level.FINE))
log.fine("User " + userId + " authenticated.");
return true;
} catch (javax.naming.AuthenticationException e) {
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "Authentication error: " + e.getMessage());
return false;
} catch (Exception e) {
if (log.isLoggable(Level.WARNING))
log.log(Level.WARNING, "Can't authenticate user", e);
return false;
}
}
@Override
public String getResourceUri() {
return providerUrl;
}
@Override
public long getUsersCount() {
return -1;
};
@Override
public long getUsersCount(String domain) {
return -1;
}
@Override
public void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {
this.userDnPattern = params.get(USER_DN_PATTERN_KEY);
this.providerUrl = resource_uri;
if (log.isLoggable(Level.CONFIG)) {
log.config("User DN Pattern: " + this.userDnPattern);
log.config("LDAP URL: " + this.providerUrl);
}
}
@Override
public void logout(BareJID user) throws UserNotFoundException, TigaseDBException {
}
@Override
public boolean otherAuth(Map<String, Object> props) throws UserNotFoundException, TigaseDBException, AuthorizationException {
String proto = (String) props.get(PROTOCOL_KEY);
if (proto.equals(PROTOCOL_VAL_SASL)) {
if (props.get(MACHANISM_KEY).equals("PLAIN")) {
return saslAuth(props);
}
} else if (proto.equals(PROTOCOL_VAL_NONSASL)) {
String password = (String) props.get(PASSWORD_KEY);
BareJID user_id = (BareJID) props.get(USER_ID_KEY);
boolean auth = doBindAuthentication(user_id, password);
if (auth) {
props.put(USER_ID_KEY, user_id);
}
return auth;
}
throw new AuthorizationException("Protocol is not supported.");
}
@Override
@Deprecated
public boolean plainAuth(BareJID user, String password) throws UserNotFoundException, TigaseDBException,
AuthorizationException {
throw new AuthorizationException("Not supported.");
}
@Override
public void queryAuth(Map<String, Object> authProps) {
String protocol = (String) authProps.get(PROTOCOL_KEY);
if (protocol.equals(PROTOCOL_VAL_NONSASL)) {
authProps.put(RESULT_KEY, non_sasl_mechs);
}
if (protocol.equals(PROTOCOL_VAL_SASL)) {
authProps.put(RESULT_KEY, sasl_mechs);
}
}
@Override
public void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {
throw new TigaseDBException("Not available");
}
private boolean saslAuth(final Map<String, Object> props) throws AuthorizationException {
try {
SaslPLAINLdap ss = new SaslPLAINLdap((String) props.get(SERVER_NAME_KEY));
String data_str = (String) props.get(DATA_KEY);
byte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);
if (log.isLoggable(Level.FINEST)) {
log.finest("response: " + new String(in_data));
}
byte[] challenge = ss.evaluateResponse(in_data);
if (log.isLoggable(Level.FINEST)) {
log.finest("challenge: " + ((challenge != null) ? new String(challenge) : "null"));
}
String challenge_str = (((challenge != null) && (challenge.length > 0)) ? Base64.encode(challenge) : null);
props.put(RESULT_KEY, challenge_str);
if (ss.isComplete()) {
props.put(USER_ID_KEY, ss.getUser_id());
return true;
} else {
return false;
} // end of if (ss.isComplete()) else
} catch (SaslException e) {
throw new AuthorizationException("Sasl exception.", e);
}
}
@Override
public void updatePassword(BareJID user, String password) throws UserNotFoundException, TigaseDBException {
throw new TigaseDBException("Not available");
}
}