One type argument expected for interface Function <out R> - spring-boot

I am following a tutorial to implement JWT authentication with spring boot and java. But in my case I want to do it with Kotlin.
I am able to generate a jwt token but having issues when extracting claims from a jwt token.
In this tutorial there is a generic function to extract claims from a token.
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver {
final Claims claims = extractAllClaims(token);
return claimResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(KEY).parseClaimsJws(token).getBody();
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
I have written this in kotlin as follows.
fun <T> extractClaim(token: String, claimResolver: Function<Claims, T>): T {
val claims = extractAllClaims(token)
return claimResolver.apply(claims)
}
private fun extractAllClaims(token: String): Claims {
return Jwts.parser().setSigningKey(key).parseClaimsJws(token).body
}
fun extractUsername(token: String): String {
return extractClaim(token, Claims::getSubject)
}
I'm getting an error from Function<Claims, T> saying
One type argument expected for interface Function
Also I can see there are other options such as KFunction and KFunction1.
I am not much experienced in Kotlin and can someone help me to figure out the issue here or suggest a better way of doing this.

Function types are some different in Kotlin, try with this:
fun <T> extractClaim(token: String, claimsResolver: (Claims) -> T): T {
val claims: Claims = extractAllClaims(token)
return claimsResolver(claims)
}

Related

How to get Locale in Webflux Functional Endpoints?

I am using Spring's Functional Endpoints with Kotlin to create a web service, and I'm trying to figure out the idiomatic way to resolve the Locale off using the standard Accept-Language header.
Here's an example of what the code looks like:
val repository: PersonRepository = ...
val handler = PersonHandler(repository)
val route = coRouter {
GET("/person", handler::getPeople)
}
class PersonHandler(private val repository: PersonRepository) {
suspend fun getPeople(request: ServerRequest): ServerResponse {
val locale = /* ??? */
// create and return response
}
}
Spring documentation references using a LocaleContextResolver that resolves the Locale as part of the request, but I don't see a way to use it when using Functional Endpoints. You can get the raw values passed into the Accept-Language header off of ServiceRequest.headers().acceptLanguage() like so...
suspend fun getPeople(request: ServerRequest): ServerResponse {
val locale =
Locale.lookup(request.headers().acceptLanguage(), supportedLocales)
?: Locale.getDefault()
// create and return response
}
.... but isn't that just reimplementing the responsibility of LocaleContextResolver in every single Handler Function?
What is the idiomatic way to convert the Accept-Language header into the single/best supported Locale within Spring's Functional Endpoints?
I was able to do this with a before filter:
val repository: PersonRepository = ...
val handler = PersonHandler(repository)
val route = coRouter {
GET("/person", handler::getPeople)
before(::parseLocale)
}
fun parseLocale(request: ServerRequest): ServerRequest {
val locale = try {
Locale.lookup(request.headers().acceptLanguage(), wellKnownSupportedLocales) ?: wellKnownDefaultLocale
} catch (exception: RuntimeException) {
wellKnownDefaultLocale
}
return ServerRequest
.from(request)
.attribute("locale", locale)
.body(request.bodyToFlux())
.build()
}
class PersonHandler(private val repository: PersonRepository) {
suspend fun getPeople(request: ServerRequest): ServerResponse {
val locale = request.attributes("locale").get() as Locale
// create and return response
}
}

How to create custom claims in JWT using spring-authorization-server

I'm building an OAuth2 authorization server based on the experimental Spring project Spring Authorization Server
My use case is quite simple, fetch users from a DB, and based on some properties of the user, set some custom claims in the JWT being produced.
I haven't found a way to do so with Spring Authorization Server, the only way I could work out is to inject a jwtCustomizer object as part of the JwtEncoder bean definition:
#Bean
public JwtEncoder jwtEncoder(CryptoKeySource keySource) {
NimbusJwsEncoder jwtEncoder = new NimbusJwsEncoder(keySource);
jwtEncoder.setJwtCustomizer((headersBuilder, claimsBuilder) -> {
// Inject some headers and claims...
});
return jwtEncoder;
}
This obviously doesn't give me access to users information, therefore I can't set the claims I need at this point.
Did anyone manage to solve this problem?
The solution for this is in a test of the library
#Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {
Authentication principal = context.getPrincipal();
Set<String> authorities = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
context.getClaims().claim(AUTHORITIES_CLAIM, authorities);
}
};
}
You can try following way. Though it is Kotlin code, not Java, but approach should be clear:
import org.springframework.security.oauth2.provider.token.TokenEnhancer
class UserTokenEnhancer : TokenEnhancer {
override fun enhance(accessToken: OAuth2AccessToken,
authentication: OAuth2Authentication): OAuth2AccessToken {
val username = authentication.userAuthentication.name
val additionalInfo = mapOf( /* populate with some data for given username */ )
(accessToken as DefaultOAuth2AccessToken).additionalInformation = additionalInfo
return accessToken
}
}
Then just register bean:
#Bean
fun userTokenEnhancer(): TokenEnhancer {
return UserTokenEnhancer()
}

Secure a Spring Webflux controller considering dynamic parameters with Spring Security annotations

I have an application with some controllers that require access control based on the requested resources ids, checking against the Spring Security user Authentication roles. At the moment I have created a function that checks this condition returning a Mono<True> if it is ok (so that I can flatmap it) or an empty Mono (and also setting a 403 status code) otherwise:
#RestController
#RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
#GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
#PathVariable clientId: String, #PathVariable departmentId: String): Flux<Users> {
return checkDepartmentViewPermissions(principal, response, clientId, departmentId)
.flatMap {
userService.getAll(clientId, departmentId)
}
}
...
}
fun checkDepartmentViewPermissions(principal: Principal, response: ServerHttpResponse,
clientId: String, departmentId: String): Mono<Boolean> {
val authentication = principal as MyAuthentication
authentication.authorities.contains(SimpleGrantedAuthority("${clientId}:${departmentId}")).toMono()
.filter {
it == true
}.switchIfEmpty {
response.statusCode = HttpStatus.FORBIDDEN
Mono.empty()
}
}
As seen above, requests are in the format /api/v1/clients/{clientId}/departments/{departmentId}/users where clientId and departmentId are dynamic path variables.
The checkDepartmentViewPermission method accesses the Authentication roles where users will have a list such as (client1:department1, client1:department2, client2:department1). Thus, a URL /api/v1/clients/client1/departments/department1/users would work fine for those permissions.
Although what I have works, I would like to use a more declarative way to deal with this if it is possible, ideally something based on Spring Security annotations and having into account I need to access PathVariable parameters, something like (I'm making it up):
#RestController
#RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
#PreAuthorize("#{principal.roles.contains(clientId:departmentId)}")
#GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
#PathVariable clientId: String, #PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
...
}
Does Spring Security support a way to do this?
If not, could you suggest any ideas to achieve it?
First off, thanks Thomas for pointing me in the right direction, I hadn't realized we can invoke a Spring bean from the security web expressions. This way, there's no need anymore to inject the principal since the Authentication object will be passed to the bean.
Controller:
#PreAuthorize("#permissionChecker.hasDepartmentViewPermissions(authentication, #clientId, #departmentId)")
#GetMapping
fun getAll(#PathVariable clientId: String, #PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
PermissionChecker bean:
class PermissionChecker {
fun hasDepartmentViewPermissions(authentication: Authentication, clientId: String, projectId: String): Boolean {
...
}

Throw exception spring boot webflux

I am trying to make a condition, which if not satisfied, throw an exception. But I tried in many ways and without success.
My restcontroller:
#GetMapping(value = ["/{id}"])
fun find(#PathVariable id: String): Mono<ResponseEntity<Mono<Person>>> {
return ResponseEntity.ok().body(service.find(id)).toMono()
}
My service
override fun find(id: String): Mono<Person> {
return repository.findById(id).doOnError { throw DataNotFound("Person not found")}
}
If I enter an existing ID, it returns me a registered person.
But if I enter a nonexistent ID, instead of throwing the exception, it returns me a 200 with empty body.
How do I solve this? Could anyone help?
Try this:
#GetMapping(value = ["/{id}"])
fun find(#PathVariable id: String): Mono<ResponseEntity<?>> {
service.find(id).map(person -> ResponseEntity.ok().body(person))
.onErrorResume(DataNotFound.class, exception -> ResponseEntity.notFound())
}
fun find(id: String): Mono<Person> {
repository.findById(id).onErrorMap(error -> new DataNotFound("Person not found"))
}
It returns OK response if a person exists and NOT_FOUND otherwise.
Usually, in a more complex scenario, you want to do a translation from exceptions to some kind of error resource.
In this scenario, you will use the same response type for your method which handles the HTTP request. More precisely, in your case:
#GetMapping(value = ["/{id}"])
fun find(#PathVariable id: String): Mono<ResponseEntity<Mono<Person>>> {
return ResponseEntity.ok().body(service.find(id)).toMono()
}
(this will remain as it is!)
And next, you will provide a so-called ControllerAdvice which can look in your case like in the following snippet:
#ControllerAdvice(assignableTypes = [YourRestController::class]
class ControllerAdvice {
#ExceptionHandler
fun handle(ex: DataNotFoundException): ResponseEntity<DataNotFoundErrorResource> {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(DataNotFoundErrorResource.from(ex)
}
}
Note: Be aware that I've not used a Kotlin compiler, but I've compiled it in my mind :) I hope that it will be ok!

Authorization in jersey framework

I am using jersey (java) framework. I did authentication based on cookie using Container request filter. Now I have to do Authorization. So, how to I proceed? Quick guidance please.
Jersey has #RolesAllowed("role") annotation to facilitate auth check. Make use of:
#Context
HttpServletRequest httpRequest;`
and in the login method put identity into session like here:
HttpSession session = httpRequest.getSession(true);
session.setAttribute(key, val);
in filter
final String name = session.getAttribute(key);
...
SecurityContext securityContext = new SecurityContext() {
public boolean isUserInRole(String roleName) {
return roleName.equals("role");
}
...
public Principal getUserPrincipal() {
...
return new Principal() {
public String getName() {
return name;
}
};
...
}
...
};
requestContext.setSecurityContext(securityContext);
That's it in short. It is quite common approach. If you want I can share ref impl on GitHub.

Resources