preparing sqlite-based services
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -6,6 +6,7 @@ import static de.srsoftware.oidc.api.Constants.*;
|
||||
import static de.srsoftware.oidc.api.data.Permission.*;
|
||||
import static de.srsoftware.utils.Optionals.emptyIfBlank;
|
||||
import static de.srsoftware.utils.Paths.configDir;
|
||||
import static de.srsoftware.utils.Paths.extension;
|
||||
import static de.srsoftware.utils.Strings.uuid;
|
||||
import static java.lang.System.Logger.Level.DEBUG;
|
||||
import static java.lang.System.Logger.Level.ERROR;
|
||||
@@ -14,21 +15,21 @@ import static java.util.Optional.empty;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import de.srsoftware.logging.ColorLogger;
|
||||
import de.srsoftware.oidc.api.KeyManager;
|
||||
import de.srsoftware.oidc.api.KeyStorage;
|
||||
import de.srsoftware.oidc.api.*;
|
||||
import de.srsoftware.oidc.api.data.User;
|
||||
import de.srsoftware.oidc.backend.*;
|
||||
import de.srsoftware.oidc.datastore.file.FileStore;
|
||||
import de.srsoftware.oidc.datastore.file.FileStoreProvider;
|
||||
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
|
||||
import de.srsoftware.oidc.datastore.file.UuidHasher;
|
||||
import de.srsoftware.oidc.datastore.sqlite.SqliteKeyStore;
|
||||
import de.srsoftware.oidc.datastore.sqlite.*;
|
||||
import de.srsoftware.oidc.web.Forward;
|
||||
import de.srsoftware.oidc.web.StaticPages;
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.sqlite.SQLiteDataSource;
|
||||
|
||||
public class Application {
|
||||
public static final String API_CLIENT = "/api/client";
|
||||
@@ -42,41 +43,76 @@ public class Application {
|
||||
public static final String ROOT = "/";
|
||||
public static final String STATIC_PATH = "/web";
|
||||
|
||||
private static final String BASE_PATH = "basePath";
|
||||
private static final String FAVICON = "/favicon.ico";
|
||||
private static final String INDEX = STATIC_PATH + "/index.html";
|
||||
private static final String WELL_KNOWN = "/.well-known";
|
||||
private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG);
|
||||
private static final String BASE_PATH = "basePath";
|
||||
private static final String FAVICON = "/favicon.ico";
|
||||
private static final String INDEX = STATIC_PATH + "/index.html";
|
||||
private static final String WELL_KNOWN = "/.well-known";
|
||||
private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG);
|
||||
private static ConnectionProvider connectionProvider = new ConnectionProvider();
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var argMap = map(args);
|
||||
Optional<Path> basePath = argMap.get(BASE_PATH) instanceof Path p ? Optional.of(p) : empty();
|
||||
var configFile = (argMap.get(CONFIG_PATH) instanceof Path p ? p : configDir(APP_NAME).resolve("config.json")).toFile();
|
||||
var storageFile = configDir(APP_NAME).resolve("data.json").toFile();
|
||||
var keyDir = storageFile.getParentFile().toPath().resolve("keys");
|
||||
var passwordHasher = new UuidHasher();
|
||||
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
||||
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS, MANAGE_PERMISSIONS, MANAGE_SMTP, MANAGE_USERS);
|
||||
KeyStorage keyStore = new PlaintextKeyStore(keyDir);
|
||||
{ // SQLite
|
||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
||||
var dbFile = storageFile.getParentFile().toPath().resolve("db.sqlite3").toFile();
|
||||
dataSource.setUrl("jdbc:sqlite:%s".formatted(dbFile));
|
||||
var conn = dataSource.getConnection();
|
||||
keyStore = new SqliteKeyStore(conn);
|
||||
var argMap = map(args);
|
||||
Optional<Path> basePath = argMap.get(BASE_PATH) instanceof Path p ? Optional.of(p) : empty();
|
||||
var configFile = (argMap.get(CONFIG_PATH) instanceof Path p ? p : configDir(APP_NAME).resolve("config.json")).toFile();
|
||||
var config = new Configuration(configFile);
|
||||
var defaultConfigDir = configDir(APP_NAME);
|
||||
var passwordHasher = new UuidHasher();
|
||||
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
||||
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS, MANAGE_PERMISSIONS, MANAGE_SMTP, MANAGE_USERS);
|
||||
var defaultFile = defaultConfigDir.resolve("data.json");
|
||||
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys")));
|
||||
KeyStorage keyStore;
|
||||
if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) {
|
||||
keyStore = new PlaintextKeyStore(keyStorageLocation.toPath());
|
||||
} else { // SQLite
|
||||
var conn = connectionProvider.get(keyStorageLocation);
|
||||
keyStore = new SqliteKeyStore(conn);
|
||||
}
|
||||
|
||||
KeyManager keyManager = new RotatingKeyManager(keyStore);
|
||||
FileStore fileStore = new FileStore(storageFile, passwordHasher).init(firstUser);
|
||||
FileStoreProvider fileStoreProvider = new FileStoreProvider(passwordHasher);
|
||||
|
||||
var userStorageLocation = new File(config.getOrDefault("user_storage",defaultFile));
|
||||
var userService = switch (extension(userStorageLocation).toLowerCase()){
|
||||
case "db", "sqlite", "sqlite3" -> new SqliteUserService(connectionProvider.get(userStorageLocation));
|
||||
default -> fileStoreProvider.get(userStorageLocation);
|
||||
};
|
||||
userService.init(firstUser);
|
||||
|
||||
var mailConfigLocation = new File(config.getOrDefault("mail_config_storage",defaultFile));
|
||||
var mailConfig = switch (extension(mailConfigLocation)){
|
||||
case "db", "sqlite", "sqlite3" -> new SqliteMailConfig(connectionProvider.get(userStorageLocation));
|
||||
default -> fileStoreProvider.get(mailConfigLocation);
|
||||
};
|
||||
|
||||
var sessionStore = new File(config.getOrDefault("session_storage",defaultFile));
|
||||
var sessionService = switch (extension(sessionStore)){
|
||||
case "db", "sqlite", "sqlite3" -> new SqliteSessionService(connectionProvider.get(sessionStore));
|
||||
default -> fileStoreProvider.get(sessionStore);
|
||||
};
|
||||
|
||||
var authServiceLocation = new File(config.getOrDefault("auth_store",defaultFile));
|
||||
AuthorizationService authService = switch (extension(authServiceLocation)){
|
||||
case "db", "sqlite", "sqlite3" -> new SqliteAuthService(connectionProvider.get(sessionStore));
|
||||
default -> fileStoreProvider.get(sessionStore);
|
||||
};
|
||||
|
||||
var clientStore = new File(config.getOrDefault("client_store",defaultFile));
|
||||
ClientService clientService = switch (extension(clientStore)){
|
||||
case "db", "sqlite", "sqlite3" -> new SqliteClientService(connectionProvider.get(sessionStore));
|
||||
default -> fileStoreProvider.get(sessionStore);
|
||||
};
|
||||
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
||||
var staticPages = (StaticPages) new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server);
|
||||
new Forward(INDEX).bindPath(ROOT).on(server);
|
||||
new WellKnownController().bindPath(WELL_KNOWN).on(server);
|
||||
new UserController(fileStore, fileStore, fileStore, staticPages).bindPath(API_USER).on(server);
|
||||
new UserController(mailConfig, sessionService, userService, staticPages).bindPath(API_USER).on(server);
|
||||
var tokenControllerConfig = new TokenController.Configuration("https://lightoidc.srsoftware.de", 10); // TODO configure or derive from hostname
|
||||
new TokenController(fileStore, fileStore, keyManager, fileStore, tokenControllerConfig).bindPath(API_TOKEN).on(server);
|
||||
new ClientController(fileStore, fileStore, fileStore).bindPath(API_CLIENT).on(server);
|
||||
new TokenController(authService, clientService, keyManager, userService, tokenControllerConfig).bindPath(API_TOKEN).on(server);
|
||||
new ClientController(authService, clientService, sessionService).bindPath(API_CLIENT).on(server);
|
||||
new KeyStoreController(keyStore).bindPath(JWKS).on(server);
|
||||
new EmailController(fileStore, fileStore).bindPath(API_EMAIL).on(server);
|
||||
new EmailController(mailConfig, sessionService).bindPath(API_EMAIL).on(server);
|
||||
server.setExecutor(Executors.newCachedThreadPool());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* © SRSoftware 2024 */
|
||||
package de.srsoftware.oidc.app;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class Configuration {
|
||||
private final JSONObject json;
|
||||
private final Path storageFile;
|
||||
|
||||
public Configuration(File storage) throws IOException {
|
||||
storageFile = storage.toPath();
|
||||
if (!storage.exists()) {
|
||||
var parent = storage.getParentFile();
|
||||
if (!parent.exists() && !parent.mkdirs()) throw new FileNotFoundException("Failed to create directory %s".formatted(parent));
|
||||
Files.writeString(storageFile, "{}");
|
||||
}
|
||||
json = new JSONObject(Files.readString(storageFile));
|
||||
}
|
||||
|
||||
public String getOrDefault(String key, Object defaultValue) {
|
||||
if (!json.has(key)) {
|
||||
json.put(key, defaultValue.toString());
|
||||
save();
|
||||
}
|
||||
return json.getString(key);
|
||||
}
|
||||
|
||||
public Configuration save() {
|
||||
try {
|
||||
Files.writeString(storageFile, json.toString(2));
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user