Are multiple roles allowed in the #Secured annotation with Spring Security - java

I would like to allow access to a particular method to more than one group of users. Is it possible in Spring Security 3.x to do such a thing using the #Secured annotation? Consider two groups (roles) OPERATOR and USER, would this code be valid:
#Secured("ROLE_OPERATOR", "ROLE_USER")
public void doWork() {
// do useful processing
}

You're almost there. Syntactically, you need to write it like this:
#Secured({"ROLE_OPERATOR", "ROLE_USER"})
public void doWork() { ... }
This is because you're supplying multiple values to a single array attribute of the annotation. (Java syntactically special-cases handing in a single value, but now you need to do it “properly”.)

#Donal Fellows answer is correct for Spring apps. However, if you're working in Grails, you need to use the Groovy syntax for lists so the code would look like this
#Secured(["ROLE_OPERATOR", "ROLE_USER"])
public void doWork() { ... }

Related

Is it possible to create necessary / required interfaces?

i have a little kont in my brain about structuring our code. We have a REST Backend based on SpringBoot. To handle requests regarding to security checks we use HandlerInterceptors. In some specific cases we need a specific interceptor and not our default one. The default one is registered in a 3rd party lib that no one can forget it. But i want all coders to think about this specific interceptor.
Actually, i just said it to them to achieve this.
Here's my question: Is there an option to create required (or necessary) interfaces which must be implemented? This would be a way to provide our security code by lib and to have the security that every coder implemented our specific interface (also if he just does nothing with it).
pseudo code:
public interface thinkForIt(){
Object SecBean specificSecBean;
public void methodToThinkOn();
}
public SecImpl implements thinkForIt(){
#Override
public void methodToThinkOn(){
return null; // i thought about it but i do not need to do anyting!
}
If the interface thinkForIt would have any annotations like #required, users could get warning or error if they did not implement it...
Looking for a solution and thanks for your comments in advance!
Your overall design is questionable; you are reinventing security code, which is always a red flag. Use Spring Security instead.
However, there's a simple way to ensure that "some bean of type Foo" has been registered with the context:
#Component
#RequiredArgsConstructor
public class ContextConfigurationVerifier {
final Foo required;
}

Spring REST - Is there a way to override the character Spring uses to separate a query parameter into a list of values?

I'm writing a REST API using Spring and have certain clients to the service that cannot or will not change how they call my service.
Normally when sending a query param with a list of values you would just comma delimit the parameter and Spring will do the rest
curl http://host.com/api/endpoint?listParam=1,2,3
And the controller
#GetMapping("/api/endpoint")
public ResponseEntity endpoint(#RequestParam("listParam" List<String> listParam){
// Here, listParam is populated with 1,2,3
}
Unfortunately my clients are going to be passing lists with the bar | delimiter and it simply isn't possible to get them to change that.
Example: curl http://host.com/api/endpoint?listParam=1%7C2%7C3%7C
I would still like to use Spring to break these calls out into lists so I don't have to clutter my code with manual String.split() calls.
What I've already tried:
I found the #InitBinder annotation and wrote the following
#InitBinder
public void initBinder(WebDataBinder dataBinder){
dataBinder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("|"));
}
However, this code doesn't seem to ever be called (watching with breakpoints) and requests using the bar as the delimiter fail with a 400 BAD REQUEST.
Any suggestions would be much appreciated, thanks!
404 is coming due to URL encoding issue.
You need to encode | then it will work, but it will create another problem, params would not be split.
To work around this you need to create a custom conversion that can convert String to Collection. For the custom conversion, you can check the StringToCollectionConverter class. Once you have custom conversion then you can register that service, in any of the configuration classes add following function
#Autowired
void conversionService(GenericConversionService genericConversionService) {
genericConversionService.addConverter(myStringToCollectionConvert());
}
#Bean
public MyStringToCollectionConvert myStringToCollectionConvert() {
return new MyStringToCollectionConvert();
}
In this MyStringToCollectionConvert is class that will parse String and converts to a collection of Strings.
I've accepted Sonus21's answer since his suggestion allowed me to hunt down an example that worked, but my solution was not exactly his.
The class StringToCollectionConverter did in fact exist for me, but it wasn't accessible and I couldn't use it in any way. However, in looking at the interface it implemented (ConditionalGenericConverter) and searching for more examples with Spring converters I eventually settled on the following solution.
The listParam in my question actually refers to a set of Enum values. The first thing I did was rewrite my controller to actually use the Enum values instead of raw Integers.
#GetMapping("/api/endpoint")
public ResponseEntity endpoint(#RequestParam("listParam" List<EnumClass> listParam){
// ...
}
Next, I wrote a Spring Custom Converter (Baeldung Doc)
public class CustomStringToEnumClassListConverter implements Converter<String, List<EnumClass>> {
#Override
public List<EnumClass> convert(String str) {
return Stream.of(
str.split("\\|")) // Here is where we manually delimit the incoming string with bars instead of commas
.map(i -> EnumClass.intToValue(Integer.parseInt(i))) // intToValue is a method I wrote to get the actual Enum for a given int
.collect(Collectors.toList());
}
}
Finally, I wrote a Config Bean and registered this Custom Converter with Spring:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry){
registry.addConverter(new CustomStringToEnumClassListConverter());
}
}
Once all of this was done, Spring automatically populated the listParam list with EnumClass objects.

Change method signature with #Aspect

Can you change a method's signature in Spring using aspects?
Like effectively transform the following:
#GetMapping("/thing")
#User // custom annotation that should authenticate the user
public ResponseEntity getThing() {
... // user is successfully authenticated, get the "thing" from the database
}
into:
#GetMapping("/thing")
public ResponseEntity getThing(#CookieValue("Session-Token") String sessionToken) {
User user = authenticator.authenticateSessionTokenOrThrow(sessionToken);
... // user is successfully authenticated, get the "thing" from the database
}
With the user variable also becoming available for use in the method body.
If not, how can I achieve the same result without repeating the code (parameter and authenticator call) everywhere?
Aspects aren't meant for that.
Yes, they can effectively modify .class files bytecode, with compile time or run time weaving, but they do not override methods' signatures.
Also, the default Spring AOP Aspects are implemented in pure Java, and thus cannot touch the bytecode layer. For that you'd need AspectJ.
Tools for customizing bytecode at run/compile time are ASM, ByteBuddy, CGLIB or Javassist.
However, you can probably accomplish this via an Annotation Processor, which lets you modify the actual sources, instead of the already compiled bytecode.
If not, how can I achieve the same result without repeating the code
(parameter and authenticator call) everywhere?
Possible solutions are
HandlerInterceptor, which simply throws an Exception if the user isn't authenticated
Standard Spring AOP advice, which simply throws an Exception if the user isn't authenticated
Spring Security
1 is pretty easy.
2 is more time-consuming
3 imho, seems the best match for authentication, but it's the most complex, probably
The HandlerInterceptor can choose which methods it applies to?
No, unfortunately. I had a requirement a couple of months ago to "cover" only certain methods with an Interceptor, and I implemented a custom solution, which simply look for an annotation specified on the method itself.
This is an extract of my custom HandlerInterceptor, which looks for the CheckInit annotation, first on the type, and then on the method, for a more specific customization.
#Override
public boolean preHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final Object handler
) throws Exception {
if (handler instanceof HandlerMethod) {
if (shouldCheckInit((HandlerMethod) handler)) {
checkInit();
}
}
return true;
}
private static boolean shouldCheckInit(final HandlerMethod handlerMethod) {
final var typeAnnotation = handlerMethod.getBeanType().getAnnotation(CheckInit.class);
final var shouldCheckInit = typeAnnotation != null && typeAnnotation.value();
final var methodAnnotation = handlerMethod.getMethodAnnotation(CheckInit.class);
return (methodAnnotation == null || methodAnnotation.value()) && shouldCheckInit;
}
private void checkInit() throws Exception {
if (!manager.isActive()) {
throw new NotInitializedException();
}
}
The "Standard Spring AOP advice" seems interesting, do you have a link
for that?
Spring AOP documentation - look for the Java-based configuration (I hate XML)
AspectJ really touches the bytecode and can modify signatures as well?
You could make AspectJ modify signatures. Just fork the project and modify its Java Agent or compiler.
AFAIK Annotation Processors cannot modify classes, they can only
create new ones.
The thing is, they don't modify .class files, instead they modify source files, which means they simply edit them. E.g. Lombok uses annotation processing to modify source files.
But yes, the modified sources are written to a new file.

Restrict execution of a Method with Java Annotations

Do you know, if there is the possibility to check who is calling a method and to restrict whether they are allowed to execute it with Java Annotations?
For example if you have a client and a server. There are several users, which have different roles and they login into the client. Then (the same client) with different users wants to call a getMethod on the server.
Can I restrict, who is allowed to call this methos with Java Annotations?
Like:
#Role(role="AllowedRole")
public ReturnType getMethod() {
...
}
Well, I used to achieve this with Seam/DeltaSpike in JBoss Server. It's pretty straightforward.
Basically, you have a method which you annotate with your annotation. For example, mine is #User:
public class MyClass {
#User
public Object getMethod() {
//implementation
}
}
Next, you need a class where you define how you check your annotations:
public class Restrictions {
#Secures #User
public boolean isOk(Identity identity) {
if (identity.getUsername("Peter")) {
return true;
}
return false;
}
}
That's it! Ofcourse, you need some libraries and to define these intercepting stuff in certain xml files (like beans.xml) but it can be easily done with a little googling.
Start from these links:
Seam framework
Questions I asked on JBoss community when I was starting with this
This seems to be a good case for Method Security of Spring Security.
Annotations do not include code and are not processed magically. They just define metadata, so you need some kind of engine that processes the annotations and performs the access validation.
There are a lot of frameworks and tools that do this. For example you can implement this using AspectJ, Spring framework and Java EE support similar annotations.
You can also implement this logic yourself using dynamic proxy, byte code engineering or other technique.
So, please explain better what kind of application are you implementing and we can probably give you better advice.

Spring security authrorize based on input parameter criteria

I have a scenario where I need to authorize user based on combination of his permission and input parameter passed.
this is the current scenario
public void bookTicket(String bookingType)
{
if (bookingType == "AIR"){
bookAirTicket();
}else{
bookBusTicket();
}
}
#PreAuthorize("hasRole('BOOK_AIR')")
private void bookAirTicket(){
}
#PreAuthorize("hasRole('BOOK_BUS')")
private void bookBusTicket(){
}
Can we have some thing like
#PreAuthorize(("hasRole('BOOK_AIR')" AND bookinType='AIR') OR ("hasRole('BOOK_BUS')" AND bookinType='BUS'))
public void bookTicket(String bookingType)
{
if (bookingType == "AIR"){
bookAirTicket();
}else{
bookBusTicket();
}
}
Basically I need authorization based in input parameters
Thanks
Yes, you can. Parameters can be accessed as Spring EL variables. In fact the reference manual gives several examples which use method parameters. The class needs to be compiled with debug symbols present (which is usually the case).
Note that the annotation value is a single expressions string:
"(hasRole('BOOK_AIR') and #bookinType == 'AIR') or (hasRole('BOOK_BUS') and #bookinType='BUS')"
In practice, using complicated expressions is rather error-prone. You could also use a simpler expression, something like
"#accessChecker.check('book', #bookinType)"
Where accessChecker is a bean in your application context with a "check" method which returns true or false depending on whether the supplied operation information is allowed (you can check the current user's roles by accessing the security context yourself - you'll find that discussed elsewhere on SO).
You could also look into writing your own AccessDecisionManager or AccessDecisionVoter and plugin the functionality there, but that requires more internal knowledge.

Categories