Restelet routing: some resources require authentication, some don't. How to do it? - authentication

I've been workin on a RESTlet 2.1 project. I've figured out how to set up authentication for my Resources. Fact is.. not all of them need authentication! I am quite puzzled about how should I do it right.
In the following code you can see the outline of my server Application, in particular the "create Inbound Root":
#Override
public Restlet createInboundRoot(){
/* the structure so far is: a filter, followed by an authenticator,
followed by a rooter.
The filter is returned at end of the method.
*/
//Init filter:
SomeFilter someFilter = new SomeFilter();
//Init authenticator:
ChallengeAuthenticator authenticator = new ChallengeAuthenticator(
......);
//more authenticator stuff goes here....
//Init router:
Router router = new Router(getContext());
//this should be a public resource, no need for auth:
router.attach("/0.1/getResource", SomeResource.class)
//this is a private resource, needs auth:
router.attach("/0.1/getPrivateResource", PrivateResource.class);
//set up the flow: filter -> authenticator -> router
authenticator.setNext(router);
someFilter.setNext(authenticator);
return someFilter;
}
The filter must be before everything, since I need to modify some Headers for all packages. After the filter I would like to set-up a fork, where requests of my public resource are just routed to the Resource class and requests of the private resource must pass through the authenticator.
How can I accomplish this? I am new to this framework and couldn't figure out even if it looks dead simple.

Think about your routers like a chain. Yours look like this:
someFilter -> authenticator -> router -> (SomeResource.class | PrivateResource.class)
This means all requests start at someFilter, go through the authenticator, hit the router and end up at either SomeResource or PrivateResource.
You need to put the authenticator in front of PrivateResource only, move the authenticator to that bit of the chain, so it looks more like this:
someFilter -> router -> (SomeResource.class | authenticator -> PrivateResource.class)
The code might look like:
ChallengeAuthenticator authenticator = new ChallengeAuthenticator(......);
authenticator.setNext(PrivateResource.class);
router.attach("/0.1/getPrivateResource", authenticator);
Does that help?

I've figured out the full solution to this, it was about "URI templates". The one thing missing was the different way a router can match the uri, in fact, the problem needed a "Match the first part of the URI" kind of approach.
/*
* Routing structure:
*
* ---/public---->(publicR()----> Public Resources
* (Filter()---->(routerPP()--|
* ---/private--->(authenticator()---->(privateR()---> Private Resources
*
*/
where routerPP takes the decision if the URL begins with /public or /private:
Router routerPP = new Router(getContext());
routerPP.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
routerPP.attach("/private", authenticator);
routerPP.attach("/public", publicR);
the one particularity is that, after an URL "passes through" a router, it looses the matched part of the URL, therefore a following router (e.g. the public one) will have this structure:
Router publicR = new Router(getContext());
publicR.attach("/somePublicResource", SomePublicResource.class);
and such configuration matches the following URL:
http://somehost.com/public/somePublicResource
if in the second router you add the "/public/" token again, you will get a "resource not found" error, and the resource would then be on: http://somehost.com/public/public/somePublicResource
So the routers match and remove from the URL.
Reference about the routing and the URI matching I've found useful are:
http://restlet-discuss.1400322.n2.nabble.com/Trying-to-route-to-two-routes-that-start-with-same-prefix-td7019794.html
http://restlet.org/learn/javadocs/snapshot/jse/api/org/restlet/routing/Router.html

Related

Register dependent services on every request

I am working in Multi-tenant solution primarily there are 2 type of applications
WebAPI
Console app to process message from queue
I have implemented dependency injection to inject all services. I have crated TenantContext class where I am resolving tenant information from HTTP header and it's working fine for API, but console application getting tenant information with every message (tenant info is part of queue message) so I am calling dependency injection register method on every incoming message which is not correct, do you have any suggestion/solution here?
The way I am resolving ITenantContext in API
services.AddScoped<ITenantContext>(serviceProvider =>
{
//Get Tenant from JWT token
if (string.IsNullOrWhiteSpace(tenantId))
{
//1. Get HttpAccessor and processor settings
var httpContextAccessor =
serviceProvider.GetRequiredService<IHttpContextAccessor>();
//2. Get tenant information (temporary code, we will get token from JWT)
tenantId = httpContextAccessor?.HttpContext?.Request.Headers["tenant"]
.FirstOrDefault();
if (string.IsNullOrWhiteSpace(tenantId))
//throw bad request for api
throw new Exception($"Request header tenant is missing");
}
var tenantSettings =
serviceProvider.GetRequiredService<IOptionsMonitor<TenantSettings>>();
return new TenantContext(tenantId, tenantSettings );
});
Create two different ITenantContext implementations. One for your Web API, and one for your Console application.
Your Web API implementation than might look as follows:
public class WebApiTenantContext : ITenantContext
{
private readonly IHttpContextAccessor accessor;
private readonly IOptionsMonitor<TenantSettings> settings;
public WebApiTenantContext(
IHttpContextAccessor accessor,
IOptionsMonitor<TenantSettings> settings)
{
// Notice how the dependencies are not used in this ctor; this is a best
// practice. For more information about this, see Mark's blog:
// https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
this.accessor = accessor;
this.settings = settings;
}
// This property searches for the header each time its called. If needed,
// it can be optimized by using some caching, e.g. using Lazy<string>.
public string TenantId =>
this.accessor.HttpContext?.Request.Headers["tenant"].FirstOrDefault()
?? throw new Exception($"Request header tenant is missing");
}
Notice that this implementation might be a bit naive for your purposes, but hopefully you'll get the idea.
This class can be registered in the Composition Root of the Web API project as follows:
services.AddScoped<ITenantContext, WebApiTenantContext>();
Because the WebApiTenantContext has all its dependencies defined in the constructor, you can do a simple mapping between the ITenantContext abstraction and the WebApiTenantContext implementation.
For the Console application, however, you need a very different approach. The WebApiTenantContext, as shown above, is currently stateless. It is able to pull in the required data (i.e. TenantId) from its dependencies. This probably won't work for your Console application. In that case, you will likely need to manually wrap the execution of each message from the queue in a IServiceScope and initialize the ConsoleTenantContext at the beginning of that request. In that case, the ConsoleTenantContext would look merely as follows:
public class ConsoleTenantContext : ITentantContext
{
public string TenantId { get; set; }
}
Somewhere in the Console application's Composition Root, you will have to pull messages from the queue (logic that you likely already have), and that's the point where you do something as follows:
var envelope = PullInFromQueue();
using (var scope = this.serviceProvider.CreateScope())
{
// Initialize the tenant context
var context = scope.ServiceProvider.GetRequiredService<ConsoleTenantContext>();
content.TenantId = envelope.TenantId;
// Forward the call to the message handler
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler>();
handler.Handle(envelope.Message);
}
The Console application's Composition Root will how have the following registrations:
services.AddScoped<ConsoleTenantContext>();
services.AddScoped<ITenentContext>(
c => c.GetRequiredServices<ConsoleTenantContext>());
With the registrations above, you register the ConsoleTenantContext as scoped. This is needed, because the previous message infrastructure needs to pull in ConsoleTenantContext explicitly to configure it. But the rest of the application will depend instead on ITenantContext, which is why it needs to be registered as well. That registration just forwards itself to the registered ConsoleTenantContext to ensure that both registrations lead to the same instance within a single scope. This wouldn't work when there would be two instances.
Note that you could use the same approach for Web API as demonstrated here for the Console application, but in practice it's harder to intervene in the request lifecycle of Web API compared to doing that with your Console application, where you are in full control. That's why using an ITenantContext implementation that is itself responsible of retrieving the right values is in this case an easier solution for a Web API, compared to the ITenantContext that is initialized from the outside.
What you saw here was a demonstration of different composition models that you can use while configuring your application. I wrote extensively about this in my series on DI Composition Models on my blog.

ASP.NET Core : Return Json response on Unauthorized in a filter at the controller/action level

I am not using Identity.
I have this ASP.NET Core configuration enabling two authentication schemes, cookies and basic auth:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "_auth";
options.Cookie.HttpOnly = true;
options.LoginPath = new PathString("/Account/Login");
options.LogoutPath = new PathString("/Account/LogOff");
options.AccessDeniedPath = new PathString("/Account/Login");
options.ExpireTimeSpan = TimeSpan.FromHours(4);
options.SlidingExpiration = true;
})
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
BasicAuthenticationHandler is a custom class inheriting from AuthenticationHandler and overriding HandleAuthenticateAsync to check the request headers for basic authentication challenge, and returns either AuthenticateResult.Fail() or AuthenticateResult.Success() with a ticket and the user claims.
It works fine as is:
Controllers/Actions with the [Authorize] tag will check the cookies and redirect to the login page is not present.
Controllers/Actions with the [Authorize(AuthenticationSchemes = "BasicAuthentication")] tag will check the header and reply a 401 Unauthorized HTTP code if not present.
Controllers/Actions with the [Authorize(AuthenticationSchemes = "BasicAuthentication,Cookies")] tag will allow both methods to access the page, but somehow use the Cookies redirection mechanism when failing both checks.
My goal is to have most of my project to use Cookies (hence why it is set as default), but have some API type of controllers to accept both methods. It should also be possible to tag the Controllers/Actions to return a specific Json body when desired (as opposed to the login redirect or base 401 response), but only for certain controllers.
I've spent the last 2 days reading different similar questions and answers here on StackOverflow, nothing seems to accommodate my need.
Here's a few methods I found:
The options under AddCookie allow you to set certain events, like OnRedirectToAccessDenied and change the response from there. This does not work because it applies to the whole project.
Under my BasicAuthenticationHandler class, the AuthenticationHandler class allow to override HandleChallengeAsync to change the response from there instead of replying 401. Unfortunately, again it applies globally to everywhere you use the scheme, not on a controller/action level. Not sure if it's applied when mixing multiple schemes either.
Many answers point to adding a Middleware to the solution, again, it impacts the whole project.
Many answers point to Policies, but it seems to be to control whether or not an user have access to the resource based on claims, not controlling the response when he do not.
Many answers suggest creating a class inheriting from AuthorizeAttribute, IAuthorizationFilter. Again, this allow to override the OnAuthorization method to decide if the user have the right or not to access the resource, but not to control the response AFTER the normal authentication scheme failed.
I'm thinking either there's a filter type I'm missing, or maybe I need to create a third authentication type that will mix the previous two and control the response from there. Finding a way to add a custom error message in the options would also be nice.
I managed to do it via a IAuthorizationMiddlewareResultHandler. Not my favorite approach because there can be only one per project and it intercepts all calls, but by checking if a specific (empty) attribute is set, I can control the flow:
public class JsonAuthorizationAttribute : Attribute
{
public string Message { get; set; }
}
public class MyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
private readonly AuthorizationMiddlewareResultHandler DefaultHandler = new AuthorizationMiddlewareResultHandler();
public async Task HandleAsync(RequestDelegate requestDelegate, HttpContext httpContext, AuthorizationPolicy authorizationPolicy, PolicyAuthorizationResult policyAuthorizationResult)
{
// if the authorization was forbidden and the resource had specific attribute, respond as json
if (policyAuthorizationResult.Forbidden)
{
var endpoint = httpContext.GetEndpoint();
var jsonHeader = endpoint?.Metadata?.GetMetadata<JsonAuthorizationAttribute>();
if (jsonHeader != null)
{
var message = "Invalid User Credentials";
if (!string.IsNullOrEmpty(jsonHeader.Message))
message = jsonHeader.Message;
httpContext.Response.StatusCode = 401;
httpContext.Response.ContentType = "application/json";
var jsonResponse = JsonSerializer.Serialize(new
{
error = message
});
await httpContext.Response.WriteAsync(jsonResponse);
return;
}
}
// Fallback to the default implementation.
await DefaultHandler.HandleAsync(requestDelegate, httpContext, authorizationPolicy, policyAuthorizationResult);
}
}
I was typing this on comment... but it's doesn't fit... so here is something we probably need to make clear before choosing a solution:
Authorization process happen at the upper middleware above controller
Yes, AuthorizationMiddleware was registered when we use app.UseAuthorization();, that quite far above controller layer, so it was returned long before the request can reach controller, so, any type of filter cannot be applied here.
Not specify an authentication scheme or policy would easily lead to un-stable behavior.
Imagine, Authentication process return an instance of User that stick with the request, but what would happen if the permission on cookie and basicAuth was difference, like cookie have myclaim, while basicAuth doens't ? Related process on both type of scheme was difference (like challenge on cookie would lead to /Account/Login and basicAuth to /Login ?). And various logic case that we could implement on each page.
I Know, this is not possible, but it would become a mess, not for the author of these code, but for those maintainers to come.
Json response for some specific process on client ?
This might sound detailed at first glance, but it would rather become burden soon, if some more authentication requirement raise after that (like Jwt). Covering each of these case on client would make user experience quite awkward (like, half-authentication and authorization).
And if It's un-avoidable in the project. Might I suggest create a default authentication scheme with ForwardDefaultSelector that would elected which authentication scheme to use for each request. And maintain a stable routing HashSet that would use to detect on which endpoint to set Json Response as wished on some upper level than AuthorizationMiddleware, by using middleware, ofcourse. Then, we narrow down to 2 centralize places to checkout the authorization.
Chaos came when we tried to make one thing to do somethings. At least in this case, I think we would breath easier when coming to debug phase.

Route matching from static Uri + route data extract

I have a .Net Core 2.2 MVC app with routes defined as route attributes on my MVC actions.
I would like to find the matching route (if any) and what the route data are from a given Uri (i.e. not the current HTTP request but a static Uri coming from a database for instance).
I already use the LinkGenerator.GetPathByAction() method to get the "route URL" for a specific action with route data. What I am after would be the opposite: a method that takes a URL/Uri and return the matching route and its route data.
For instance if I have a route registered with the following template:
[Route("/my-action/{id:int}/{name}")]
the URL "/my-action/5/my-test-name" would return the following route data:
id: 5
name: my-test-name
I went through the routing documentation but I haven't found anything.
https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/fundamentals/routing.md
The only option that I see would be to somehow call the RouteMiddleware (https://github.com/aspnet/AspNetCore/blob/master/src/Http/Routing/src/RouterMiddleware.cs) with a mock HttpContext which seems overkill if even doable?
I would like to find the matching route (if any) and what the route data are from a given Uri.
If you want to get the route data in the action with the matching route , you could directly use GetRouteData in the current HttpContext object like below :
[Route("/GetRouteData/{id:int}/{name}")]
public void GetRouteData()
{
var routeData = HttpContext.GetRouteData();
var routeCollection = routeData.Values;
var id = routeData?.Values["id"]?.ToString();
var name = routeData?.Values["name"]?.ToString();
}
About finding out if a URL matches an action in ASP.NET MVC Core , you could refer to the following links :
https://joonasw.net/view/find-out-if-url-matches-action
https://weblog.west-wind.com/posts/2019/May/15/Accessing-RouteData-in-an-ASPNET-Core-Controller-Constructor
https://rimdev.io/asp-net-core-routes-middleware/

Web-Shiro getSuccessUrl of PassThruAuthenticationFilter

I've used Shiro's inbuilt login behavior for sometime. The user is redirected to the login page when they try to access a protected resource, then after successful login, they are redirected back to the resource they were trying to access; that is, the successUrl property of PassThruAuthenticationFilter.
Now that I'm using my own custom login, I can't seem to find a way of getting this successUrl right. Below is shiro.ini configuration:
shiro.ini
[main]
authc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
authc.loginUrl = /login.xhtml
authc.successUrl = /index.xhtml #index.xhtml is fallback url
Below is my login code
Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFileDir + "shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
session = currentUser.getSession();
PassThruAuthenticationFilter filter = new PassThruAuthenticationFilter();
String url = filter.getSuccessUrl();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//
//
//
}
I know it does not make sense to use getSuccessUrl on a newly initiated PassThruAuthenticationFilter class, so I'm wondering, what's the correct way of getting the filter object?
I eventually found the solution; I added the following lines in my login code:
import org.apache.shiro.web.util.WebUtils;
//...
String successUrl = WebUtils.getSavedRequest(request);
It worked like a charm. I din't have to worry about PassThruAuthenticationFilter anymore.
Updated
I found it from this link
Use the following from within your Spring MVC controller:
import org.apache.shiro.web.util.WebUtils; ... String fallbackUrl =
"/path/to/go/to/incase/there/is/no/saved/request";
WebUtils.redirectToSavedRequest(request, response, fallbackUrl);
return null; //tell Spring MVC not to render a view, we're redirecting
explicitly

HTTP Authentication in Restlet, authenticating child URL

I am using HTTP Digest authentication mechanism in the server side and client is firefox.
This is the server side code
Application application = new Vehicle();
component.getDefaultHost().attachDefault(application);
component.getDefaultHost().attach("/home",new Home());
DigestAuthenticator guard = new DigestAuthenticator(null, "TestRealm","mySecretServerKey");
Instantiates a Verifier of identifier/secret couples based on a simple Map.
MapVerifier mapVerifier = new MapVerifier();
Load a single static login/secret pair.
mapVerifier.getLocalSecrets().put("login", "secret".toCharArray());
guard.setWrappedVerifier(mapVerifier);
Guard the restlet
guard.setNext(application);
component.getDefaultHost().attachDefault(guard);
component.start();
In home class
Router router = new Router(getContext());
router.attach("/People", People.class);
router.attach("/categories/",Categories.class);
return router;
if i request http://localhost:8182/ Http authentication is working but http://localhost:8182/home/categories/ is not asking for any http authentication if first we try for /home/categories/ instead of http://localhost:8182/ it will give out the result with out any authentication mechanism. How to solve this ?
You are attaching the guard only to the default route, so the routes that are not matching any other routes. See the javadoc for attachDefault :
* Attaches a Resource class to this router as the default target to invoke
* when no route matches. It actually sets a default route that scores all
* calls to 1.0.
Your other routes are not the default routes and so they are not guarded
router.attach("/People", People.class);
router.attach("/categories/",Categories.class);
You must wire the guard between each route that you want to protect like this :
DigestAuthenticator peopleGuard = new DigestAuthenticator(null, "TestRealm","mySecretServerKey");
peopleGuard.setNext(People.class);
router.attach("/People", peopleGuard);