Refactored ConfigProperties

This commit is contained in:
grimsi
2024-09-22 19:29:57 +02:00
parent 3e64cfd30a
commit 1b01c368ea
8 changed files with 149 additions and 163 deletions
@@ -43,11 +43,11 @@ class ConfigEndpoint(
@PermitAll
fun isSsoEnabled(): Boolean? {
return config.get(ConfigProperties.SsoEnabled)
return config.get(ConfigProperties.SSO.OIDC.Enabled)
}
@PermitAll
fun getLogoutUrl(): String? {
return config.get(ConfigProperties.SsoLogoutUrl)
return config.get(ConfigProperties.SSO.OIDC.LogoutUrl)
}
}
@@ -13,119 +13,133 @@ sealed class ConfigProperties<T : Serializable>(
) {
/** Libraries */
data object LibraryAllowPublicAccess : ConfigProperties<Boolean>(
Boolean::class,
"library.allow-public-access",
"Allow access to game libraries without login",
false
)
sealed class Libraries {
data object AllowPublicAccess : ConfigProperties<Boolean>(
Boolean::class,
"library.allow-public-access",
"Allow access to game libraries without login",
false
)
data object LibraryEnableFilesystemWatcher : ConfigProperties<Boolean>(
Boolean::class,
"library.scan.enable-filesystem-watcher",
"Enable automatic library scanning using file system watchers",
true
)
sealed class Scan {
data object EnableFilesystemWatcher : ConfigProperties<Boolean>(
Boolean::class,
"library.scan.enable-filesystem-watcher",
"Enable automatic library scanning using file system watchers",
true
)
}
data object LibraryMetadataUpdateEnabled : ConfigProperties<Boolean>(
Boolean::class,
"library.metadata.update.enabled",
"Enable periodic refresh of video game metadata",
true
)
sealed class Metadata {
data object UpdateEnabled : ConfigProperties<Boolean>(
Boolean::class,
"library.metadata.update.enabled",
"Enable periodic refresh of video game metadata",
true
)
data object LibraryMetadataUpdateSchedule : ConfigProperties<String>(
String::class,
"library.metadata.update.schedule",
"Schedule for periodic metadata refresh in cron format",
"0 0 * * 0"
)
data object UpdateSchedule : ConfigProperties<String>(
String::class,
"library.metadata.update.schedule",
"Schedule for periodic metadata refresh in cron format",
"0 0 * * 0"
)
}
}
/** User management */
data object UsersAllowNewSignUps : ConfigProperties<Boolean>(
Boolean::class,
"users.sign-ups.allow",
"Allow new users to sign up by themselves",
false
)
sealed class Users {
sealed class SignUps {
data object Allow : ConfigProperties<Boolean>(
Boolean::class,
"users.sign-ups.allow",
"Allow new users to sign up by themselves",
false
)
data object UsersConfirmNewSignUps : ConfigProperties<Boolean>(
Boolean::class,
"users.sign-ups.confirm",
"Admins need to confirm new users",
false
)
data object Confirm : ConfigProperties<Boolean>(
Boolean::class,
"users.sign-ups.confirm",
"Admins need to confirm new users",
false
)
}
}
/** Single Sign-On */
data object SsoEnabled : ConfigProperties<Boolean>(
Boolean::class,
"sso.oidc.enabled",
"Enable SSO via OIDC/OAuth2",
false
)
sealed class SSO {
sealed class OIDC {
data object Enabled : ConfigProperties<Boolean>(
Boolean::class,
"sso.oidc.enabled",
"Enable SSO via OIDC/OAuth2",
false
)
data object SsoClientId : ConfigProperties<String>(
String::class,
"sso.oidc.client-id",
"Client ID"
)
data object MatchExistingUsersBy : ConfigProperties<MatchUsersBy>(
MatchUsersBy::class,
"sso.oidc.match-existing-users-by",
"Match existing users by",
MatchUsersBy.username,
MatchUsersBy.entries
)
data object SsoClientSecret : ConfigProperties<String>(
String::class,
"sso.oidc.client-secret",
"Client secret"
)
data object AutoRegisterNewUsers : ConfigProperties<Boolean>(
Boolean::class,
"sso.oidc.auto-register-new-users",
"Automatically create new users after registration",
true
)
data object SsoIssuerUrl : ConfigProperties<String>(
String::class,
"sso.oidc.issuer-url",
"Issuer URL"
)
data object ClientId : ConfigProperties<String>(
String::class,
"sso.oidc.client-id",
"Client ID"
)
data object SsoAuthorizeUrl : ConfigProperties<String>(
String::class,
"sso.oidc.authorize-url",
"Authorize URL"
)
data object ClientSecret : ConfigProperties<String>(
String::class,
"sso.oidc.client-secret",
"Client secret"
)
data object SsoTokenUrl : ConfigProperties<String>(
String::class,
"sso.oidc.token-url",
"Token URL"
)
data object IssuerUrl : ConfigProperties<String>(
String::class,
"sso.oidc.issuer-url",
"Issuer URL"
)
data object SsoUserInfoUrl : ConfigProperties<String>(
String::class,
"sso.oidc.userinfo-url",
"Userinfo URL"
)
data object AuthorizeUrl : ConfigProperties<String>(
String::class,
"sso.oidc.authorize-url",
"Authorize URL"
)
data object SsoJwksUrl : ConfigProperties<String>(
String::class,
"sso.oidc.jwks-url",
"JWKS URL"
)
data object TokenUrl : ConfigProperties<String>(
String::class,
"sso.oidc.token-url",
"Token URL"
)
data object SsoLogoutUrl : ConfigProperties<String>(
String::class,
"sso.oidc.logout-url",
"Logout URL"
)
data object UserInfoUrl : ConfigProperties<String>(
String::class,
"sso.oidc.userinfo-url",
"Userinfo URL"
)
data object SsoMatchExistingUsersBy : ConfigProperties<MatchUsersBy>(
MatchUsersBy::class,
"sso.oidc.match-existing-users-by",
"Match existing users by",
MatchUsersBy.username,
MatchUsersBy.entries
)
data object JwksUrl : ConfigProperties<String>(
String::class,
"sso.oidc.jwks-url",
"JWKS URL"
)
data object SsoAutoRegisterNewUsers : ConfigProperties<Boolean>(
Boolean::class,
"sso.oidc.auto-register-new-users",
"Automatically create new users after registration",
true
)
data object LogoutUrl : ConfigProperties<String>(
String::class,
"sso.oidc.logout-url",
"Logout URL"
)
}
}
/** Notifications */
sealed class Notifications {
@@ -166,58 +180,30 @@ sealed class ConfigProperties<T : Serializable>(
}
}
data object NotificationsTemplateNewUser : ConfigProperties<String>(
String::class,
"notifications.templates.new-user",
"Template for new user notifications"
)
data object NotificationsTemplateNewInvite : ConfigProperties<String>(
String::class,
"notifications.templates.new-invite",
"Template for new user notifications"
)
data object NotificationsTemplateNewPasswordRequest : ConfigProperties<String>(
String::class,
"notifications.templates.new-password-request",
"Template for new password request notifications"
)
data object NotificationsTemplateNewGame : ConfigProperties<String>(
String::class,
"notifications.templates.new-game",
"Template for new game notifications"
)
data object NotificationsTemplateNewGameRequest : ConfigProperties<String>(
String::class,
"notifications.templates.new-game-request",
"Template for new game request notifications"
)
/** Logs */
data object LogsFolder : ConfigProperties<String>(
String::class,
"logs.folder",
"Storage folder for log files",
"./logs"
)
sealed class Logs {
data object Folder : ConfigProperties<String>(
String::class,
"logs.folder",
"Storage folder for log files",
"./logs"
)
data object LogsMaxHistoryDays : ConfigProperties<Int>(
Int::class,
"logs.max-history-days",
"Log retention in days",
30
)
data object MaxHistoryDays : ConfigProperties<Int>(
Int::class,
"logs.max-history-days",
"Log retention in days",
30
)
data object LogsLevel : ConfigProperties<LogLevel>(
LogLevel::class,
"logs.level",
"Log level",
LogLevel.INFO,
LogLevel.entries
)
data object Level : ConfigProperties<LogLevel>(
LogLevel::class,
"logs.level",
"Log level",
LogLevel.INFO,
LogLevel.entries
)
}
}
enum class MatchUsersBy {
@@ -24,7 +24,7 @@ class DynamicAccessInterceptor(
// Check if method is annotated with @DynamicPublicAccess
if (method.isAnnotationPresent(DynamicPublicAccess::class.java)) {
// Check if user is authenticated or public access is enabled
if (request.userPrincipal != null || configService.get(ConfigProperties.LibraryAllowPublicAccess) == true) {
if (request.userPrincipal != null || configService.get(ConfigProperties.Libraries.AllowPublicAccess) == true) {
return true
}
@@ -49,7 +49,7 @@ class SecurityConfig(
super.configure(http)
if (config.get(ConfigProperties.SsoEnabled) == true) {
if (config.get(ConfigProperties.SSO.OIDC.Enabled) == true) {
setOAuth2LoginPage(http, "/oauth2/authorization/$ssoProviderKey")
// Use custom success handler to handle user registration
http.oauth2Login { oauth2Login -> oauth2Login.successHandler(ssoAuthenticationSuccessHandler) }
@@ -74,16 +74,16 @@ class SecurityConfig(
@Conditional(SsoEnabledCondition::class)
fun clientRegistrationRepository(): ClientRegistrationRepository? {
val clientRegistration = ClientRegistration.withRegistrationId(ssoProviderKey)
.clientId(config.get(ConfigProperties.SsoClientId))
.clientSecret(config.get(ConfigProperties.SsoClientSecret))
.clientId(config.get(ConfigProperties.SSO.OIDC.ClientId))
.clientSecret(config.get(ConfigProperties.SSO.OIDC.ClientSecret))
.scope("openid", "profile", "email")
.userNameAttributeName("preferred_username")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.issuerUri(config.get(ConfigProperties.SsoIssuerUrl))
.authorizationUri(config.get(ConfigProperties.SsoAuthorizeUrl))
.tokenUri(config.get(ConfigProperties.SsoTokenUrl))
.userInfoUri(config.get(ConfigProperties.SsoUserInfoUrl))
.jwkSetUri(config.get(ConfigProperties.SsoJwksUrl))
.issuerUri(config.get(ConfigProperties.SSO.OIDC.IssuerUrl))
.authorizationUri(config.get(ConfigProperties.SSO.OIDC.AuthorizeUrl))
.tokenUri(config.get(ConfigProperties.SSO.OIDC.TokenUrl))
.userInfoUri(config.get(ConfigProperties.SSO.OIDC.UserInfoUrl))
.jwkSetUri(config.get(ConfigProperties.SSO.OIDC.JwksUrl))
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.build()
@@ -33,7 +33,7 @@ class SsoAuthenticationSuccessHandler(
// If user is not registered via SSO, check if user is already registered by username or email
// This is meant to map existing users to SSO users
if (matchedUser == null) {
matchedUser = when (config.get(ConfigProperties.SsoMatchExistingUsersBy)) {
matchedUser = when (config.get(ConfigProperties.SSO.OIDC.MatchExistingUsersBy)) {
MatchUsersBy.username -> userService.getByUsername(oidcUser.preferredUsername)
MatchUsersBy.email -> userService.getByEmail(oidcUser.email)
else -> throw IllegalStateException("Unknown 'match users by' configuration")
@@ -43,7 +43,7 @@ class SsoAuthenticationSuccessHandler(
// User could not be found in the database
if (matchedUser == null) {
// Check if new user registration is enabled
if (config.get(ConfigProperties.SsoAutoRegisterNewUsers) == false) {
if (config.get(ConfigProperties.SSO.OIDC.AutoRegisterNewUsers) == false) {
response.sendRedirect("/")
return
}
@@ -21,9 +21,9 @@ class SsoEnabledCondition : Condition {
val password = environment.getProperty("spring.datasource.password");
val connection = DriverManager.getConnection(url, user, password);
connection.use { connection ->
val statement = connection.prepareStatement("SELECT \"value\" FROM app_config WHERE \"key\" = ?")
statement.setString(1, ConfigProperties.SsoEnabled.key)
connection.use { c ->
val statement = c.prepareStatement("SELECT \"value\" FROM app_config WHERE \"key\" = ?")
statement.setString(1, ConfigProperties.SSO.OIDC.Enabled.key)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
return resultSet.getBoolean("value")
@@ -41,9 +41,9 @@ class LogService(
@EventListener(ApplicationStartedEvent::class)
fun configureFileLogging() {
return configureFileLogging(
config.get(ConfigProperties.LogsFolder)!!,
config.get(ConfigProperties.LogsMaxHistoryDays)!!,
config.get(ConfigProperties.LogsLevel)!!
config.get(ConfigProperties.Logs.Folder)!!,
config.get(ConfigProperties.Logs.MaxHistoryDays)!!,
config.get(ConfigProperties.Logs.Level)!!
)
}