Play! Java. pass request param to #With anotation - java

I have routes like:
GET /job$id<[0-9]+>/ controllers.Jobs.index(id)
POST /job$id<[0-9]+>/done controllers.Jobs.done(id)
POST /job$id<[0-9]+>/update controllers.Jobs.update(id)
DELETE /job$id<[0-9]+>/remove controllers.Jobs.remove(id)
and I' like to secure it. Each job has an owner. So I made
public class Secured extends Security.Authenticator {
...
}
Then I tryed to secure all my Actions by "#With()" annotatian, but I need to pass "Id" param to my secure method, so I wrote smth like:
#With(IsOwner.class)
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Owner {
long value() default 0;
}
public static class IsOwner extends Action<Owner>{
#Override
public Promise<SimpleResult> call(Context ctx) throws Throwable {
if(!isOwner(configuration.value())){
return Promise.pure(redirect(..));
}
return delegate.call(ctx);
}
}
But I can't pass my action parametr to annotation.
#Security.Authenticated(Secured.class)
#Owner(id) //**this part I want to work**
public class Jobs extends Controller {
//#Owner(id) - or at list there
public static Result index(Long Id){
//I have too many Actions and don't want to do every time this
/*if(!Secured.isOwnerMethod(Id)){
return forbidden();
}*/
return ok();
}
Other way I see is get params from Context in IsOwner.call() method...
Please, give me a recommendation or good practise for such situations.
Thanks.

I ran into this problem and came up with a Play extension that exposes all url parameters as common request parameters... You can check it out here => https://github.com/asouza/reverse-route-plugin
Basically you have to extend from my Global class.
Cheers,
Alberto

Related

Spring - Pass parameters in custom annotation

I have the following:
Aspect:
#Aspect
#Component
public class AuthorizeUserAspect {
#Autowired
PermissionService permissionService;
#Around("#annotation(AuthorizeUser)")
public Object authorize(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
}
Interface:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizeUser {
}
Controller:
#GetMapping("/businesses/{businessId}/test")
public ResponseEntity<List<BusinessDto>> getBusinessListAsClient(Principal jwtAuthUser, #PathVariable String businessId) throws Exception {
...
}
I need to pass in the named parameters from the method signature, plus an additional custom permission parameter (READ / WRITE), so something like this:
#AuthorizeUser(Principal jwtAuthUser, String businessId, permission = <Enum or String>)
#GetMapping("/businesses/{businessId}/test")
Is this possible? I don't really care how it's done as long as I can get these values in my authorize function to use them.
I know I can do something like:
joinPoint.getArgs();
to get the parameters by position, but I want this to be more generic, and certain controllers can have different params as 1st and second for example, so that would not work, plus I have no idea
how to get the last value.
Note: The authorize is just an example, but I'd like to do this for other custom annotation parameters as well.
Maybe you should read the Spring AOP manual. How about this?
#Around("#annotation(authorizeUser)")
public Object authorize(ProceedingJoinPoint joinPoint, AuthorizeUser authorizeUser)

Quarkus Annotation-Based Interceptor with Non-Optional Arguments

This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.
Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:
public void someApiCall(#Context HttpHeaders headers) {
authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
//…
}
Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:
#Authorize
public void someApiCall(/*…*/) { /*…*/ }
The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:
#Authorize(UserRole.SYSADMIN)
This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):
Authorize.java
#Retention(value=RUNTIME)
#Target(value=METHOD)
public #interface Authorize {}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize
public class AuthorizeInterceptor {
#Inject
AuthorizationService authService;
#AroundInvoke
public void validateRole(InvokationContext ctx) {
authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
}
}
RestEndpoint.java
public class RestEndpoint {
#Context
HttpHeaders headers;
public HttpHeaders getHttpHeaders() { return headers; }
}
SomeResource.java
public class SomeResource extends RestEndpoint {
#GET
#Authorize
public Object someApiCall() {
/* do code directly */
}
}
So, in conclusion, where I write #Authorize, I want to have #Authorize(UserRole.SOME_ROLE).
Thanks in advance!
So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.
Here are the modified classes:
Authorize.java
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Authorize {
// Nonbinding is very important. It makes the interceptor fire regardless of the value
#Nonbinding ApiToken value();
}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize(ApiToken.NULL)
public class AuthorizeInterceptor {
/* fields */
public Object validate(InvokationContext ctx) throws Exception {
authService.validate(/* stays the same */);
return ctx.proceed();
}
}
SomeResource.java
public class SomeResource {
#GET
#Authorize(ApiToken.SOME_API_CALL)
public Object someApiCall() { /* implementation */ }
}
As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.

Create bean instance at runtime for interface

i am kind of stuck on a problem with creating beans, or probably i got the wrong intention.. Maybe you can help me solve it:
I got a application which takes in requests for batch processing. For every batch i need to create an own context depending on the parameters issued by the request.
I will try to simplyfy it with the following example:
I receive a request to process in a batch FunctionA which is a implementation for my Function_I interface and has sub-implementation FunctionA_DE and FunctionA_AT
Something like this:
public interface Function_I {
String doFunctionStuff()
}
public abstract class FunctionA implements Function_I {
FunctionConfig funcConfig;
public FunctionA(FunctionConfig funcConfig) {
this.funcConfig = funcConfig;
}
public String doFunctionStuff() {
// some code
String result = callSpecificFunctionStuff();
// more code
return result;
}
protected abstract String callSpecificFunctionStuff();
}
public class FunctionA_DE extends FunctionA {
public FunctionA_DE(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
public class FunctionA_AT extends FunctionA {
public FunctionA_AT(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
what would be the Spring-Boot-Way of creating a instance for FunctionA_DE to get it as Function_I for the calling part of the application, and what should it look like when i add FunctionB with FunctionB_DE / FunctionB_AT to my classes..
I thought it could be something like:
PSEUDO CODE
#Configuration
public class FunctionFactory {
#Bean(SCOPE=SCOPE_PROTOTYPE) // i need a new instance everytime i call it
public Function_I createFunctionA(FunctionConfiguration funcConfig) {
// create Function depending on the funcConfig so either FunctionA_DE or FunctionA_AT
}
}
and i would call it by Autowiring the FunctionFactory into my calling class and use it with
someSpringFactory.createFunction(functionConfiguration);
but i cant figure it out to create a Prototype-Bean for the function with passing a parameter.. And i cant really find a solution to my question by browsing through SO, but maybe i just got the wrong search terms.. Or my approach to solve this issue i totally wrong (maybe stupid), nobody would solve it the spring-boot-way but stick to Factories.
Appreciate your help!
You could use Springs's application context. Create a bean for each of the interfaces but annotate it with a specific profile e.g. "Function-A-AT". Now when you have to invoke it, you can simply set the application context of spring accordingly and the right bean should be used by Spring.
Hello everyone and thanks for reading my question.
after a discussion with a friend who is well versed in the spring framework i came to the conclusion that my approach or my favoured solution was not what i was searching for and is not how spring should be used. Because the Function_I-Instance depends on the for the specific batch loaded configuration it is not recommended to manage all these instances as #Beans.
In the end i decided to not manage the instances for my Function_I with spring. but instead i build a Controller / Factory which is a #Controller-Class and let this class build the instance i need with the passed parameters for decision making on runtime.
This is how it looks (Pseudo-Code)
#Controller
public class FunctionController {
SomeSpringManagedClass ssmc;
public FunctionController(#Autowired SomeSpringManagedClass ssmc) {
this.ssmc = ssmc;
}
public Function_I createFunction(FunctionConfiguration funcConf) {
boolean funcA, cntryDE;
// code to decide the function
if(funcA && cntryDE) {
return new FunctionA_DE(funcConf);
} else if(funB && cntryDE) {
return new FunctionB_DE(funcConf);
} // maybe more else if...
}
}

Authentication with Play! 2.4

I am working on a new Application on Play! 2.4. I have other Applications on 1.4, and am looking how to implement a similar security method as in Play! 1.x.
I created a secure controller as follows:
#With(Security.class)
public abstract class SecureController extends Controller {
}
I then have the Security.class:
public class Security extends Action.Simple {
public F.Promise<Result> call(Http.Context ctx) throws Throwable {
if(ctx.session().containsKey("pwdHash") && ctx.session().containsKey("securityId")){
User user = User.find.where().eq("id",ctx.session().get("securityId")).findUnique();
if(user != null) {
if(user.getAuthToken().equals(ctx.session().get("pwdHash"))) {
// TODO: Don't think this works yet.
ctx.request().setUsername(user.getEmail());
return delegate.call(ctx);
}
}
}
ctx.session().put("referer", ctx.request().path());
return F.Promise.pure(redirect(routes.Logon.doLogon()));
}
}
This works fine, a user can access the pages when validly logged on.
But what I would like to do now is have it work as with 1.x, that you can annotate with something like #Check("admin").
In 1.x this could be done by extending Secure.Security and to implement:
public static boolean check(String profile, User user)
Creating the annotation is obviously easy enough:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Check {
UserTask value();
}
But how can I now have the system check on each method of the SecureController if the user has the required rights for the given annotation? In play 1.x there was play.mvc.Before which could be used.
[Edit]
My intention is to add something like the following:
#Check(UserTask.REGISTRATION)
public static Result index(int page)
After some further research I found the solution in the Play! documentation.
The above question was already in the right direction, I just needed to extend it by adding the annotation and linking it to the action (and then use the annotation to call on the function).
https://www.playframework.com/documentation/2.4.x/JavaActionsComposition

Jax-RS - Custom attribute to get header value

EDIT: I just realized, is it even possible to perform a custom action with a custom attribute in Java? Or is it just informational?
I want to include an authentication token in my Jax-RS service header, but I don't want to add a parameter to every request to get the header and check it like so:
public Response getUser(#Context HttpHeaders headers) {
if(authorize(headers.getRequestHeader("token").get(0)) {
// Do something
}
}
I would much rather add an attribute to each request (or even the class if that is possible:
#Authorize
public Response getUser() {
// Do something
}
This way I can also add the attribute to only the requests I want to.
And if the request isn't authorized, I can override it and return a 401.
A custom attribute is easy to write, but how can I get the header information in the attribute without passing it in every time?
NOTE: I would rather not use a web.xml. I don't have one right now and I don't like using them. I want to keep my code clean without xml and I think if I used a filter/web.xml it would apply to all calls. If that is the only way, I will, but I much prefer the approach with custom attributes.
"I think if I used a filter/web.xml it would apply to all calls"
Actually there are #NameBinding annotations we can use. For example
#NameBinding
#Rentention(RetentionPoilicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Authorize {}
Then just annotate the filter and the methods/classes you want filtered.
#Authorize
public Response getUser() {
// Do something
}
#Provider
#Authorize
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
MultivauledMap<String, String> headers - requestContext.getHeaders();
...
if (!authorized) {
throw new NotAuthorizedException();
}
}
}
Notice the use of #Priority. This is important. Say you want the authenticate also, so you create a filter for authentication. If you don't set the priority, either filter may occur first. It's unpredictable. If we provide the authentication filter with #Priority(Priorities.AUTHENTICATION), then that filter will always occur before the #Priority(Priorities.AUTHORIZATION) filter.
You will also need to register this filter with the Application subclass (See some other Deployment Options (Jersey, but the Application subclass is portable with implementations))
#ApplicationPath("/api")
public class YourApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
private Set<Object> singletons = new HashSet<>();
public YourApplication() {
classes.add(AuthorizationRequestFilter.class);
}
#Override
public Set<Class<?>> getClasses() {
return classes;
}
#Override
public Set<Object> singletons() {
return singletons;
}
}
See more on Filters and Interceptors
See the WebAppplicationException Hierarchy for more exceptions like NotAuthorizedException
See the Priorities class and Priories guide
The best way to solve your use case would be to use name binding and filters. In this way, you can use a filter to do your authorization logic as well as return a 401 in the case of unauthorized requests.
You can find more information here.
Name binding via annotations is only supported as part of the Server API. In name binding, a name-binding annotation is first defined using the #NameBinding meta-annotation:
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(value = RetentionPolicy.RUNTIME)
#NameBinding
public #interface Logged { }
The defined name-binding annotation is then used to decorate a filter or interceptor class (more than one filter or interceptor may be decorated with the same name-binding annotation):
#Logged
public class LoggingFilter
implements ContainerRequestFilter, ContainerResponseFilter {
...
}
At last, the name-binding annotation is applied to the resource method(s) to which the name-bound JAX-RS provider(s) should be bound to:
#Path("/")
public class MyResourceClass {
#GET
#Produces("text/plain")
#Path("{name}")
#Logged
public String hello(#PathParam("name") String name) {
return "Hello " + name;
}
}
A name-binding annotation may also be attached to a custom JAX-RS Application subclass. In such case a name-bound JAX-RS provider bound by the annotation will be applied to all resource and sub-resource methods in the JAX-RS application:
#Logged
#ApplicationPath("myApp")
public class MyApplication extends javax.ws.rs.core.Application {
...
}
Based on peeskillet's answer, which the concept is right but the code is slightly wrong, this is the final code for the answer.
Using #NameBinding, this works:
Authorize.java
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Authorize {
}
AuthorizeFilter.java
note: still needs to do the actual token authorization. This is just checking if the token exists right now.
#Provider
#Authorize
#Priority(Priorities.AUTHORIZATION)
public class AuthorizeFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
MultivaluedMap<String, String> headers = requestContext.getHeaders();
String token = headers.getFirst("token");
if (token == null || token.isEmpty()) {
Response.ResponseBuilder responseBuilder = Response
.status(Response.Status.UNAUTHORIZED)
.type(MediaType.APPLICATION_JSON)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600");
requestContext.abortWith(responseBuilder.build());
}
}
}
ApplicationConfig.java
note: add the filter here so it doesn't have to be included in the web.xml
#ApplicationScoped
#ApplicationPath("/api")
public class ApplicationConfig extends Application
{
#Override
public Set<Class<?>> getClasses()
{
return getRestResourceClasses();
}
private Set<Class<?>> getRestResourceClasses()
{
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(com.example.AuthorizeFilter.class);
resources.add(com.example.UserService.class);
return resources;
}
}

Categories

Resources