|
|
|
|
@@ -1,9 +1,11 @@
|
|
|
|
|
/* © SRSoftware 2024 */
|
|
|
|
|
package de.srsoftware.oidc.datastore.sqlite;
|
|
|
|
|
|
|
|
|
|
import static de.srsoftware.utils.Optionals.nullable;
|
|
|
|
|
import static de.srsoftware.utils.Strings.uuid;
|
|
|
|
|
import static java.util.Optional.empty;
|
|
|
|
|
|
|
|
|
|
import de.srsoftware.oidc.api.PasswordHasher;
|
|
|
|
|
import de.srsoftware.oidc.api.UserService;
|
|
|
|
|
import de.srsoftware.oidc.api.data.AccessToken;
|
|
|
|
|
import de.srsoftware.oidc.api.data.Permission;
|
|
|
|
|
@@ -17,23 +19,29 @@ import java.time.temporal.ChronoUnit;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
private static final String SELECT_USERSTORE_VERSION = "SELECT * FROM metainfo WHERE key = 'user_store_version'";
|
|
|
|
|
private static final String SET_USERSTORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = 'user_store_version'";
|
|
|
|
|
private static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS users(uuid VARCHAR(255) NOT NULL PRIMARY KEY, password VARCHAR(255), email VARCHAR(255), sessionDuration INT NOT NULL DEFAULT 10, username VARCHAR(255), realname VARCHAR(255));";
|
|
|
|
|
private static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS users(uuid VARCHAR(255) NOT NULL PRIMARY KEY, password VARCHAR(255), email VARCHAR(255), session_duration INT NOT NULL DEFAULT 10, username VARCHAR(255), realname VARCHAR(255));";
|
|
|
|
|
private static final String CREATE_USER_PERMISSION_TABLE = "CREATE TABLE IF NOT EXISTS user_permissions(uuid VARCHAR(255), permission VARCHAR(50), PRIMARY KEY(uuid,permission));";
|
|
|
|
|
private static final String COUNT_USERS = "SELECT count(*) FROM users";
|
|
|
|
|
private static final String INSERT_USER = "INSERT INTO users (uuid,password,email,sessionDuration,username,realname) VALUES (?,?,?,?,?,?)";
|
|
|
|
|
private static final String DROP_PERMISSIONS = "DELETE FROM user_permissions WHERE uuid = ?";
|
|
|
|
|
private static final String INSERT_PERMISSIONS = "INSERT INTO user_permissions (uuid, permission) VALUES (?,?)";
|
|
|
|
|
private static final String DROP_USER = "DELETE FROM users WHERE uuid = ?";
|
|
|
|
|
private static final String LOAD_USER = "SELECT * FROM users WHERE uuid = ?";
|
|
|
|
|
private static final String FIND_USER = "SELECT * FROM users WHERE uuid = ? OR username LIKE ? OR realname LIKE ? ORDER BY COALESCE(uuid, ?), username";
|
|
|
|
|
private static final String LIST_USERS = "SELECT * FROM users";
|
|
|
|
|
private static final String LIST_USER_PERMISSIONS = "SELECT * FROM user_permissions WHERE uuid = ?";
|
|
|
|
|
private static final String SELECT_USERSTORE_VERSION = "SELECT * FROM metainfo WHERE key = 'user_store_version'";
|
|
|
|
|
private static final String SET_USERSTORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = 'user_store_version'";
|
|
|
|
|
private static final String INSERT_USER = "INSERT INTO users (uuid,password,email,session_duration,username,realname) VALUES (?,?,?,?,?,?)";
|
|
|
|
|
private static final String INSERT_PERMISSIONS = "INSERT INTO user_permissions (uuid, permission) VALUES (?,?)";
|
|
|
|
|
|
|
|
|
|
private static final String DROP_PERMISSIONS = "DELETE FROM user_permissions WHERE uuid = ?";
|
|
|
|
|
private static final String DROP_USER = "DELETE FROM users WHERE uuid = ?";
|
|
|
|
|
private static final String UPDATE_PASSWORD = "UPDATE users SET password = ? WHERE uuid = ?";
|
|
|
|
|
private final PasswordHasher<String> hasher;
|
|
|
|
|
|
|
|
|
|
private Map<String, AccessToken> accessTokens = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SqliteUserService(Connection connection) throws SQLException {
|
|
|
|
|
public SqliteUserService(Connection connection, PasswordHasher<String> passHasher) throws SQLException {
|
|
|
|
|
super(connection);
|
|
|
|
|
hasher = passHasher;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@@ -50,10 +58,9 @@ public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void dropPermissionsOf(String uuid) throws SQLException {
|
|
|
|
|
var stmt = conn.prepareStatement(DROP_PERMISSIONS);
|
|
|
|
|
stmt.setString(1, uuid);
|
|
|
|
|
stmt.execute();
|
|
|
|
|
private void createUserStoreTables() throws SQLException {
|
|
|
|
|
conn.prepareStatement(CREATE_USER_TABLE).execute();
|
|
|
|
|
conn.prepareStatement(CREATE_USER_PERMISSION_TABLE).execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@@ -71,6 +78,12 @@ public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void dropPermissionsOf(String uuid) throws SQLException {
|
|
|
|
|
var stmt = conn.prepareStatement(DROP_PERMISSIONS);
|
|
|
|
|
stmt.setString(1, uuid);
|
|
|
|
|
stmt.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Optional<User> forToken(String id) {
|
|
|
|
|
AccessToken token = accessTokens.get(id);
|
|
|
|
|
@@ -85,29 +98,8 @@ public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
try {
|
|
|
|
|
var rs = conn.prepareStatement(COUNT_USERS).executeQuery();
|
|
|
|
|
var count = rs.next() ? rs.getInt(1) : 0;
|
|
|
|
|
if (count < 1) {
|
|
|
|
|
var stmt = conn.prepareStatement(INSERT_USER);
|
|
|
|
|
stmt.setString(1, defaultUser.uuid());
|
|
|
|
|
stmt.setString(2, defaultUser.hashedPassword());
|
|
|
|
|
stmt.setString(3, defaultUser.email());
|
|
|
|
|
stmt.setLong(4, defaultUser.sessionDuration().toMinutes());
|
|
|
|
|
stmt.setString(5, defaultUser.username());
|
|
|
|
|
stmt.setString(6, defaultUser.realName());
|
|
|
|
|
stmt.execute();
|
|
|
|
|
}
|
|
|
|
|
rs.close();
|
|
|
|
|
conn.setAutoCommit(false);
|
|
|
|
|
dropPermissionsOf(defaultUser.uuid());
|
|
|
|
|
|
|
|
|
|
var stmt = conn.prepareStatement(INSERT_PERMISSIONS);
|
|
|
|
|
stmt.setString(1, defaultUser.uuid());
|
|
|
|
|
for (Permission perm : Permission.values()) {
|
|
|
|
|
if (defaultUser.hasPermission(perm)) {
|
|
|
|
|
stmt.setString(2, perm.toString());
|
|
|
|
|
stmt.execute();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
conn.commit();
|
|
|
|
|
if (count < 1) save(defaultUser);
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
@@ -143,10 +135,6 @@ public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
conn.setAutoCommit(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createUserStoreTables() throws SQLException {
|
|
|
|
|
conn.prepareStatement(CREATE_USER_TABLE).execute();
|
|
|
|
|
conn.prepareStatement(CREATE_USER_PERMISSION_TABLE).execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<User> list() {
|
|
|
|
|
@@ -181,37 +169,97 @@ public class SqliteUserService extends SqliteStore implements UserService {
|
|
|
|
|
var user = rs.getString("username");
|
|
|
|
|
var name = rs.getString("realname");
|
|
|
|
|
var mail = rs.getString("email");
|
|
|
|
|
var mins = rs.getLong("sessionDuration");
|
|
|
|
|
var mins = rs.getLong("session_duration");
|
|
|
|
|
return new User(user, pass, name, mail, uuid).sessionDuration(Duration.ofMinutes(mins));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Set<User> find(String key) {
|
|
|
|
|
return Set.of();
|
|
|
|
|
public Set<User> find(String idOrEmail) {
|
|
|
|
|
try {
|
|
|
|
|
var result = new HashSet<User>();
|
|
|
|
|
var stmt = conn.prepareStatement(FIND_USER); // TODO: implement test for this query
|
|
|
|
|
stmt.setString(1, idOrEmail);
|
|
|
|
|
stmt.setString(2, "%" + idOrEmail + "%");
|
|
|
|
|
stmt.setString(3, "%" + idOrEmail + "%");
|
|
|
|
|
stmt.setString(4, idOrEmail);
|
|
|
|
|
var rs = stmt.executeQuery();
|
|
|
|
|
while (rs.next()) result.add(userFrom(rs));
|
|
|
|
|
rs.close();
|
|
|
|
|
return result;
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Optional<User> load(String id) {
|
|
|
|
|
return Optional.empty();
|
|
|
|
|
try {
|
|
|
|
|
User user = null;
|
|
|
|
|
var stmt = conn.prepareStatement(LOAD_USER);
|
|
|
|
|
stmt.setString(1, id);
|
|
|
|
|
var rs = stmt.executeQuery();
|
|
|
|
|
if (rs.next()) user = userFrom(rs);
|
|
|
|
|
return nullable(user);
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Optional<User> load(String username, String password) {
|
|
|
|
|
return Optional.empty();
|
|
|
|
|
var candidates = find(username);
|
|
|
|
|
for (var user : candidates) {
|
|
|
|
|
if (passwordMatches(password, user.hashedPassword())) return Optional.of(user);
|
|
|
|
|
}
|
|
|
|
|
return empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean passwordMatches(String password, String hashedPassword) {
|
|
|
|
|
return false;
|
|
|
|
|
return hasher.matches(password, hashedPassword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public <T extends UserService> T save(User user) {
|
|
|
|
|
return null;
|
|
|
|
|
public SqliteUserService save(User user) {
|
|
|
|
|
try {
|
|
|
|
|
conn.setAutoCommit(false);
|
|
|
|
|
var stmt = conn.prepareStatement(INSERT_USER);
|
|
|
|
|
stmt.setString(1, user.uuid());
|
|
|
|
|
stmt.setString(2, user.hashedPassword());
|
|
|
|
|
stmt.setString(3, user.email());
|
|
|
|
|
stmt.setLong(4, user.sessionDuration().toMinutes());
|
|
|
|
|
stmt.setString(5, user.username());
|
|
|
|
|
stmt.setString(6, user.realName());
|
|
|
|
|
stmt.execute();
|
|
|
|
|
dropPermissionsOf(user.uuid());
|
|
|
|
|
|
|
|
|
|
stmt = conn.prepareStatement(INSERT_PERMISSIONS);
|
|
|
|
|
stmt.setString(1, user.uuid());
|
|
|
|
|
for (Permission perm : Permission.values()) {
|
|
|
|
|
if (user.hasPermission(perm)) {
|
|
|
|
|
stmt.setString(2, perm.toString());
|
|
|
|
|
stmt.execute();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
conn.commit();
|
|
|
|
|
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public <T extends UserService> T updatePassword(User user, String plaintextPassword) {
|
|
|
|
|
return null;
|
|
|
|
|
public SqliteUserService updatePassword(User user, String plaintextPassword) {
|
|
|
|
|
try {
|
|
|
|
|
var stmt = conn.prepareStatement(UPDATE_PASSWORD);
|
|
|
|
|
stmt.setString(1, user.uuid());
|
|
|
|
|
var hashedPassword = hasher.hash(plaintextPassword, uuid());
|
|
|
|
|
stmt.setString(2, hashedPassword);
|
|
|
|
|
stmt.execute();
|
|
|
|
|
return this;
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|