I need to do some lightweight attribute-based access control in a Spring Boot application.
(The background is not related to my question, but here's a brief summary: There are different types of users, different types of resources (each with attributes), and different levels of access. I need to evaluate access to resources very dynamically - hence using enums to separate permission evaluation from other logic.)
I want to store the configuration for the permissions in application.yml so I can configure it easily, and change it without making code changes.
I wanted to set this up using @ConfigurationProperties, so that the config in application.yml had autocompletion and could be softly validated. This works very nicely for other properties, but not when I start using Maps.
I can change the keys to strings, and map them in the config class (even though I lose type-completion), but the documentation in a few places indicates that that Maps should use enum, and metadata will be generated correctly - so I am confused.
I have correctly set up spring-boot-configuration-processor (it works for other properties) and it is generating the metadata JSON (see below).
MyConfig.kt
package example.myapp
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
@ConstructorBinding
@ConfigurationProperties("my-app")
data class MyConfig(
val permissions: Map<Role, Map<ResourceId, Set<Permission>>>,
val owner: Map<ResourceId, Set<Permission>>
) {
enum class Role {
OWNER,
VIEWER,
}
enum class Permission {
VIEW,
EDIT,
}
enum class ResourceId {
ARTICLE_TITLE,
ARTICLE_BODY,
}
}
spring-configuration-metadata.json
{
"groups": [
{
"name": "my-app",
"type": "example.myapp.MyConfig",
"sourceType": "example.myapp.MyConfig"
}
],
"properties": [
{
"name": "my-app.owner",
"type": "java.util.Map<example.myapp.MyConfig$ResourceId,? extends java.util.Set<? extends example.myapp.MyConfig.Permission>>",
"sourceType": "example.myapp.MyConfig"
},
{
"name": "my-app.permissions",
"type": "java.util.Map<example.myapp.MyConfig$Role,? extends java.util.Map<example.myapp.MyConfig.ResourceId,? extends java.util.Set<? extends example.myapp.MyConfig.Permission>>>",
"sourceType": "example.myapp.MyConfig"
}
],
"hints": []
}
Versions
- Spring Boot 2.7.1
- Kotlin 1.7.10
Update
Changing Map<> to HashMap<> seems to help avoid the errors in the keys, but doesn't affect the values - I get a misleading warning: Cannot resolve configuration property
@ConstructorBinding
@ConfigurationProperties("my-app")
data class MyConfig(
val permissions: HashMap<Role, HashMap<ResourceId, HashSet<Permission>>>,
val owner: HashMap<ResourceId, HashSet<Permission>>
)

