154 lines
5.0 KiB
Java
154 lines
5.0 KiB
Java
|
|
package ins.frontend.utils;
|
||
|
|
|
||
|
|
import org.openautonomousconnection.webserver.utils.Sha256;
|
||
|
|
|
||
|
|
import java.sql.*;
|
||
|
|
import java.util.Objects;
|
||
|
|
import java.util.Optional;
|
||
|
|
import java.util.UUID;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* DAO for users table:
|
||
|
|
* users(id, uid(uuid string), username(sha256 hex or plain), password(pbkdf2...))
|
||
|
|
*/
|
||
|
|
public final class UserDao {
|
||
|
|
|
||
|
|
private final DataSourceProvider dataSource;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Creates a UserDao.
|
||
|
|
*
|
||
|
|
* @param dataSource connection provider
|
||
|
|
*/
|
||
|
|
public UserDao(DataSourceProvider dataSource) {
|
||
|
|
this.dataSource = Objects.requireNonNull(dataSource, "dataSource");
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Finds a user by username.
|
||
|
|
*
|
||
|
|
* @param uid exact uid match
|
||
|
|
* @return optional row
|
||
|
|
* @throws SQLException on SQL errors
|
||
|
|
*/
|
||
|
|
public Optional<UserRow> findByUid(UUID uid) throws SQLException {
|
||
|
|
if (uid == null) return Optional.empty();
|
||
|
|
|
||
|
|
String sql = "SELECT id, uid, username, password FROM users WHERE uid = ? LIMIT 1";
|
||
|
|
|
||
|
|
try (Connection c = dataSource.getConnection();
|
||
|
|
PreparedStatement ps = c.prepareStatement(sql)) {
|
||
|
|
|
||
|
|
ps.setString(1, uid.toString());
|
||
|
|
|
||
|
|
try (ResultSet rs = ps.executeQuery()) {
|
||
|
|
if (!rs.next()) return Optional.empty();
|
||
|
|
return Optional.of(new UserRow(
|
||
|
|
rs.getInt("id"),
|
||
|
|
rs.getString("uid"),
|
||
|
|
rs.getString("username"),
|
||
|
|
rs.getString("password")
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Creates a new user.
|
||
|
|
*
|
||
|
|
* @param uid uuid string (36 chars)
|
||
|
|
* @param username username string (your choice: raw or sha256 hex)
|
||
|
|
* @param passwordEncoded encoded password
|
||
|
|
* @return generated users.id
|
||
|
|
* @throws SQLException on SQL errors
|
||
|
|
*/
|
||
|
|
public int createUser(String uid, String username, String passwordEncoded) throws SQLException {
|
||
|
|
if (uid == null || uid.isBlank()) throw new IllegalArgumentException("uid must not be blank");
|
||
|
|
if (username == null || username.isBlank()) throw new IllegalArgumentException("username must not be blank");
|
||
|
|
if (passwordEncoded == null || passwordEncoded.isBlank())
|
||
|
|
throw new IllegalArgumentException("passwordEncoded must not be blank");
|
||
|
|
|
||
|
|
String sql = "INSERT INTO users (uid, username, password) VALUES (?, ?, ?)";
|
||
|
|
|
||
|
|
try (Connection c = dataSource.getConnection();
|
||
|
|
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||
|
|
|
||
|
|
ps.setString(1, uid);
|
||
|
|
ps.setString(2, Sha256.hex(username));
|
||
|
|
ps.setString(3, passwordEncoded);
|
||
|
|
ps.executeUpdate();
|
||
|
|
|
||
|
|
try (ResultSet rs = ps.getGeneratedKeys()) {
|
||
|
|
if (rs.next()) return rs.getInt(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
throw new SQLException("No generated key returned for users.id");
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convenience: creates a user with a new random UUID.
|
||
|
|
*
|
||
|
|
* @param username username
|
||
|
|
* @param passwordEncoded encoded password
|
||
|
|
* @return generated users.id
|
||
|
|
* @throws SQLException on SQL errors
|
||
|
|
*/
|
||
|
|
public int createUserWithNewUuid(String username, String passwordEncoded) throws SQLException {
|
||
|
|
UUID uuid = UUID.randomUUID();
|
||
|
|
while (findByUid(uuid).isPresent()) uuid = UUID.randomUUID();
|
||
|
|
return createUser(uuid.toString(), username, passwordEncoded);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Finds a user by username.
|
||
|
|
*
|
||
|
|
* @param username exact username match
|
||
|
|
* @return optional row
|
||
|
|
* @throws SQLException on SQL errors
|
||
|
|
*/
|
||
|
|
public Optional<UserRow> findByUsername(String username) throws SQLException {
|
||
|
|
if (username == null || username.isBlank()) return Optional.empty();
|
||
|
|
username = Sha256.hex(username);
|
||
|
|
|
||
|
|
String sql = "SELECT id, uid, username, password FROM users WHERE username = ? LIMIT 1";
|
||
|
|
|
||
|
|
try (Connection c = dataSource.getConnection();
|
||
|
|
PreparedStatement ps = c.prepareStatement(sql)) {
|
||
|
|
|
||
|
|
ps.setString(1, username);
|
||
|
|
|
||
|
|
try (ResultSet rs = ps.executeQuery()) {
|
||
|
|
if (!rs.next()) return Optional.empty();
|
||
|
|
return Optional.of(new UserRow(
|
||
|
|
rs.getInt("id"),
|
||
|
|
rs.getString("uid"),
|
||
|
|
rs.getString("username"),
|
||
|
|
rs.getString("password")
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Connection provider abstraction.
|
||
|
|
*/
|
||
|
|
public interface DataSourceProvider {
|
||
|
|
/**
|
||
|
|
* @return open SQL connection
|
||
|
|
* @throws SQLException on errors
|
||
|
|
*/
|
||
|
|
Connection getConnection() throws SQLException;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* User row.
|
||
|
|
*
|
||
|
|
* @param id users.id
|
||
|
|
* @param uid users.uid (uuid)
|
||
|
|
* @param username users.username
|
||
|
|
* @param passwordEncoded users.password
|
||
|
|
*/
|
||
|
|
public record UserRow(int id, String uid, String username, String passwordEncoded) {
|
||
|
|
}
|
||
|
|
}
|