Multiple roles in mvc not working - asp.net-mvc

I am using
Authorize[Roles = "Agent")]
Which has always worked fine, however now I am to check if they are also in the paid usergroup I thought I could just do this:
Authorize[Roles = "Agent, Paid")]
However the above isnt working, it seems to be checking if I am in any of them roles instead of if I am in both. What do I do here ?

You should do your custom Authorize Attribute
public class AuthorizeMultipleAttribute : AuthorizeAttribute
{
//Authorize multiple roles
public string MultipleRoles { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
//Logic here
//Note: Make a split on MultipleRoles, by ','
//User is in both roles => return true, else return false
}
}
DEMO :
[AuthorizeMultiple(MultipleRoles ="Agent,Paid")]

Related

base.AuthorizeCore(httpContext) is always false

I referred to hundreds of posts and no luck yet. The base.AuthorizeCore(httpContext) returns false always.
I am running the MVC application from VS2012 using IIS express. Many people were able to solve this issue with forms based authentication. But i tried that too.
Please help..
Below is the AuthorizeADAttribute is am using.
public class AuthorizeADAttribute : AuthorizeAttribute
{
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (base.AuthorizeCore(httpContext))
{
// var authorized = (httpContext.User.Identity.IsAuthenticated);
/* Return true immediately if the authorization is not
locked down to any particular AD group */
if (String.IsNullOrEmpty(Groups))
return true;
// Get the AD groups
var groups = Groups.Split(',').ToList<string>();
// Verify that the user is in the given AD group (if any)
var context = new PrincipalContext(ContextType.Domain, "MYDOMAIN");
var userPrincipal = UserPrincipal.FindByIdentity(context,
IdentityType.SamAccountName,
httpContext.User.Identity.Name);
foreach (var group in groups)
{
try
{
if (userPrincipal.IsMemberOf(context, IdentityType.Name, group))
return true;
}
catch (NoMatchingPrincipalException exc)
{
var msg = String.Format("While authenticating a user, the operation failed due to the group {0} could not be found in Active Directory.", group);
System.ApplicationException e = new System.ApplicationException(msg, exc);
// ErrorSignal.FromCurrentContext().Raise(e);
return false;
}
catch (Exception exc)
{
var msg = "While authenticating a user, the operation failed.";
System.ApplicationException e = new System.ApplicationException(msg, exc);
//ErrorSignal.FromCurrentContext().Raise(e);
return false;
}
}
}
return false;
}
}
I am passing the group name like this. This works perfect when i run the application from VS2012 using IIS Express. The web.config file is set to
In the IIS settings Forms Based Authentication isenabled. But the URL redirection goes to login.aspx. I dont have any login page in my application
But when I publish the website to IIS. Error page shows up.
[AuthorizeAD(Groups = "DevUsers")]
public ActionResult Index()
{
return View();
}
base.AuthorizeCore(httpContext) works base on roles and group. you may not have the group of 'DevUsers'.
add public string CustomGroups { get; set; } to class and call it like this:
[AuthorizeAD(CustomGroups = "DevUsers")]
public ActionResult Index()
{
return View();
}
in this way which you send the group null then base.AuthorizeCore(httpContext) just does the authenticate part and you do the groups and role in your custom function.
Simply forget about httpcontext getting receive in the method... Stuff can interfere with things...
like this:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string Domain = WebConfigurationManager.AppSettings["Domain"];
string AdGroups = WebConfigurationManager.AppSettings["AdGroups"];
/* Return true immediately if the authorization is not
locked down to any particular AD group */
if (String.IsNullOrEmpty(AdGroups))
return true;
// Get the AD groups
//var groups = Groups.Split(',').ToList();
WindowsIdentity CurrentIdentity = WindowsIdentity.GetCurrent();
UserPrincipal userPrincipal = UserPrincipal.Current;
var groups = AdGroups.Split(',').ToList();
List<GroupPrincipal> result = new List<GroupPrincipal>();
PrincipalSearchResult<Principal> groups2 = userPrincipal.GetAuthorizationGroups();
// iterate over all groups
foreach (Principal p in groups2)
{
// make sure to add only group principals
if (p is GroupPrincipal)
{
foreach (var group in groups)
try
{
if (p.ToString().Equals(group.ToString()))
{
return true;
}
}
catch (NoMatchingPrincipalException ex)
{
}
//result.Add((GroupPrincipal)p);
}
}
return false;
}

How can I use an action filter in ASP.NET MVC to route to a different view but using the same URL?

Is it possible to make a filter that, after a controller action has been (mostly) processed, checks for a certain test condition and routes to a different view transparently to the user (i.e., no change in the URL)?
Here would be my best guess at some pseudocode:
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// If some condition is true
// Change the resulting view resolution to XYZ
base.OnResultExecuting(filterContext);
}
filterContext.Result = new ViewResult
{
ViewName = "~/Views/SomeController/SomeView.cshtml"
};
This will short-circuit the execution of the action.
also you can return view as from your action
public ActionResult Index()
{
return View(#"~/Views/SomeView.aspx");
}
This is what I ended up doing, and wrapped up into a reusable attribute and the great thing is it retains the original URL while redirecting (or applying whatever result you wish) based on your requirements:
public class AuthoriseSiteAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Perform your condition, or straight result assignment here.
// For me I had to test the existance of a cookie.
if (yourConditionHere)
filterContext.Result = new SiteAccessDeniedResult();
}
}
public class SiteAccessDeniedResult : ViewResult
{
public SiteAccessDeniedResult()
{
ViewName = "~/Views/SiteAccess/Login.cshtml";
}
}
Then just add the attribute [SiteAccessAuthorise] to your controllers you wish to apply the authorisation access to (in my case) or add it to a BaseController. Make sure though the action you are redirecting to's underlying controller does not have the attribute though, or you'll be caught in an endless loop!
I have extended the AuthorizeAttribute of ASP.NET MVC action filter as DCIMAuthorize, in which I perform some security checks and if user is not authenticated or authorized then action filter will take user to access denied page. My implementation is as below:
public class DCIMAuthorize : AuthorizeAttribute
{
public string BusinessComponent { get; set; }
public string Action { get; set; }
public bool ResturnJsonResponse { get; set; }
public bool Authorize { get; set; }
public DCIMAuthorize()
{
ResturnJsonResponse = true;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
try
{
//to check whether user is authenticated
if (!httpContext.User.Identity.IsAuthenticated)
return false;
//to check site level access
if (HttpContext.Current.Session["UserSites"] != null)
{
var allSites = (VList<VSiteList>)HttpContext.Current.Session["UserSites"];
if (allSites.Count <= 0)
return false;
}
else
return false;
// use Authorize for authorization
Authorize = false;
string[] roles = null;
//get roles for currently login user
if (HttpContext.Current.Session["Roles"] != null)
{
roles = (string[])HttpContext.Current.Session["Roles"];
}
if (roles != null)
{
//for multiple roles
string[] keys = new string[roles.Length];
int index = 0;
// for each role, there is separate key
foreach (string role in roles)
{
keys[index] = role + "-" + BusinessComponent + "-" + Action;
index++;
}
//access Authorization Details and compare with keys
if (HttpContext.Current.Application["AuthorizationDetails"] != null)
{
Hashtable authorizationDetails = (Hashtable)HttpContext.Current.Application["AuthorizationDetails"];
bool hasKey = false;
foreach (var item in keys)
{
hasKey = authorizationDetails.ContainsKey(item);
if (hasKey)
{
Authorize = hasKey;
break;
}
}
}
}
return base.AuthorizeCore(httpContext);
}
catch (Exception)
{
throw;
}
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
try
{
filterContext.Controller.ViewData["ResturnJsonResponse"] = ResturnJsonResponse;
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
return;
}
if (!Authorize)
{
//Authorization failed, redirect to Access Denied Page
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary{{ "controller", "Base" },
{ "action", "AccessDenied" }
//{ "returnUrl", filterContext.HttpContext.Request.RawUrl }
});
}
}
catch (Exception)
{
throw;
}
}
}
You Can Also Save All Route File Path in a Static And Use it Like This :
public static class ViewPath
{
public const string SomeViewName = "~/Views/SomeViewName.cshtml";
//...
}
And into Your ActionFilter :
context.Result = new ViewResult()
{
ViewName = ViewPath.SomeViewName /*"~/Views/SomeViewName.cshtml"*/
};

Custom Authorize Attribute

I'm building my own membership system and I want nothing to do with the MS Membership provider. I've looked around the internet and here on StackOverflow but all I could found was membership providers built on top of the MS Membership provider.
Anyway, I've got almost everything hooked up now, but I'd like to use a custom Authorize attribute which utilized my membership infrastructure. I checked out this thread here on the site and I'm trying to do something similar, but I'm not sure that's quiet what I need. So far these are the classes I've got:
SessionManager:
public static class SessionManager : ISessionManager
{
public static void RegisterSession(string key, object obj)
{
System.Web.HttpContext.Current.Session[key] = obj;
}
public static void FreeSession(string key)
{
System.Web.HttpContext.Current.Session[key] = null;
}
public static bool CheckSession(string key)
{
if (System.Web.HttpContext.Current.Session[key] != null)
return true;
else
return false;
}
public static object ReturnSessionObject(string key)
{
if (CheckSession(key))
return System.Web.HttpContext.Current.Session[key];
else
return null;
}
}
SharweAuthorizeAttribute: (I am not really sure if that's actually what I should be doing)
public class SharweAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (SessionManager.CheckSession(SessionKeys.User) == true)
return true;
else
return false;
}
}
Now here's what I need:
Is my SharweAuthorizeAttribute class
correct in the first place?
I need to be able to redirect
unauthenticated users to the login
page
I need to authorize users based on
their roles (using my own role
provider) so I would do something
like:
[SharweAuthorize(Roles="MyRole")]
That's it I guess... Any suggestions are more than welcome :)
UPDATE:
Ok I just read that page again and found the solution to question number two:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (SessionManager.CheckSession(SessionKeys.User) == false)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "ActionName" },
{ "controller", "ControllerName" }
});
}
else
base.HandleUnauthorizedRequest(filterContext);
}
Let me know if I got it right please...
Yes, you got it right (IMO it's safer and simpler to implement a custom membership provider, but it's your choice)
Yes, it's correct
You do it right
You inherit the roles property from the AuthorizeAttribute base class and you check in your implementation if the user is in the role.
Edit: a little more on the roles thing
if you have
[SharweAuthorize(Roles="MyRole")]
then you can check the Roles property in the AuthorizeCore method
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (SessionManager.CheckSession(SessionKeys.User) == true) {
if (SessionManager.CheckUserIsInRole( Roles )) // where Roles == "MyRole"
return true;
}
return false;
}

Custom Authorize Attribute (follow-up)

Ok following up with this thread, here's what I came up with...
public class SharweAuthorizeAttribute : AuthorizeAttribute
{
private bool isAuthenticated = false;
private bool isAuthorized = false;
public new string[] Roles { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (SessionManager.CheckSession(SessionKeys.User) == true)
{
isAuthenticated = true;
foreach (string role in Roles)
{
if (RolesService.HasRole((string)role))
isAuthorized = true;
}
}
return (isAuthenticated && isAuthorized);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!isAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "User" },
{ "controller", "Login" }
});
} else if(!isAuthorized) {
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "Home" },
{ "controller", "Error" }
});
}
}
}
How/why I came up with this? Because I believe the AuthorizeAttribute workflow goes as follows:
First, AuthorizeCore is triggered. If it returns true, the user is authorized. If it returns false, HandleUnauthorizedRequest is triggered. Is that right?
I read somewhere that I need to use the new keyword to override a property. Therefore, this is how I overrode the Roles property. But what if the overriding property was of a different type of the initial one (the one in the base class), does that also hide it or creates a totally different property?
So what do you think? Should that actually work? I cannot test it now because I haven't set up the UI (waiting for the designer to get done with the design)... In fact, this is the first time I appreciate the benefits of TDD, I used to think it's utterly stupid and useless, but I was wrong :)
P.S: On this thread, #tvanfosson is setting the CachePolicy of the context (I think), could someone explain that and why I might need to do that please?
Thanks in advance.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private readonly bool _authorize;
private readonly string[] _roles;
public CustomAuthorizeAttribute(string roles)
{
_authorize = true;
_roles = roles.Split(',');
}
public CustomAuthorizeAttribute(string roles, bool isAdminPath)
{
_authorize = true;
_roles = roles.Split(',');
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//if controller have role auth and user is not loged
if(_authorize && !httpContext.User.Identity.IsAuthenticated)
return false;
// if controller have role auth and user is loged
if(_roles != null)
{
//grab user roles from DB
var UserRole = RoleRepository.GetUserRole(new Guid(httpContext.User.Identity.Name));
if (_roles.Contains(UserRole))
return true;
}
return false;
}
}
In Controller
[CustomAuthorize("Administrator,Company,OtherRole")]
public ActionResult Test(){
return View();
}

How do I restrict access to certain pages in ASP.NET MVC?

I wish to lock out access to a user's EDIT page (eg. /user/pure.krome/edit) if
a) Identity.IsAuthenticated = false
or they are authenticated but
b) Idenitity.Name != user name of the user page they are trying to edit
c) Identity.UserType() != UserType.Administrator // This is like a Role, without using RoleProviders.
I'm assuming u can decorate a controller or a controller's action method with something(s), but i'm just not sure what?
Look at the AuthorizeAttribute.
ASP.Net MVC: Can the AuthorizeAttribute be overriden?
A custom attribute derived from AuthorizeAttribute is what I use to do this. Override the OnAuthorize method and implement your own logic.
public class OnlyUserAuthorizedAttribute : AuthorizeAttribute
{
public override void OnAuthorize( AuthorizationContext filterContext )
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizeResult();
}
...
}
}
I implemented the following ActionFilterAttribute and it works to handle both authentication and roles. I am storing roles in my own DB tables like this:
User
UserRole (contains UserID and RoleID foreign keys)
Role
public class CheckRoleAttribute : ActionFilterAttribute
{
public string[] AllowedRoles { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userName = filterContext.HttpContext.User.Identity.Name;
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (AllowedRoles.Count() > 0)
{
IUserRepository userRepository = new UserRepository();
User user = userRepository.GetUser(userName);
bool userAuthorized = false;
foreach (Role userRole in user.Roles)
{
userAuthorized = false;
foreach (string allowedRole in AllowedRoles)
{
if (userRole.Name == allowedRole)
{
userAuthorized = true;
break;
}
}
}
if (userAuthorized == false)
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + String.Format("?ReturnUrl={0}", filterContext.HttpContext.Request.Url.AbsolutePath), true);
}
}
I call this like this...
[CheckRole(AllowedRoles = new string[] { "admin" })]
public ActionResult Delete(int id)
{
//delete logic here
}

Resources