Routing in multitenancy site - c#

I have a multitenacy WebSite, and i need to solve the routing so that:
A request enters to
www.maindomain.com/{site}/{controller}/{action}/{id}
First, a global filter is executed, and stores the current site on ViewData
ViewData["tenant"] = current {controller} request
And then, the correct controller/action is executed, so that it can retrive the request's site:
public class ViewItem : Controller
{
public ActionResult ViewItem(int id)
{
string site = ViewData["tenant"];
return View("you are viewing the item" + id + " from tenant" + tenant ;
}
}
I think i need to configure some routing, is it possible?

Create a custom ActionFilter. The trick is to hook into OnActionExecuting method, which is called before the action method in controller.
public class PropagateTenantToViewDataFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["tenant"] =
filterContext.RouteData.Values["tenant"];
}
}
Register this filter as a global filter for all controllers and actions - add this line to your Application_Start method in Global.asax.cs file:
GlobalFilters.Filters.Add(new PropagateTenantToViewDataFilter());
If you are using App_Start and FilterConfig, then add the following line to your RegisterGlobalFilters instead:
filters.Add(new PropagateTenantToViewDataFilter());
See e.g. this page for more information about Action Filters in ASP.NET MVC.

I'd look at Autofac mutli tennancy it made our lives a lot easier. Can define a tenant identification strategy and use that to make DI decisions allowing tenant specific services or controllers etc.

Related

How to redirect from OnActionExecution to an Action in a controller (Asp.net core 5)

Hi guys I’m working on an Asp.net core project targeted .Net 5
I created a class inherited from IActionFilter and I used the OnActionExecution method I did some logic in it and wanna redirect from it to another Action in a Controller.
The problem :
The problem is how can I redirect to the action that request came from and I tried many solution depending on my knowledge but no one succeeds.
What I tried :
public class ValidationFilterAttribute : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//some logic to get the model
if(!context.ModelState.IsValid)
{
Context.Result = new RedirectToAction(“Idon’t know how to get action”, “I don’t know how to get controller”,new{model= /*model I got*/});
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
Question :
I have two questions,
How to know the Action and the Controller names that request came from ?
How can I redirect to the same Action but in the same Controller and send the same model gotten in ‘OnActionExecution’
Why I do that :
My Idea is using my own ‘IActionFilter’ class with any method worked in HttpPost To check if the model sent is valid or not and if not valid On OnActionExecution method will add errors to the model and resent it again to the action.
Please any help about this issue ?
You can define a custom ActionFilter like this
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; // Get controller name
var modelState = (filterContext.Controller. as Controller).ViewData.ModelState; // Get the model state of the request
var model = (filterContext.Controller as Controller).ViewData.Model; // Get the model of the request
//do your stuff here
base.OnActionExecuting(filterContext);
}
}
And then register your filter so it will be applied to every controller and every action in your project
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.Filters.Add(typeof(ValidationFilterAttribute));
});
}
This way before hitting the controller's action method body each request will pass through your ValidationFilterAttribute where you can examine his Model and change his ModelState.

How to redirect all pages in my mvc asp.net web app except for one role?

I work with asp.net c# mvc framework. I need a way to 'turn-off' my web app for all users except administrator (i. e. all pages should return to something like "The application is closed" for all the roles except Admin).
I already create a button in order to save the status of the web app (ON/OFF) in a DB.
Do I have to check on each page the status of the application ?
Is-it possible to have a global redirection except for one role ?
I don't know how to properly do this global closure. Any suggestions are welcomed.
I can think of three approaches to check and do a redircet
An HttpModule hooked into the appropriate, post-authorisation event. Presumably PostAuthorizeRequest of HttpApplication.
In your "global" (Global.aspx.cs) subscribe to that same event.
An MVC Action filter, overriding OnActionExecuting. (Ensure you make it global, to avoid needing to apply to every controller: add to GlobalFilters.Filters in your Application_Start.)
Of these 3 is part of MVC, but is much later in the pipeline (much more work will have been done, to be thrown away when the filter fails).
Use of a module is controlled by configuration which would make is easier to switch on and off.
option 2 is likely easiest to implement, but I would tend to prefer the modularity that 1 gives.
You can accomplish your requirement with the help of custom filters shown below :-
[CheckUserRole]
public class YourController : Controller
{
public ActionResult YourAction()
{
}
}
public class CheckUserRoleAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Get the User Id from the session
// Get Role associated with the user (probably from database)
// Get the permission associated with the role (like Read, write etc)
// if user is not authenticated then do as :
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
Did you tryActionFilterAttribute ?
Here is a basic example:
Your controller:
[IsAdmin]
public class YourController
{
}
Your attribute
public class IsAdminAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if () // Check that your user is not an Admin and that your application is "turn-off"
{
filterContext.Result = new HttpStatusCodeResult(403); // or whatever you want
}
}
}
Add [IsAdmin] on top of all your controllers.
You can write in all other Controllers which are used as follows..
public class HomeController : Controller
{
public ActionResult Index()
{
if (User.IsInRole("Administrator"))
return RedirectToAction("PagetoRedirect");
else
return RedirectToAction("CommonPagetoShowApplicationAsClosed");
}
}
Or
Action Filter, you can create on your own and look for named action like IndexRolename

Found a cutstom ActionFilterAttribute in sample project but I can't figure out where it is called

I downloaded a sample MVC application and in it there is a class called "AuthorizationHelper" that inherits from ActionFilterAttribute but doing a search of the project I can not find where or how this helper is being called so I am confused on this is being called. I understand what the helper is doing I just can't figure out how it might be called. Here is the code if it helps at all:
public class AuthorizationHelper : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string AppLocation = WebConfigurationManager.AppSettings["AppLocation"];
if (AppLocation == "Development")
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Home"},
{"action", "Index"},
{"area", ""}
}
);
}
base.OnActionExecuted(filterContext);
}
}
This is an action filter which can either be registered in App_Start\FilterConfig.cs, or used to decorate controllers, or actions. If you take a look in App_Start\FilterConfig.cs, you might find something like this:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizationHelper());
}
}
In your particular case, OnActionExecuted means the filter will be executed after an action has been executed but before the action result has been executed.
For more information, I'd suggest reading Understanding Action Filters (C#). Although this link is for older versions of MVC, the only difference is that of how the filter is registered. Versions of MVC older than MVC 4 would register filters directly within Global.asax, rather than in App_Start\FilterConfig.cs.

How can I redirect to different views on authorization failure?

While using mvc intranet template with domain authorization, how can I redirect to some certain error page on authorization failure depending on the controller?
So, I have a controller class:
[AuthorizeWithRedirect(Users = #"user")]
public class MyController : Controller
{
...
}
By default I'm not redirected anywhere. I see only a blank page, if I open the page under another user. The issue is that I want the request to be redirected to different pages for different controllers if the authorization fails. That is, one page for MyController, another page for other controller, etc.
I know I could derive from AuthorizeAttribute and override HandleUnauthorizedRequest method. But I can't make it work:
public class AuthorizeWithRedirect : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
UrlHelper urlHelper = new UrlHelper(context.RequestContext);
context.Result = new RedirectResult(urlHelper.Action("MyErrorPage"));
}
}
I get an error, saying that the specified url is not found, while MyErrorPage.cshtml does present in the Views\Shared folder.
EDIT
[Authorize(Users = #"user")]//should be redirected to ErrorPage1
public class MyController1 : Controller
{
...
}
while
[Authorize(Users = #"user")]//should be redirected to ErrorPage2
public class MyController2 : Controller
{
...
}
That is, different error pages for different controllers on one and the same authorization failure
The AuthorizeWithRedirect looks good.
But MVC is complaining that you do not have right action method.
The view is not an issue here. The controller and action method is.
Try something like this:
context.Result = new RedirectResult(urlHelper.Action("Test", "Redirect"));
With present TestController with an public action method Redirect.
Use the Handle Error Data Annotation on top of your login method
[HandleError]
public ActionResult Login()
{
//your action method
}

If all my .Net Controllers/Action need Authorize Attr, Why don't have a attribute to use only those who do not need?

I have a App that need authorization to access all Controllers/Actions. Except the Login and Error Controllers/Actions.
With this scenario, working in a defensive manner is better to keep default restrict access to all Controllers/Actions(without Authorize Attribute) and select with a custom Attribute only those who do not.
Have you guys done something like this?
I have a MVC Filter that execute before all Actions if the Logged User have access to them:
public class ValidatePermissionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
bool isAuthorized = false;
//Logic that verify if logged user have permission to access the requested Controller/Action
...
//Redirect to a page Error if Logged User don't have Authorization
if (!isAuthorized)
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Erro");
redirectTargetDictionary.Add("controller", "Index");
context.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
I'm thinking the best way to do this. I can create a Blank Custom Attribute and put in the Controllers do not need authorization and check it in my Filter:
public class ValidatePermissionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
bool isAuthorized = false;
var DoNotRequiresAuthorizationAttributes = context.ActionDescriptor.GetCustomAttributes(typeof(DoNotRequiresAuthorizationAttribute), false);
if (DoNotRequiresAuthorizationAttributes.Length > 0)
isAuthorized = true;
...
//Redirect to a page Error if Logged User don't have Authorization
if (!isAuthorized)
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Erro");
redirectTargetDictionary.Add("controller", "Index");
context.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
What you expert Guys think?
Update:
Thinking better, I can replace my Filter with a Custom Authorize Attribute and register that to act in all Controllers/Actions in Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyCustomAuthorizeAttribute());
}
Update 2:
Instead create a Blank Custom Attribute and put in the Controllers do not need authorization I pass in Parameters of my Custom Authorize the Controllers do not need authorization (in Global.asax):
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ValidatePermissionAttribute("Login", "Erro"));
}
My Authorize Attribute:
public class ValidatePermissionAttribute : AuthorizeAttribute
{
...
public ValidatePermissionAttribute(params string[] optionalControllers)
{
_optionalControllers = optionalControllers;
}
...
}
Update 3:
Conditional Filters is the way to go.
Have you considered using Conditional Filters in ASP.NET MVC 3?
Several ways to handle bulk implementations of attributes:
Create a custom controller base class and implement there.
I think you can use the MVC filter's global filters collection: http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx
I've been told before the issue with using a filter attribute is that the result can be cached via output caching, and then this wouldn't run. It's better to implement an IAuthorizationFilter interface (or AuthorizeAttribute class) and create an authorization filter instead.
If the goal is just to reduce the need to re-declare the attribute in many places, it seems one could achieve the same by creating one abstract AuthorizeController with the attribute, and any controller whose actions all require authorization can inherit that.

Categories