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