Sessionless controller in Asp.Net Mvc Core - c#

I need to remove session from a controller because it adds unnecessarily load to my Redis server. I use Redis to store my session.
I have a controller that it's used from a Web-hook that its called rapidly and with high volume, the Web-hook doesn't use session and it would be better if I could completely remove the session from it.
As I google-searched I discover the attribute [ControllerSessionState] that removes the session from the controller but unfortunately it's only for Mvc3.
Is there something similar for Asp.Net Mvc Core?

There are two basic approaches
Middleware filter
Create a base controller from which your stateful controllers inherit from and decorate it with an middleware filter attribute which registers a session.
Once created you'd have a base class
public class SessionPipeline
{
public void Configure(IApplicationBuilder applicationBuilder)
{
applicationBuilder.UseSession();
}
}
[MiddlewareFilter(typeof(SessionPipeline))]
public class StatefulControllerBase : ControllerBase
{
}
and have your stateful Controllers inherit from StatefulControllerBase instead of ControllerBase/Controller
Use MapWhen to conditionally register the Session
This approach was more common in the first versions of ASP.NET Core 1.x, but isn't much used these days
app.MapWhen(context => !context.Request.Path.StartsWith("/hooks/"), branch =>
{
branch.UseSession();
});
This way session middleware will only be used for pathes not matching /hooks/ request path.

Related

How can I secure Controllers in a Razor Class Library

I have developed some Admin functionality (EF logic, Controller, and Razor UI for Audit Logs actually) that I've packaged into a Razor Class Library (RCL) and created a NuGet package. I want this functionality available to users of the package, but I want to allow them to control the access security. I would usually decorate the Controller with an Authorize Attribute, something like:
[Area("MyAuditLogPackage")]
[Authorize(Roles = "Admin")]
public class AuditLogController : Controller
...
But I don't want to presume the client's security policy and Audit Logs are sensitive data.
They could derive their own controller from mine, but the original Route would still be in their default Area Mappings.
How can I give full control of this over to the package clients?
Rather than authorizing by Role, you could require that people using your code create custom security policies that are defined on startup. This would result in something like
[Area("MyAuditLogPackage")]
[Authorize(Policy= "AuditControllerPolicy")]
public class AuditLogController : Controller
...
The policy approach is extremely flexible so the policy might be a requirement that a user be in role Admin. It could also require other claims be present in the token, including custom claims. Check out Policy-based Authorization in Asp.Net Core.
This approach gives a user of your NuGet package complete flexibility, but many might find it burdensome. You might want to canvas a few to get their opinion first.
You can create a extension method to dynamically secure your Razor Class Library routes.
Definition:
internal static class IEndpointConventionBuilderExtensions
{
public static TBuilder AddAuthorization<TBuilder>(this TBuilder builder, AuthorizeAttribute? metadata = null)
where TBuilder : IEndpointConventionBuilder
{
if(metadata != null)
{
builder.WithMetadata(metadata);
}
return builder;
}
}
Usage:
app.MapControllerRoute(
name: "MasterData",
pattern: "{culture}/{area:exists}/{controller=Log}/{action=Index}/{dictionaryName?}/")
.AddAuthorization(new AuthorizeAttribute("MasterData"));

How to change the default controller and action in ASP.NET Core API?

I'm creating an ASP.NET Core API app, and currently, and when one creates a new project there is a controller named Values, and by default the API opens it when you run. So, I deleted that controller and added a new controller named Intro, and inside it an action named Get. In the Startup.cs file, I have the following lines of code:
app.UseMvc(opt =>
{
opt.MapRoute("Default",
"{controller=Intro}/{action=Get}/{id?}");
});
And my Intro controller looks like this:
[Produces("application/json")]
[Route("api/[controller]")]
[EnableCors("MyCorsPolicy")]
public class IntroController : Controller
{
private readonly ILogger<IntroController> _logger;
public IntroController(ILogger<IntroController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
// Partially removed for brevity
}
}
But, again when I run the API, it by default tries to navigate to /api/values, but since I deleted the Values controller, now I get a 404 not found error. If I manually then navigate to /api/intro, I get the result that is provided from my Get action inside the Intro controller. How can I make sure that when the API run (for example through Debug->Start Without Debugging) that it by default gets the Get action from the Intro controller?
You can change it in launchSettings.json file in Properties node. There should be field launchUrl which contains default launching url
With later version of ASP .Net Core, MVC routing is less prominent than it once was, there is general routing now in place which can handle routing of Web APIs, MVC and SignalR amongst other kinds of routes.
If you are using later versions, and not specifically calling app.UseMvc you can do this with the generalised Endpoints configuration:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Account}/{action=login}/{id?}");
// Create routes for Web API and SignalR here too...
});
Where Account and login are your new default controller and actions. These can be MVC or Web API controller methods.

Reusing route & logic for Asp.Net Core controller

I'm just getting started with Asp.Net Core (and asp.net in general) and I'm trying to build nice controller classes for my rest api.
I'm trying to inherit from a base controller to avoid redefining routes and logic such as validation for resources like so (non working example):
[Route("/api/v1/users/{id}")]
public class UserController: Controller
{
protected int userId;
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
// Validate userId..
userId = (int) RouteData.Values["id"];
}
[HttpGet]
public IActionResult Info()
{
// Use this.userId here
return this.Json("User info..");
}
}
[Route("/friends")]
public class UserFriendsController: UserController
{
[HttpGet]
public IActionResult Info()
{
// Use this.userId here
return this.Json("List of user's friends..");
}
}
I realize I can put this into a single class with multiple actions, but my real scenario involves more controllers that all may want to inherit from UserController.
Route attributes cannot be inherited.
You can play with Routing Middleware. See documentation and the examples on Routing github repo.
Also I can recommend this ASP.NET Core 1.0 - Routing - Under the hood article, as it has good explanation about how routing works:
I'm trying to inherit from a base controller to avoid redefining
routes and logic such as validation for resources
If you want to check whether current user has access right on the resource or not, you should use Resource Based Authorization. For other cross cutting concerns, you can use Filters or Middlewares.

Blocking anonymous access by default in ASP .NET 5

My team and I are starting up a new website project in ASP .NET 5 and I'm trying to set up the basis of our user authentication and authorization policy.
So far, I've managed to use the [Authorize] and [AllowAnonymous] attributes to selectively define an authorization policy controllers or actions. The one thing I'm still struggling to achieve is defining a default authorization policy.
Bascially, I'd like every controller and action to behave as if they had an [Authorize] attribute by default, so that only actions specifically tagged as [AllowAnonymous] can be accessed by an anonymous user. Otherwise, we expect that, at some point, someone will forget to add an [Authorize] attribute to their controller and introduce vulnerabilities into the webapp.
It is my understanding that what I'm trying to do could be achieved in previous versions of ASP .NET by adding the following statement in FilterConfig.cs:
filters.Add(new AuthorizeAttribute());
... except that FilterConfig.cs no longer exists in MVC 6. According to How to register a global filter with mvc 6, asp.net 5 I can now access the global filters list using:
services.ConfigureMvc(options =>
{
options.Filters.Add(new YouGlobalActionFilter());
}
... tried it, looks fine, but now it's the AuthorizeAttribute filter that I can't seem to find.
For experimenting purposes I've tried to handcraft an equivalent to the AuthorizeAttribute filter and came up with the following:
public class LoginFilter: AuthorizeFilter
{
public LoginFilter(): base(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build())
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.AuthorizationContext context)
{
if(!context.HttpContext.User.Identity.IsAuthenticated && context.ActionDescriptor is ControllerActionDescriptor)
{
var action = context.ActionDescriptor as ControllerActionDescriptor;
if(!AcceptAnonymous(action.ControllerTypeInfo) && !AcceptAnonymous(action.MethodInfo))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
return base.OnAuthorizationAsync(context);
}
private static bool AcceptAnonymous(ICustomAttributeProvider o)
{
return o.IsDefined(typeof(AllowAnonymousAttribute), true);
}
}
This kinda works... I can add it to the global filters list, and it does reject queries coming from unauthenticated users unless the query is resolved to an action tagged [AllowsAnonymous].
However...
the AuthorizationPolicyBuilder thingy is ugly and misleading: it does not serve any purpose and is apparently ignored during the whole processing. The only reason I added it is that AuthorizeFilter requires an AuthorizationPolicy in its constructor. I guess, but haven't tried yet, that directly implementing IAsyncAuthorizationFilter would solve this particular issue
nothing in this code is specific to my webapp and the functionality was apparently provided in previous versions of the framework, so I'm willing to bet that there already is (or there will soon be) a component doing exactly the same thing, and I'd rather use a standard component from the framework than handcraft my own.
So, long story short, where has the AuthorizeAttribute filter gone? Or is there any functional equivalent I can use to make rejection of anonymous users the default behavior?
You can use Microsoft.AspNet.Mvc.AuthorizeFilter.
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Authorization;
services.ConfigureMvc(options =>
{
options.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
});
If you need custom authorization requirements see this answer for more information.

controller specific iis logging with ASP.NET MVC in IIS

I want to log visits only for some controllers (or routes) as it was possible with classic ASP.NET pages by checking/unchecking the 'log visits' checkbox in IIS.
Does anyone know if this is possible somehow? A solution without a custom logging component would be fantastic! Please share your knowledge, if you know how ;)
Thanks in advance
Create a BaseController which the controllers that you want to record data for inherit from. Then create an ActionFilter which overrides the OnActionExecuted method and apply it to the base controller. Something like this..
public class ActionExecutedFilter : System.Web.Mvc.ActionFilterAttribute
{
UnitOfWork unitOfWork= new UnitOfWork();
public override void OnActionExecuted(ActionExecutedContext filter)
{
Transaction tran = new Transaction();
tran.Controller = filter.ActionDescriptor.ControllerDescriptor.ControllerName;
tran.ActionName = filter.ActionDescriptor.ActionName;
tran.User = HttpContext.Current.User.Identity.Name;
tran.Date = DateTime.Now;
unitOfWork.TransactionRepository.Insert(tran);
unitOfWork.Save();
}
}
This will save to a database table called Transactions information for every time a action method is called on that controller, recording the user, controller and action method name. Obviously I just typed in the UnitOfWork method of saving to the database, you can just plug in whichever method you like. I usually keep these methods in a filters folder, add a using statement then add it to the controller like so;
[ActionExecutedFilter]
public class BaseController : Controller
{
}
Again, just make all the controller you wish to record data from inherit the BaseController.
You could also use something like Log4Net, but I find just doing it this way gets what I need. Whatever you think yourself.
http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/
A solution without a custom logging component would be fantastic!
Apart from basic IIS diagnostic logs, I cannot think of any ASP.Net MVC specific Logging features in IIS.
One simple solution what I want to offer is to write a HttpModule. In the HttpModule you can log the requests. Also if you want to have control on what to log and what not to, then based on Routes you can make that happen in HttpModule. Advantage with HttpModule is it is easy to plug in and also easy to remove.
Make HttpModule work only on certain routes
When it comes to logging itself, you can have your own custom logic to database or to log file. But you can opt for ELMAH or Log4Net or Enterprise Logging Application Block

Categories

Resources