I am trying to configure my API and restrict the access depending on the authority of the jwt, my authority has this format: "ROLE_MON|1", or "ROLE_MON|2" or "ROLE_MON|3", etc, the number after the character '|' can be any number.
I configured like this:
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/users").access("hasAuthority('^ROLE_MON\\|[0-9]+$')")
.anyRequest().authenticated();
But doesn't work.
Note: Without regex this works, for example
.antMatchers("/users").access("hasAuthority('ROLE_MON|1')")
works for authority 'ROLE_MON|1'
I am using this version of springBoot:
plugins {
id 'java'
id 'org.springframework.boot' version '2.6.13'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
My dependences:
implementation(group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.5.2.RELEASE'){
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
implementation(group: 'org.springframework.security.oauth.boot', name: 'spring-security-oauth2-autoconfigure', version: '2.6.8'){
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
implementation(group: 'org.springframework.boot', name: 'spring-boot-starter-security'){
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
implementation(group: 'org.springframework.security', name: 'spring-security-jwt', version: '1.0.9.RELEASE'){
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
I tried:
antMatchers("/users").access("hasAuthority('^ROLE_MON.*')")
and
antMatchers("/users").access("hasAuthority('^ROLE_MON.+')")
But doesn't work
This is not possible using the default RoleVoter
Spring security only checks for string equality so you can't use a regex here. In the examples you have given the token is being checked for the literal strings^ROLE_MON.* and ^ROLE_MON.+ in your token respectively, which do not exist. There are a few ways of working around this and the best solution will depend on your needs.
Write your own AccessDecisionVoter to provide REGEX matching:
You can write a custom AccessDecisionVoter that can accept a regular expression. If you aren't able to enumerate your existing roles (perhaps because they are dynamically generated or because there are many of them), this will be your only best solution.
Create a generic role that you can use to give privileges to your existing roles:
If you want all of these roles to be able to access this endpoint, you can add them to a role hierarchy under a generic role such that each ROLE_MON|.* has the permissions of the generic role. You can then add the generic role's name to the hasAuthority string and use that to configure access to your endpoints. This will only be possible if you can reasonably enumerate the roles you want to use.
Use hasAnyAuthority and a list of roles
This method allows you to provide a list or roles, for example:
antMatchers("/users").access("hasAnyAuthority('ROLE_MON|1','ROLE_MON|2','ROLE_MON|3')")
This will be the easiest solution if the number or roles is small and will not be difficult to maintain, but can be ugly or impossible depending how many roles you have.
Related
I have few dependencies, that have the same transitive dependency for tests, resulting in test dependencies ending up in my 'implementation'.
Can I somehow smoothly redirect said dependency to 'testImplementation' or do I have to perform something like:
implementation(A){exclude T}
implementation(B){exclude T}
implementation(C){exclude T}
testImplementation(T)
Something a bit better would be:
configurations {
implementation {
exclude(T)
}
}
testImplementation(T)
Then you won't need to explicitly exclude T for each implementation dependency that might pull it in.
How to add a dependency constraint to a project from a custom gradle plugin?
I want to create a gradle plugin that will constrain transitive dependencies in the project the plugin is applied to. Ideally, I want this plugin to follow the following rules:
Only try and apply a constraint to a project if the it exists in the project. E.g. only apply a constraint on dependency X if dependency X is pulled into the project.
Apply the constraints automatically on application of the plugin, I don't want to create/run an extra task or anything like that.
(Bonus) Don't apply the constraints to primary dependencies, only transitive dependencies.
Here's what I have so far.
class ConstraintPlugin implements Plugin<Project> {
private Map<String, String> constraintMap = [
"jackson-core": "2.13.2",
"logback-classic": "2.3.9"
]
#Override
void apply(Project project) {
project.configurations.each {config ->
config.resolvedConfiguration.resolvedArtifacts.each {dep ->
if (constraintMap.containsKey(dep.name)) {
ModuleVersionIdentifier id = dep.moduleVersion.id
String constraintVersion = constraintMap.get(id.name)
DependencyConstraint dc = new DefaultDependencyConstraint(id.group, id.name, constraintVersion)
config.dependencyConstraints.add(dc)
}
}
}
I'm using a map instantiated within the constraint plugin class with the names of the dependencies I want to constrain along with the versions to constrain to in the project this plugin is applied to. Here I want to constrain the jackson-core and logback-classic dependencies.
My first thought was to run through each configuration and see if that dependency name is present within that configuration. If so, create a new DependencyConstraint and add it to the configuration. However this is not working when I apply the plugin to another project.
Also note I am not disregarding primary dependencies quite yet, this feature is more like a "nice to have" at this point, I haven't figured out how to only look at transitive dependencies within a given configuration.
I think my issue lies with how I am applying the new DependencyConstraint programmatically but I can't seem to find a way to add a constraint that works. The only other way I've tried is project.dependencies.constraints.create(dc) which also doesn't work.
The title basically says it all.
I am trying to create a plugin that can be configured and depending on the configuration, the task provided by the plugin adds compileOnly or implementation deppendencies to the project.
The resources on writing custom Gradle plugins are abhorrent (especially in Kotlin instead of Groovy) and I can't figure out how to do this myself.
This is where I'm at with my custom plugin code:
class SpigotVersioner: Plugin<Project> {
override fun apply(project: Project) {
println("Latest spigot version: ${WebScraper.getLatestVersion()}")
val extension = project.extensions.create("spigot", SpigotExtension::class.java)
extension.apiVersion.set("latest")
extension.bukkitVersion.set("latest")
project.task("compileSpigotAPI") {
it.group = "spigot"
it.description = "Adds the spigot api implementation to the project."
it.doLast {
val apiVersion = extension.apiVersion.get()
val dependency = deriveDependencyStr(apiVersion)
//DOESN'T WORK!
project.dependencies {
compileOnly(dependency)
}
//WHAT ARE THESE PARAMETERS SUPPOSED TO BE?
project.dependencies.add(configurationName: String, dependencyNotation: Any)
}
}
}
}
This is supposed to mimic something like
dependencies {
compileOnly 'my.derived.dependency.str:apiVersion:xy'
}
only the dependency being added is supposed to be configurable via an extension.
If possible, I'd like to extend this to also add the appropriate repository as well but the dependency issue is more important.
Bit of an old question now, but I too struggled with this so hopefully this answer is of use to someone.
//WHAT ARE THESE PARAMETERS SUPPOSED TO BE?
project.dependencies.add(configurationName: String, dependencyNotation: Any)
The configurationName is the configuration that you wish to add the dependency to e.g. implementation, testImplementation or api etc.
The dependencyNotation can be any of the following:
String Notation: Simply a String written using Gradle dependency notation e.g. com.mycompany:my-awesome-dependency:1.2.3. There are ways to also specify things like strictness when using these 'simple' declarations, this is somewhat documented here.
Map Notation: This is where you pass a Map<String, String> containing key-value pairs representing the dependency. The documentation on this is either non existent or elusive, but for example: "group": "com.mycompany", "name": "my-awesome-dependency", "version": "1.2.3".
Dependency Interface: This is where you pass in an object that implements one of the Dependency interfaces that the Gradle API provides. The most basic being org.gradle.api.artifacts.Dependency. The main issue with this method is again that the documentation is either elusive or non-existent. I cannot see a way to have Gradle create one of these objects (or see any pre implemented classes in the public API). You could always just implement the interface but there are some methods on there like contentEquals and copy() which seem overkill to implement.
My recommendation if it suits your use case would be to use the first option above.
How do I inherit constraints from BOMs for all configurations in an ergonomic way ? The following is how I am currently doing it. I am on Gradle 6.6.1.
dependencies {
compileOnly(platform('x:y:z'))
implementation(platform('x:y:z'))
annotationProcessor(platform('x:y:z'))
testAnnotationProcessor(platform('x:y:z'))
testImplementation(platform('x:y:z'))
testCompileOnly(platform('x:y:z'))
}
Well, you could do it by abusing the configurations.all method like this:
// Groovy DSL
configurations.all { config ->
project.dependencies.add(config.name, project.dependencies.platform('x:y:z'))
}
But you don't need to add the platform to all those configurations in the first place. Because most of them are resolvable and extend both api and implementation, you typically only need to add it to one of those. The only exception is annotationProcessor, which is isolated (but is extended by testAnnotationProcessor). So you can still reduce it to:
// Groovy DSL
dependencies {
implementation platform('x:y:z') // or api
annotationProcessor platform('x:y:z')
}
This is in my opinion more readable and more precise.
A common use case is for Spring Boot. It could look like this:
// Groovy DSL
import org.springframework.boot.gradle.plugin.SpringBootPlugin
plugins {
id 'java'
id 'org.springframework.boot' version '2.4.2'
}
dependencies {
// BOMS (Note that using the "BOM_COORDINATES" variable makes it match the version of the plugin)
implementation platform(SpringBootPlugin.BOM_COORDINATES)
annotationProcessor platform(SpringBootPlugin.BOM_COORDINATES)
// Actual dependencies
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
}
Interestingly, there is a Gradle issue on this exact use case. Here they explained that typically you don't need this functionality, and where you do it is better to be explicit about it rather than just "hammer" a set of dependency versions onto everything.
My application uses configuration file (parsed as #ConfigurationProperties) to start multiple network streams with different services/processors in them. A good analogy would be setting up Netty channel pipelines from external config.
YAML properties look like this:
streams:
- type: tcp-server
port: 2000
services: # this is the tricky part
- type: watchdogTimer
- type: someFormatConverter
- type: dbLogger
options:
- table: "things"
I want services to be easily defined, created, and located. So I decided to use Spring as registry for them - they can be prototype-scoped Spring beans, type is Spring Bean name and they are created like this:
// Kotlin language
#Autowired
private lateinit var appContext: ApplicationContext
fun createStreamService(stream: MyStream, type: String, options:Map<String,String>): MyStreamService {
return appContext.getBean(type, stream, options) as MyStreamService
}
My problem is that even if service doesn't need stream or options reference, I still must have them in constructor (stream:MyStream, options:Map<String,String>). Is it possible to supply arguments that might not exist in bean implementation signature? I know you can make them optional in sense that the call site can not supply them, but what about the other way?
In case there would be no better solution, I'm going to pass single object encapsulating all arguments (stream and options).
While still an enforcement of implementation constructor signature, it is more concise and leaves space for extension without breaking compatibility.