base.jee.api.cassandra.Authenticate Maven / Gradle / Ivy
/**
* Creative commons Attribution-NonCommercial license.
*
* http://creativecommons.org/licenses/by-nc/2.5/au/deed.en_GB
*
* NO WARRANTY IS GIVEN OR IMPLIED, USE AT YOUR OWN RISK.
*/
package base.jee.api.cassandra;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import base.KeyValue;
import base.Query;
import base.jee.api.Settings;
import base.jee.api.model.Location;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.naming.NamingException;
import base.jee.Constants;
import base.json.Json;
import base.ldap.LdapHelper;
import base.security.PermissionException;
import base.security.User;
import base.text.Password;
import base.text.StringHelper;
import static base.jee.api.cassandra.util.AddPerson.addPerson;
import static base.jee.api.cassandra.util.CreateSession.createSession;
import static base.jee.api.cassandra.util.IpLocation.ipLocation;
import static base.jee.api.cassandra.util.IsThrottled.isThrottled;
import static base.jee.api.cassandra.util.Log.log;
import static base.jee.api.cassandra.util.MarkForThrottling.markForThrottling;
import static base.jee.api.cassandra.util.UpdatePersonFromLdap.updatePersonFromLdap;
/**
*/
public class Authenticate extends Query {
private CassandraAPI api;
private String currentToken;
private String username;
private String password;
private String ip;
public Authenticate() {
}
public Authenticate(CassandraAPI api, String currentToken, String username, String password, String ip) {
if(api == null) {
throw new IllegalArgumentException("Invalid parameter: api");
}
if(username == null) {
throw new IllegalArgumentException("Invalid parameter: username");
}
if(password == null) {
throw new IllegalArgumentException("Invalid parameter: password");
}
if(currentToken != null && currentToken.trim().length() > Constants.MAX_TOKEN_LENGTH) {
throw new IllegalArgumentException("Invalid token.");
}
if(username.trim().length() > Constants.MAX_USERNAME_LENGTH) {
throw new IllegalArgumentException("Invalid username. Usernames should not have more than " + Constants.MAX_USERNAME_LENGTH + " characters.");
}
if(password.trim().length() > Constants.MAX_PASSWORD_LENGTH) {
throw new IllegalArgumentException("Invalid password. Passwords should not have more than " + Constants.MAX_PASSWORD_LENGTH + " characters.");
}
if(ip != null && ip.trim().length() > Constants.MAX_IP_ADDRESS_LENGTH) {
throw new IllegalArgumentException("Invalid IP address. IP address should not have more than " + Constants.MAX_IP_ADDRESS_LENGTH + " characters.");
}
this.api = api;
this.currentToken = currentToken;
this.username = username.trim().toLowerCase();
this.password = password.trim();
this.ip = ip == null?null:ip.trim();
}
@Override
public Query newWithParameters(Map parameters) throws IOException, PermissionException {
return new Authenticate(
(CassandraAPI)parameters.get("api"),
(String)parameters.get("current_token"),
(String)parameters.get("username"),
(String)parameters.get("password"),
((User)parameters.get("user")).getIp());
}
public List execute() throws IOException {
List results = new LinkedList<>();
String token = null;
if(password.trim().length() < Constants.MIN_PASSWORD_LENGTH) {
results.add(new KeyValue("error", "Invalid password."));
return results;
}
try {
Session s = api.getCassandraSession();
Settings settings = api.getSettingsCache();
if(isThrottled(s, ip, "ip")) {
log(s, "SEVERE", User.userWithIp(ip), "Blocked authentication for throttled IP address: " + ip);
results.add(new KeyValue("error", "Sign-in from this IP address is temporarily disabled due to repated sign in failures. Please try again shortly."));
return results;
}
if(isThrottled(s, username, "auth")) {
log(s, "SEVERE", User.userWithIp(ip), "Blocked authentication for throttled username: " + username);
results.add(new KeyValue("error", "Sign-in using this account is temporarily disabled due to repated sign in failures. Please try again shortly."));
return results;
}
UUID personUuid = null;
String firstName = null;
String lastName = null;
PreparedStatement p = s.prepare("select uuid, password, first_name, last_name from person where " + (username.contains("@") ? "email" : "username") + "=?");
for(Row r : s.execute(p.bind(username))) {
personUuid = r.getUUID(0);
firstName = r.getString(2);
lastName = r.getString(3);
// If password hash does not match password, we forget this search result,
// this enables a lookup on LDAP (below).
if(!Password.verifyPassword(r.getString(1), password)) {
personUuid = null;
}
}
// Internal password check failed. Do LDAP lookup if this feature is configured to be enabled.
if(personUuid == null) {
boolean ldapEnabled = false;
String ldapUrl = null;
String ldapUserDn = null;
if(!username.contains("@")) {
String e = settings.get("ldap.enabled", "false");
ldapEnabled = e.equalsIgnoreCase("true") || e.equalsIgnoreCase("y") || e.equalsIgnoreCase("yes");
ldapUrl = settings.get("ldap.url");
ldapUserDn = settings.get("ldap.userdn");
}
if(ldapEnabled) {
String user = ldapUserDn.replace("{u}", username);
LdapHelper ldap;
Map attributes;
try {
ldap = new LdapHelper(ldapUrl, user, password, true);
attributes = ldap.getAttributes(user);
} catch (NamingException | IOException e) {
if(e.getCause() instanceof javax.naming.AuthenticationException) {
log(s, "FINE", User.userWithIp(ip), "Invalid internal and/or ldap username or password. Username=" + username);
results.add(new KeyValue("error", "Invalid username or password."));
return results;
} else {
log(s, "SEVERE", User.userWithIp(ip), "Problem communicating with LDAP server. user=" + user + " - " + StringHelper.exceptionToString(e, "\n "));
results.add(new KeyValue("error", "Authentication temporarily unavailable."));
return results;
}
}
firstName = attributes.get("givenName");
lastName = attributes.get("sn");
personUuid = createOrUpdatePersonUsingLdapAttributes(s, username, attributes);
}
if(personUuid == null) {
if(ip != null) {
markForThrottling(s, ip, "ip");
}
markForThrottling(s, username, "auth");
log(s, "FINE", User.userWithIp(ip), "Invalid username or password. Username: " + username);
results.add(new KeyValue("error", "Invalid username or password."));
return results;
}
}
if(password.length() == 0) {
log(s, "SEVERE", User.userWithUuidAndIp(personUuid, ip), "Blocked authentication for account with no password: Username: " + username);
results.add(new KeyValue("error", "This user account has no password."));
return results;
}
p = s.prepare("update person set last_auth=?,last_auth_ip=? where uuid=?");
s.execute(p.bind(new Date().getTime(), ip, personUuid));
token = createSession(s, personUuid, firstName, lastName, currentToken, ip, Long.parseLong(settings.get("session.expiry")));
Location l = ipLocation(s, ip);
log(s, "INFO", User.userWithUuidAndIp(personUuid, ip), "Authentication success. Location: " + (l == null?"unknown":l.toString()));
} catch(NoSuchAlgorithmException e) {
throw new IOException(e);
}
results.add(new KeyValue("token", token));
return results;
}
private UUID createOrUpdatePersonUsingLdapAttributes(Session s, String username, Map attributes) throws IOException {
UUID personUuid = null;
String firstName = attributes.get("givenName");
String lastName = attributes.get("sn");
String email = attributes.get("mail");
PreparedStatement p = s.prepare("select uuid, first_name, last_name from person where username=?");
ResultSet rs = s.execute(p.bind(username));
for(Row r : rs) {
personUuid = r.getUUID(0);
if((email != null && !email.equals(r.getString(4)))
|| (firstName != null && !firstName.equals(r.getString(2)))
|| (lastName != null && !lastName.equals(r.getString(3)))
) {
if(email.equals(r.getString(4))) {
email = null;
}
if(firstName.equals(r.getString(2))) {
firstName = null;
}
if(lastName.equals(r.getString(3))) {
lastName = null;
}
updatePersonFromLdap(s, User.userWithUuidAndIp(personUuid, ip), firstName, lastName, email);
}
return personUuid;
}
personUuid = addPerson(s, firstName, lastName, email, username, null, null);
log(s, "INFO", User.userWithUuidAndIp(personUuid, ip), "Auto created user account using LDAP date for username=" + username);
return personUuid;
}
@Override
public String getJsonParameters() {
return "{" +
"\"current_token\":\"" + Json.escape(currentToken) + "\"," +
(ip == null?"":("\"ip\":\"" + Json.escape(ip) + "\",")) +
"\"username\":\"" + Json.escape(username) + "\"" +
"}";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy