How to modify Authorize attribute to allow a group of User Roles in MVC 3 - asp.net-mvc-3

In my MVC3 wep app, I have extended the Authorize attribute like below
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (Authenticate.IsAuthenticated() && httpContext.User.Identity.IsAuthenticated)
{
var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var roles = ticket.UserData.Split('|');
var identity = new GenericIdentity(ticket.Name);
httpContext.User = new GenericPrincipal(identity, roles);
}
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!Authenticate.IsAuthenticated())
HandleUnauthorizedRequest(filterContext);
base.OnAuthorization(filterContext);
}
In my Actions, I use it like
[MyAuthorize(Roles = "Member,Inspector,SalesRep,Admin,SuperAdmin")]
public ActionResult OrderUpload()
Now, I have to specify each user role in every action. What I would like to do is
specify something like below
[MyAuthorize(Roles = "Member")]
public ActionResult OrderUpload()
and this should allow any User Role that are equal or above "Member". So a "SalesRep" should be allowed, where as "Visitor", who is below the "Member" should not be allowed.
All User Roles are enum with increasing numbers
public enum UserAccountType
{
Visitor = 5,
Member = 10,
Inspector = 15,
SalesRep = 20,
Admin = 25,
SuperAdmin = 30
}
How do I modify MyAuthorizeAttribute to make this work?
Thanks

Here is my working code
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (Authenticate.IsAuthenticated() && httpContext.User.Identity.IsAuthenticated)
{
var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
string[] roles = null;
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
roles = ticket.UserData.Split('|');
var identity = new GenericIdentity(ticket.Name);
httpContext.User = new GenericPrincipal(identity, roles);
}
if (Roles == string.Empty)
return true;
//Assuming Roles given in the MyAuthorize attribute will only have 1 UserAccountType - if more than one, no errors thrown but will always return false
else if ((UserAccountType)Enum.Parse(typeof(UserAccountType), roles[0]) >= (UserAccountType)Enum.Parse(typeof(UserAccountType), Roles))
return true;
else
return false;
}
else
return false;
//return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!Authenticate.IsAuthenticated())
HandleUnauthorizedRequest(filterContext);
base.OnAuthorization(filterContext);
}
}

I don't use AuthorizeAttribute but ActionFilter (it's just me and that's how I learned it) but What I would do is add a property on the AuthorizeAttribute that gets updated when the Attribute gets triggered before the Action.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private string Role = "";
public MyAuthorizeAttribute(string role){
this.Role = role;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
:
:
:
// now do a check if the Role is authorized or not using your enum.
// return error page if not
if(RoleisAuthorized)
return;
else
// error page
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
:
:
:
}
}
Now after you got the Role, go get it from the enum and compare if the role is allowed to access the page or not, if not return an error page. So since I'm not familiar with OnAuthorization, I would place the process inside AuthorizeCore.

Related

How to redirect user after login Web API?

If my user encounters a controller with my attribute [CustomAuthorize], he is redirected to the login page where he gets his JWT token. But on successful login I want to redirect him to the place where he initially wanted to be(the URL he wrote before was redirected to login page). what's the best way to save the path?
That's my CustomAuthorizeAttribute:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("~/Login");
return;
}
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectResult("~/Login");
return;
}
}
}
In other words, how to know from which request the user came from?
I'be decided to write pass a parameter to Login Controller like this
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var values = new RouteValueDictionary(new
{
controller = "Login",
RequestedAddress = filterContext.RequestContext.RouteData.Values.Values.First()
});
filterContext.Result = new RedirectToRouteResult(values);
return;
}
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectResult("~/Login");
return;
}
}
And then to get it like this
public class LoginController : Controller
{
public ActionResult Index(String RequestedAddress)
{
return View();
}
}

webapi-use claims with windows authentication

The method has been secured with roles=admin:
[Authorize(Roles = "admin")]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}}
I am successfully to use claims with Webapi project where Individual User Account is selected where the claim admin is injected in
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
userIdentity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
return userIdentity;
}
}
Now I want to test with Windows authentication option where a IAuthenticationFilter is implemented:
public class CustomAuthenticationFilter : IAuthenticationFilter
{
public bool AllowMultiple { get { return true; } }
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var windowsPrincipal = context.Principal as WindowsPrincipal;
if (windowsPrincipal != null)
{
var name = windowsPrincipal.Identity.Name;
// TODO: fetch claims from db (i guess based on name)
var identity = new ClaimsIdentity(windowsPrincipal.Identity);
identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
var claimsPrincipal = new ClaimsPrincipal(identity);
// here is the punchline - we're replacing original windows principal
// with our own claims principal
context.Principal = claimsPrincipal;
}
return Task.FromResult(0);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
}
and added to class webapiconfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Filters.Add(new CustomAuthenticationFilter());
...
}
}
The claim admin is in User.Identity.Claims when debugging webapi project, however it could not be authorized in method /api/values/get.
Any idea?
The default identity RoleClaimType is identity/claims/groupsid which is not role.
By setting RoleClaimType to identity/claims/role in the ClaimsIdentity constructor, we can get it passing [Authorize(Roles = "admin")]
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var windowsPrincipal = context.Principal as WindowsPrincipal;
if (windowsPrincipal != null)
{
var name = windowsPrincipal.Identity.Name;
// TODO: fetch claims from db (i guess based on name)
var identity = new ClaimsIdentity(windowsPrincipal.Identity,
null,
"Negotiate",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
//identity
identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
var claimsPrincipal = new ClaimsPrincipal(identity);
// here is the punchline - we're replacing original windows principal
// with our own claims principal
context.Principal = claimsPrincipal;
}
return Task.FromResult(0);
}
Here is the new identity:

Custom Filter Attribute or call in Action Method - Earlier in Request Pipeline but Performance?

I have a HomeController with about 8 or 9 Action Methods.
About 7 of these methods require a check to see if the User has a special setting or not to see if they are allowed to access these Methods and related Views.
If they are not they are redirected back to a Common Action Method and View.
public class HomeController : Controller
{
public ActionResult Index() {
UserManager um = new UserManager();
um.Punter p = um.GetPunter(User.Identity.Name);
return View(p);
}
public ActionResult PunterList() {
UserManager um = new UserManager();
um.Punter p = um.GetPunter(User.Identity.Name);
if (p.isPunter) {
return RedirectToAction("Index", "Home");
} else {
return View(p);
}
}
}
The check in 'PunterList' is done in other Action Methods, I was thinking about creating a FilterAttribute to do this check. As per the following:
public class NoPunterAttribute : FilterAttribute, IActionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
UserManager um = new UserManager();
um.Punter p = um.GetPunter(User.Identity.Name);
if (p.isPunter) {
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Home" }, { "action", "Index" } });
}
}
public void OnActionExecuted(ActionExecutedContext filterContext) { }
}
then put this attribute on the Action method this type of user cannot access.
[NoPunter]
public ActionResult PunterList() {
UserManager um = new UserManager();
um.Punter p = um.GetPunter(User.Identity.Name);
return View(p);
}
this puts this code in 1 place, However the UserManager.GetPunter is called twice if the User.isPunter=false. Perhaps this is not such a good idea for Performance or Memory conservation of the MVC web application.
The benefit is does the check earlier in the Request pipeline, but perhaps a method called inside of the action method would mean .GetPunter would be called only once, but further along the Request pipeline. Not sure about this, kind of split on earlier vs Performance/Memory issues.
Any suggestions or ideas would be interesting to hear. Presumably it would depend on what is done inside UserManager.GetPunter. There is some caching inside this call but it does requery the cache.
You could write a custom authorization attribute which will inject the Punter as a parameter of your action:
public class HomeController : Controller
{
public ActionResult Index()
{
UserManager um = new UserManager();
um.Punter p = um.GetPunter(User.Identity.Name);
return View(p);
}
[NoPunterAuthorize]
public ActionResult PunterList(Punter punter)
{
return View(punter);
}
}
and the custom authorization attribute:
public class NoPunterAuthorize: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
var um = new UserManager();
var p = um.GetPunter(httpContext.User.Identity.Name);
var routeData = httpContext.Request.RequestContext.RouteData;
routeData.Values["punter"] = p;
return !p.IsPunter;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Home" },
{ "action", "Index" }
}
);
}
}

How to create custom JsonAuthorize Attribute to secure actions which returns JsonResults?

I was thinking how to correctly secure JsonResult action with custom attribute instead of doing kind of this on each action like saying here ASP.NET MVC JsonResult and AuthorizeAttribute
if (!User.Identity.IsAuthenticated)
return Json("Need to login");
But the question is how could i create such attribute which would return Json.
So i've started from that:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class JsonAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
//?
}
//Need to return json somehow ?
}
}
Bot how i may return json result from such attribute? any ideas?
You can use an ActionFilterAttribute which allows you to return a result without using the httpcontext.response.write or anything.
public class JsonActionFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (!HttpContext.Current.User.Identity.IsAuthenticated) {
filterContext.Result = new JsonResult() { Data = "Need to login." };
}
base.OnActionExecuting(filterContext);
}
}
1 way is to override AuthorizeAttribute.HandleUnauthorizedRequest
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
throw new CustomUnauthorizedException();
}
... And then in your Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
Exception error = Server.GetLastError();
if (error is CustomUnauthorizedException) {
if (AjaxRequest(Request)) {
... return Json response.
} else {
... redirect
}
}
}
So you can throw the exception anywhere in your codebase and you've centralized the handling of that exception in Global.asax
Try this.. it works for me
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
dynamic ResponseObj = new JObject();
ResponseObj.Message = "Authorization has been denied for this request.";
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(ResponseObj);
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent(jsonString, System.Text.Encoding.UTF8,"application/json")
};
}

session becoming null in MVC AuthorizeAttribute

I am using an AuthorizeAttribute to check that users have an over 18 age cookie set to access pages.
This works fine, but I am extending in slightly now.
As all Views use this Attribute, I am using it to allow me to launch my site early.
If uses add ?VIEWSITE=true to any URL, it will set a Session variable, and allow them access to the site. Otherwise, they get directed to a holding page.
This works fine first time the page runs. But, I am using output caching on the page, and the next time the page loads, my httpcontext.session is null?
I've added an "Order" varible to my attributes to ensure they execute in the correct order:
[OfAge(Order = 1)]
[OutputCache(Order = 2, Duration = 2000, VaryByParam = "categoryName")]
Snipit from my Attribute:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
HttpRequestBase req = httpContext.Request;
HttpResponseBase res = httpContext.Response;
DateTime Live_Date = new DateTime(2011, 07, 01, 07, 0, 0);
if (DateTime.Now > Live_Date || req.QueryString["VIEWSITE"] != null || httpContext.Session["VIEWSITE"] != null)
{
httpContext.Session["VIEWSITE"] = true;
Is there something I am missing here for me to be able to read/set session variables once a page is loaded from cache?
To be clear, it's httpContext.Session that is null, and not specifically httpContext.Session["VIEWSITE"]
3 years down the line and I ran into a similar issue. Now I'm no expert but I believe each controller context call is unique in it's own space, thus httpContext.Session would be null on a new call.
My issue came in the form of a logged in AD user I wanted to store (with his custom application permissions) in a session variable. I'm extending on the AuthorizationAttribute too, but when this filter is applied to a controller action, httpContext is null even though the user was saved.
For people battling with the same issue, the way around this is to create a base controller where this user and it's session state is kept throughout other controllers (inheriting the base controller).
ex.
My Model:
public class LoggedInUser
{
public somenamespace.userclass UserProfile { get; set; }
public List<somenamespace.user_permission_class> UserPermissions { get; set; }
}
My Base Controller:
public class ControllerBase : Controller
{
private LoggedInUser _LoginUser;
public LoggedInUser LoginUser
{
get
{
if (_LoginUser != null)
return _LoginUser;
if (Session["_LoginUser"] == null)
return null;
return Session["_LoginUser"] as LoggedInUser;
}
set
{
_LoginUser = value;
Session["_LoginUser"] = _LoginUser;
}
}
public void PerformUserSetup(string sUsername) // sUsername for testing another user, otherwise User.Identity will be used.
{
sUsername = string.IsNullOrEmpty(sUsername) ? User.Identity.Name : sUsername;
sUsername = (sUsername.IndexOf("\\") > 0) ? sUsername.Split('\\').ToArray()[1] : sUsername;
// Todo - SQL conversion to stored procedure
List<userclass> tmpUser = Root.Query<userclass>(/*sql to select user*/).ToList();
List<user_permission_class> tmpUserpermissions = Root.Query<user_permission_class>(/*sql to select user permissions*/).ToList();
LoggedInUser _LoginUser = new LoggedInUser();
_LoginUser.UserProfile = tmpUser.First();
_LoginUser.UserPermissions = tmpUserpermissions;
LoginUser = _LoginUser;
}
}
My HomeController (standard with any MVC example) :
public class HomeController : ControllerBase
{
[Authorize] // Standard AuthorizeAttribute (AD test)
public ActionResult Index()
{
if (Session["_LoginUser"] == null)
PerformUserSetup("");
return View();
}
}
My Custom permission checking filter which I'll use on any other controller action:
public class PermissionAuthorize : AuthorizeAttribute
{
private readonly string[] permissions;
public PermissionAuthorize(params string[] perms)
{
this.permissions = perms;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool auth = false;
if (httpContext.Session["_LoginUser"] == null)
{
// Do nothing as auth is false.
}
else
{
// Check permissions and set auth = true if permission is valid.
auth = true;
}
return auth;
}
/* not using
public override void OnAuthorization(AuthorizationContext filterContext)
{
var tmp = filterContext.HttpContext.Session;
}
*/
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Todo - direct to "unauth page"
base.HandleUnauthorizedRequest(filterContext);
}
}
Usage:
public class Some_OtherController : /*PossibleNamespace?.*/ControllerBase
{
[PermissionAuthorize("somepermission")] // This was a CRUD application thus 1 permission per actionresult
public ActionResult ViewWhatever()
{
....
}
}

Resources