|
|
@ -6,6 +6,7 @@ import static de.srsoftware.oidc.api.Constants.*; |
|
|
|
import static de.srsoftware.oidc.api.data.Permission.*; |
|
|
|
import static de.srsoftware.oidc.api.data.Permission.*; |
|
|
|
import static de.srsoftware.utils.Optionals.emptyIfBlank; |
|
|
|
import static de.srsoftware.utils.Optionals.emptyIfBlank; |
|
|
|
import static de.srsoftware.utils.Paths.configDir; |
|
|
|
import static de.srsoftware.utils.Paths.configDir; |
|
|
|
|
|
|
|
import static de.srsoftware.utils.Paths.extension; |
|
|
|
import static de.srsoftware.utils.Strings.uuid; |
|
|
|
import static de.srsoftware.utils.Strings.uuid; |
|
|
|
import static java.lang.System.Logger.Level.DEBUG; |
|
|
|
import static java.lang.System.Logger.Level.DEBUG; |
|
|
|
import static java.lang.System.Logger.Level.ERROR; |
|
|
|
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 com.sun.net.httpserver.HttpServer; |
|
|
|
import de.srsoftware.logging.ColorLogger; |
|
|
|
import de.srsoftware.logging.ColorLogger; |
|
|
|
import de.srsoftware.oidc.api.KeyManager; |
|
|
|
import de.srsoftware.oidc.api.*; |
|
|
|
import de.srsoftware.oidc.api.KeyStorage; |
|
|
|
|
|
|
|
import de.srsoftware.oidc.api.data.User; |
|
|
|
import de.srsoftware.oidc.api.data.User; |
|
|
|
import de.srsoftware.oidc.backend.*; |
|
|
|
import de.srsoftware.oidc.backend.*; |
|
|
|
import de.srsoftware.oidc.datastore.file.FileStore; |
|
|
|
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.PlaintextKeyStore; |
|
|
|
import de.srsoftware.oidc.datastore.file.UuidHasher; |
|
|
|
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.Forward; |
|
|
|
import de.srsoftware.oidc.web.StaticPages; |
|
|
|
import de.srsoftware.oidc.web.StaticPages; |
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.net.InetSocketAddress; |
|
|
|
import java.net.InetSocketAddress; |
|
|
|
import java.nio.file.Path; |
|
|
|
import java.nio.file.Path; |
|
|
|
import java.util.*; |
|
|
|
import java.util.*; |
|
|
|
import java.util.concurrent.Executors; |
|
|
|
import java.util.concurrent.Executors; |
|
|
|
import org.sqlite.SQLiteDataSource; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class Application { |
|
|
|
public class Application { |
|
|
|
public static final String API_CLIENT = "/api/client"; |
|
|
|
public static final String API_CLIENT = "/api/client"; |
|
|
@ -47,36 +48,71 @@ public class Application { |
|
|
|
private static final String INDEX = STATIC_PATH + "/index.html"; |
|
|
|
private static final String INDEX = STATIC_PATH + "/index.html"; |
|
|
|
private static final String WELL_KNOWN = "/.well-known"; |
|
|
|
private static final String WELL_KNOWN = "/.well-known"; |
|
|
|
private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG); |
|
|
|
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); |
|
|
|
var argMap = map(args); |
|
|
|
Optional<Path> basePath = argMap.get(BASE_PATH) instanceof Path p ? Optional.of(p) : empty(); |
|
|
|
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 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 config = new Configuration(configFile); |
|
|
|
var keyDir = storageFile.getParentFile().toPath().resolve("keys"); |
|
|
|
var defaultConfigDir = configDir(APP_NAME); |
|
|
|
var passwordHasher = new UuidHasher(); |
|
|
|
var passwordHasher = new UuidHasher(); |
|
|
|
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID); |
|
|
|
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 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); |
|
|
|
var defaultFile = defaultConfigDir.resolve("data.json"); |
|
|
|
{ // SQLite
|
|
|
|
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys"))); |
|
|
|
SQLiteDataSource dataSource = new SQLiteDataSource(); |
|
|
|
KeyStorage keyStore; |
|
|
|
var dbFile = storageFile.getParentFile().toPath().resolve("db.sqlite3").toFile(); |
|
|
|
if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) { |
|
|
|
dataSource.setUrl("jdbc:sqlite:%s".formatted(dbFile)); |
|
|
|
keyStore = new PlaintextKeyStore(keyStorageLocation.toPath()); |
|
|
|
var conn = dataSource.getConnection(); |
|
|
|
} else { // SQLite
|
|
|
|
|
|
|
|
var conn = connectionProvider.get(keyStorageLocation); |
|
|
|
keyStore = new SqliteKeyStore(conn); |
|
|
|
keyStore = new SqliteKeyStore(conn); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
KeyManager keyManager = new RotatingKeyManager(keyStore); |
|
|
|
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); |
|
|
|
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); |
|
|
|
var staticPages = (StaticPages) new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server); |
|
|
|
var staticPages = (StaticPages) new StaticPages(basePath).bindPath(STATIC_PATH, FAVICON).on(server); |
|
|
|
new Forward(INDEX).bindPath(ROOT).on(server); |
|
|
|
new Forward(INDEX).bindPath(ROOT).on(server); |
|
|
|
new WellKnownController().bindPath(WELL_KNOWN).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
|
|
|
|
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 TokenController(authService, clientService, keyManager, userService, tokenControllerConfig).bindPath(API_TOKEN).on(server); |
|
|
|
new ClientController(fileStore, fileStore, fileStore).bindPath(API_CLIENT).on(server); |
|
|
|
new ClientController(authService, clientService, sessionService).bindPath(API_CLIENT).on(server); |
|
|
|
new KeyStoreController(keyStore).bindPath(JWKS).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.setExecutor(Executors.newCachedThreadPool()); |
|
|
|
server.start(); |
|
|
|
server.start(); |
|
|
|
} |
|
|
|
} |
|
|
|