Authorize Attribute with multiple Role Providers - c#

I am building a ASP.NET MVC App that will allow a user to connect to one of several databases (with the same schema) depending upon their selection. The user will select a database prior to login and then be authenticated against that database.
I have added added several role providers to web.config, each with a different connection string corresponding to each of the available databases.
I understand that I can access the role providers with the Roles.providers collection but I don't understand how I can choose which role provider is being user by the [Authorize] attribute in my controllers
My Controllers look something like this:
namespace MyApp.Controllers
{
[Authorize(Roles = "admin")]
public class AdminController : Controller
{
I assume that this will check the users role against the default role provider.
How can I select at run time which role provider will be used for the [Authorize] attribute?

As far as I know, there's no way to do what you're suggesting. Attributes are static metadata that can't be changed at run-time. However, even though it's less convenient, you can accomplish what you want by redirecting if the user doesn't have the appropriate role:
[Authorize]
public class AdminController : Controller {
public ActionResult ActionRequiringRoleFoo() {
if( !User.IsInRole( "foo" ) ) return RedirectToAction( "InsufficientPrivileges" );
return View();
}
If you want to have this behavior for all actions in your controller, you can override the OnActionExecuting method:
protected override void OnActionExecuting( ActionExecutingContext filterContext ) {
base.OnActionExecuting( filterContext );
if( filterContext.ActionDescriptor.ActionName != "InsufficientPrivileges" &&
!User.IsInRole( "anon" ) ) filterContext.Result = new RedirectResult( "InsufficientPrivileges" );
}
I hope that helps.

Related

Authorize attribute Roles from Database

I want to get the below roles(Admin,IT,..) from the database without hard coding on top of the action result. Please provide any help.
[Authorize(Roles = "Admin,IT")]
public ActionResult Index()
{
}
There aren't any super-easy ways to do this. You can apply the [Authorize] attribute to a controller instead of an action, but it is still "hard-coding" it.
You could create a custom Authorization attribute ([link])1, but you would have to store the Routing values in the database, as well as the Roles that were allowed to access the route. However this just shifts the burden of making manual changes into the database from the code.
I don't really think that this should really be considered "Hard Coding" as you have to declare your authorization somewhere, and you can still have different users with different permissions in different environments. Who else but the developer should know best which routes require which authorization? Would you want to break your access control because you changed the routing somewhere?
create an Action finter
public class ValidationPermission : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(System.Web.HttpContext.Current.Session["UserName"] == null)
System.Web.HttpContext.Current.Response.RedirectToRoute("Login");
else{
// code check CheckPermission
}
}
}
Action controller
[ValidationPermission(Action = ActionEnum.Read, Module = CModule)]
public ActionResult Index()
{
// something code
}
You can try with this way
public static Role {
public static string Admin ="Admin";
public static string IT ="IT";
}
[Authorize(Roles = Role.Admin,Role.IT)]
public ActionResult Index()
{
}

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

Security in MVC Views

In my MVC application I have a few different roles: Admin, General User, etc., etc.
I know that I can apply security to my Controllers via the Authorize attribute:
[Authorize(Roles="Admin")]
public ActionResult Create()
{
return View();
}
But I also need to apply some security to the Views to not display certain sections of the View to certain roles:
#if( User.IsInRole("Admin") )
{
#Html.ActionLink("Create", "Create")
}
Is it better to do it the above way, or handle this sort of security in a ViewModel:
public ActionResult Index()
{
var model = new IndexViewModel();
model.CanCreate = User.IsInRole("Admin");
return View(model);
}
View:
#( Model.CanCreate )
{
#Html.ActionLink("Create", "Create")
}
Does the second method have any benefits compared to the first or is it just a preference thing?
The second way is more preferred, as your business logic will stay at model level.
In your example, business logic is very simple. However, imagine that requirements have changed and now not only Admins can create content, but also General Users that signed up more than 1 month ago. With business logic in view you'd have to update all your views.
One way I have done this before is creating an action filter that inherits from the AuthorizeAttribute. The filter can be called something like DisplayIfAuthorizedAttribute, and in addition to the standard AuthorizeAttribute properties, has a property called ViewNameIfNotAuthorized.
The attribute calls the base method to do authorization, and if it fails, returns the ViewNameIfNotAuthorized view. Otherwise, it allows the action method to proceed normally.
You would then render these partial views via action methods, and call the action methods through Html.RenderAction or Html.Action in your parent view. Those action methods would be decorated with the attribute.
You now have a standardized way to do this and no authorization code polluting the internals of your action methods.
This is what the filter would look like:
public class DisplayIfAuthorizedAttribute : System.Web.Mvc.AuthorizeAttribute
{
private string _ViewNameIfNotAuthorized;
public DisplayIfAuthorizedAttribute(string viewNameIfNotAuthorized = null)
{
_ViewNameIfNotAuthorized = viewNameIfNotAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool isAuthorized = base.AuthorizeCore(filterContext.HttpContext);
if (!isAuthorized)
{
filterContext.Result = GetFailedResult();
}
}
private ActionResult GetFailedResult()
{
if (!String.IsNullOrEmpty(_ViewNameIfNotAuthorized))
{
return new ViewResult { ViewName = _ViewNameIfNotAuthorized };
}
else
return new EmptyResult();
}
}
Your action method would be decorate as:
[DisplayIfAuthorized("EmptyView", Roles="Admin")]
public ViewResult CreateLink()
{
return View("CreateLink");
}
You may need both...
Note that the 2nd one alone would not be secure, a user might be able to construct the URL for the actionlink in the browsers addressbar. So you absolutely need the attribute for security.
The second one is more a matter of user-friendliness or UI design. Maybe you want the user to be able to click Create and then have a choice to login differently.
Check the authorization in your controller and prepare the Viewmodel for the view according to your role's rules.
The views are used to simply show data. So, imo, they don't have to do roles check etc..
So prepare the ViewModel with the data it should have and let the View only render it. (the boolean property you're using it's enough imo)

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.

ASP.Net MVC: Can the AuthorizeAttribute be overriden?

My current project is an internal web application built using ASP.Net MVC which I am adding authentication to. I have a pre-built HTTPModule which creates a IPrincipal with the appropriate roles. If the user isn't authenticated I get a user object with the role "Public"
As this is an internal application most of the pages are private and only viewable to the role "Admin". As I have a base controller I can do this:
[Authorize(Roles="Admin")]
public abstract class MyControllerBase : Controller
{
...
}
I have a problem though as some of the actions are viewable on a public website and if I attribute them like so:
[Authorize(Roles="Public")]
public class LoginController : MyController
{
public ActionResult Index()
{
}
}
The page fails to load as the user isn't authenticated. It would seem the Role of "Public is being ignored on the inherited class. Does anyone know if the roles can be overridden by inherited classes?
I am also trying to avoid attributing all the controllers with Roles="Admin"
Thanks, Keith.
You can derive a new attribute from AuthorizeAttribute and override the OnAuthorization method, then apply your customized attribute instead of Authorize. Below is the OnAuthorization method from one of my customized attributes that redirects to an error page if the privileges aren't sufficient instead of redirecting to the logon page.
I'm not sure exactly what this will buy you, though. When you decorate your class with the attribute, presumably you'll have to allow both Admin and Public (so who are you restricting since Public is anyone who is not authenticated?). You'd then have to decorate each of the controller methods that need to be restricted to Admin individually since the class attribute would allow access otherwise. You can achieve this behavior with the regular Authorize attribute by simply decorating just those non-publicly available methods (or classes that have no publicly available methods).
I suppose you could have your attribute check to see if the method being called is also decorated with the attribute and simply approve the authorization, which would effectively defer the authorization to the method level. You'd probably have to peek into the RouteData on the AuthorizationContext to get the action and use reflection to try and find the appropriate method based on parameters and request type.
public override void OnAuthorization( AuthorizationContext filterContext )
{
if (filterContext == null)
{
throw new ArgumentNullException( "filterContext" );
}
if (AuthorizeCore( filterContext.HttpContext ))
{
SetCachePolicy( filterContext );
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
}
}
protected void SetCachePolicy( AuthorizationContext filterContext )
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge( new TimeSpan( 0 ) );
cachePolicy.AddValidationCallback( CacheValidateHandler, null /* data */);
}
Well in the end I think my answer was in the question. Instead of putting the Authorize attribute on my base controller I have derived a new AdminBaseController.
[HandleError]
public abstract class MyControllerBase : Controller
{
...
}
[Authorize(Roles="Admin")]
public abstract class AdminControllerBase : MyControllerBase
{
....
}
Now any controllers that require authentication can derive from AdminControllerBase while my public controllers can derive from MyControllerBase. OO to the rescue.

Categories