package io.vertx.ext.auth.jdbc.impl;
import io.vertx.core.*;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.jdbc.JDBCAuth;
import io.vertx.ext.auth.jdbc.JDBCHashStrategy;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.ResultSet;
import io.vertx.ext.sql.SQLConnection;
import java.util.function.Consumer;
public class JDBCAuthImpl implements AuthProvider, JDBCAuth {
private JDBCClient client;
private String authenticateQuery = DEFAULT_AUTHENTICATE_QUERY;
private String rolesQuery = DEFAULT_ROLES_QUERY;
private String permissionsQuery = DEFAULT_PERMISSIONS_QUERY;
private String rolePrefix = DEFAULT_ROLE_PREFIX;
private JDBCHashStrategy strategy;
public JDBCAuthImpl(Vertx vertx, JDBCClient client) {
this.client = client;
strategy = JDBCHashStrategy.createSHA512(vertx);
}
@Override
public void authenticate(JsonObject authInfo, Handler<AsyncResult<User>> resultHandler) {
String username = authInfo.getString("username");
if (username == null) {
resultHandler.handle(Future.failedFuture("authInfo must contain username in 'username' field"));
return;
}
String password = authInfo.getString("password");
if (password == null) {
resultHandler.handle(Future.failedFuture("authInfo must contain password in 'password' field"));
return;
}
executeQuery(authenticateQuery, new JsonArray().add(username), resultHandler, rs -> {
switch (rs.getNumRows()) {
case 0: {
resultHandler.handle(Future.failedFuture("Invalid username/password"));
break;
}
case 1: {
JsonArray row = rs.getResults().get(0);
String hashedStoredPwd = strategy.getHashedStoredPwd(row);
String salt = strategy.getSalt(row);
int version = -1;
int sep = hashedStoredPwd.lastIndexOf('$');
if (sep != -1) {
try {
version = Integer.parseInt(hashedStoredPwd.substring(sep + 1));
} catch (NumberFormatException e) {
resultHandler.handle(Future.failedFuture("Invalid nonce version: " + version));
return;
}
}
String hashedPassword = strategy.computeHash(password, salt, version);
if (JDBCHashStrategy.isEqual(hashedStoredPwd, hashedPassword)) {
resultHandler.handle(Future.succeededFuture(new JDBCUser(username, this, rolePrefix)));
} else {
resultHandler.handle(Future.failedFuture("Invalid username/password"));
}
break;
}
default: {
resultHandler.handle(Future.failedFuture("Failure in authentication"));
break;
}
}
});
}
@Override
public JDBCAuth setAuthenticationQuery(String authenticationQuery) {
this.authenticateQuery = authenticationQuery;
return this;
}
@Override
public JDBCAuth setRolesQuery(String rolesQuery) {
this.rolesQuery = rolesQuery;
return this;
}
@Override
public JDBCAuth setPermissionsQuery(String permissionsQuery) {
this.permissionsQuery = permissionsQuery;
return this;
}
@Override
public JDBCAuth setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
return this;
}
@Override
public JDBCAuth setHashStrategy(JDBCHashStrategy strategy) {
this.strategy = strategy;
return this;
}
<T> void executeQuery(String query, JsonArray params, Handler<AsyncResult<T>> resultHandler,
Consumer<ResultSet> resultSetConsumer) {
client.getConnection(res -> {
if (res.succeeded()) {
SQLConnection conn = res.result();
conn.queryWithParams(query, params, queryRes -> {
if (queryRes.succeeded()) {
ResultSet rs = queryRes.result();
resultSetConsumer.accept(rs);
} else {
resultHandler.handle(Future.failedFuture(queryRes.cause()));
}
conn.close(closeRes -> {
});
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public String computeHash(String password, String salt, int version) {
return strategy.computeHash(password, salt, version);
}
@Override
public String generateSalt() {
return strategy.generateSalt();
}
@Override
public JDBCAuth setNonces(JsonArray nonces) {
strategy.setNonces(nonces);
return this;
}
String getRolesQuery() {
return rolesQuery;
}
String getPermissionsQuery() {
return permissionsQuery;
}
}