I'm trying to create a custom Authorize attribute to do the following:
If the user has a role of "Regular user" - he is redirected to /index/subscribe
All other users(Administrator,Subscriber) gets access to /Search/Index
This is when the user tries to open up the Search controller. I made the custom Authorize attribute like this:
public class DenyRegularUser : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("~/User/Logon");
return;
}
if (filterContext.HttpContext.User.IsInRole("Regular user"))
{
filterContext.Result = new RedirectResult("~/Index/Subscribe");
}
}
}
And this is my Search controller:
namespace WebApplication2.Controllers
{
[DenyRegularUser(Roles ="Regular user")]
public class SearchController : Controller
{
// GET: Search
public ActionResult Index()
{
return View();
}
}
}
But for some reason, even when I update the user's role from Regular user to Administrator or Subscriber, I get redirected to login page: /user/login...
This shouldn't happen as the login functionality works perfectly and I get the role of the user...
What am I missing out here??
This may help.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class DenyRegularUser : AuthorizeAttribute
{
public DenyRegularUser() :
base()
{
}
protected override bool IsAuthorized (System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (AuthorizeRequest(actionContext))
{
return true;
}
return false;
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Code to handle unauthorized request
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.TemporaryRedirect);
actionContext.Response.Headers.Add("Location", "~/Index/Subscribe");
}
private bool AuthorizeRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Write your code here to perform authorization
}
}
I believe the IsAuthorized method is the correct way to override the AuthorizeAttribute.
Related
I have a Default Role for all controllers configured in my global.asax
protected override void Configure(HttpConfiguration config)
{
//Note: Client Authentication Filter is just a fancy AuthorizeAttribute
config.Filters.Add(new ClientAuthenticationFilter(APIRoles.MYAPI));
}
This adds a requirement for a role to all controllers. I would like to override that role for a specific controller
public class MFAController : ApiController
{
[HttpGet]
[Route(AuthAPIRoutes.GET_MFA_DEVICES)]
[Authorize(Roles = "MyCustomRoles")]
public MFAMethodDTO[] GetMultiFactorMethods()
{
return GlobalFactory<IMFASecurityService>.Instance.GetMultiFactorMethods();
}
//...
}
However when I do this. I get an error because my role original role APIRoles.MYAPI is missing. Is there a default way to override the AuthorizationAtrributes for controllers so they take precedence over the Global Filter?
If I understand correctly, you can do like below. This is not exactly you want, it needs to customize.
public class MyAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
// ...
// if (HttpContext.Current.User == null || HttpContext.Current.User.Identity == null || !HttpContext.Current.User.Identity.IsAuthenticated)
// throw new Exception("Not logged in");
}
}
[MyAuthorize]
public bool DoSomthing()
{
...
}
I have created a custom Authorize attribute to authorize users trough a remote web API. After authorization I receive a object with token that is valid for some specific time and is used to access further information and I also get some basic user data like name, surname, role, etc ... which I store in Session.
Everything worked just fine but when I tried using Output Caching the Session I'm accessing in my Authorization Core method is null and application crashes there.
How to solve this problem or perhaps an alternative approach avoiding this as last resort?
Authorize attribute
public class AuthorizeUser : AuthorizeAttribute
{
private class Http401Result : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
// Set the response code to 401.
context.HttpContext.Response.StatusCode = 401;
context.HttpContext.Response.Write("Session expired, please log in again.");
context.HttpContext.Response.End();
}
}
private readonly string[] users;
public AuthorizeUser(params string[] usrs)
{
this.users= usrs;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool auth = false;
var loggedInUser= httpContext.Session["LoggedInUser"] as User;
if (loggedInUser != null)
auth = true;
return auth;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
filterContext.Result = new Http401Result();
else
filterContext.Result = new RedirectToRouteResult
(
new RouteValueDictionary
(
new
{
controller = "Account",
action = "Login",
}
)
);
}
}
Controller Setup
[AuthorizeUser]
public class SomeController : Controller
{
[HttpPost]
[OutputCache(VaryByParam ="Year", Duration = 3600)]
public async Task<JsonResult> SomeAction(int Year){ ... }
}
I'm migrating my project to asp.net core and I'm stuck in migrating my CustomAuthorization attribute for my controllers. Here is my code.
public class CustomAuthorization : AuthorizeAttribute
{
public string Url { get; set; }
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(Url + "?returnUrl=" + filterContext.HttpContext.Request.Url.PathAndQuery);
}
else if (!Roles.Split(',').Any(filterContext.HttpContext.User.IsInRole))
{
filterContext.Result = new ViewResult
{
ViewName = "AcessDenied"
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
then i used it to my controllers
[CustomAuthorization(Url = "/Admin/Account/Login", Roles = "Admin")]
public abstract class AdminController : Controller { }
so, basically i can use it to redirect to different login page when roles is not met. I have few areas and each of them have different login page. I tried using the CookieAuthenticationOptions like this
services.Configure<CookieAuthenticationOptions>(options =>
{
options.AuthenticationScheme = "Admin";
options.LoginPath = "/Admin/Account/Login";
});
then on my admin controller
[Area("Admin")]
[Authorize(ActiveAuthenticationSchemes = "Admin", Roles = "Admin")]
but after i login, it still cant get in.
I am doing something similar in one of my projects. This answer is NOT using AuthorizeAttribute; but it might help some one landing here from a google search.
In my case I am using it to authorize based on custom logic.
First my custom attribute class:
public class CustomAuthorizationAttribute : ActionFilterAttribute
{
private readonly IMyDepedency _dp;
public CustomAuthorizationAttribute(IMyDepedency dp)
{
_dp = dp;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var isValid = false;
//write my validation and authorization logic here
if(!isValid)
{
var unauthResult = new UnauthorizedResult();
context.Result = unauthResult;
}
base.OnActionExecuting(context);
}
}
I decorate my controllers like this:
[ServiceFilter(typeof (CustomAuthorizationAttribute))]
Then in my Startup class
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
// my other stuff that is not relevant in this post
// Security
services.AddTransient<CustomAuthorizationAttribute>();
}
I have a implemented a custom AuthorizeAtrribute class
public class AdminAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
}
}
Action method
[AdminAuthorize(Roles = "Admin")]
public ViewResult AdminOnly()
{
return View();
}
[AdminAuthorize(Roles = "Admin, Mod")]
public ViewResult Index()
{
return View();
}
When I have a user that IsAuthenticated but not in the Admin role I would like to redirect them to the Index page and not the logon page.
I've read the many other SO posts on this but my HandleUnauthorizedRequest() method is not firing.
this the code i always use when i work with active directory
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (base.AuthorizeCore(httpContext))
{
if (String.IsNullOrEmpty(Groups)) { return true; }
var groups = Groups.Split(',').ToList();
var context = new PrincipalContext(ContextType.Domain,"yourDomain");
var userPrincipal = UserPrincipal.FindByIdentity(context,IdentityType.SamAccountName,httpContext.User.Identity.Name);
foreach(var group in groups){ // this will check user if the right role in active directory
if(userPrincipal.IsMemberOf(context, IdentityType.Name, group)){
return true;
}
}
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var result = new ViewResult();
result.ViewName = "NotAuthorized";
result.MasterName = "_Layout";
filterContext.Result = result;
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
forgot to tell the Groups variable represent the field inside my Attribute
[AuthorizeAD(Groups = ConstantsADGroups.AdminGp)]
You are overriding a method that is later in the process than you think. You'll need to override one of these methods to implement your auth logic:
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//do custom work here
}
OR
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
//do custom work here
}
Is there a way to create a custom filter with an API controller to redirect to a MVC controller?
After looking around a bit his is what i have.
public class APIHasOneOfThesePermissions : ActionFilterAttribute
{
protected UserManager<ApplicationUser> UserManager { get; set; }
private SAMPortal.DAL.SAMPortalContext db = new DAL.SAMPortalContext();
public string[] Permissions { get; set; }
public APIHasOneOfThesePermissions(string[] Permissions)
{
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.db));
this.Permissions = Permissions;
}
public override void OnActionExecuting(HttpActionContext filterContext)
{
string userID = HttpContext.Current.User.Identity.GetUserId();
var CurrUser = db.Users.Include(u => u.Role.Permissions).Where(user => user.Id.Equals(userID)).FirstOrDefault();
bool hasPermission = false;
foreach (string x in Permissions)
{
if (hasPermission == false)
{
hasPermission = CurrUser.HasPermission(x);
}
}
if (hasPermission == false)
{
filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
base.OnActionExecuting(filterContext);
}
}
However when i execute the code it doesn't redirect them to the error page. Ideally i would like to redirect to a specify non-API controller is that possible?
I've created AuthorizeRedirectAttribute in one of my projects like this:
using System;
using System.Net;
using System.Web.Mvc;
namespace MyNamespace.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirectAttribute : AuthorizeAttribute
{
public string RedirectUrl = "~/Error/Forbidden403";
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
var httpContext = filterContext.RequestContext.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
// If AJAX request, just return appropriate code
if (request.IsAjaxRequest())
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
response.StatusCode = (int)HttpStatusCode.Forbidden;
else
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.SuppressFormsAuthenticationRedirect = true;
response.End();
}
// Otherwise check if authenticated, and if not redirect to specified url
if (httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.Redirect(RedirectUrl);
}
}
}
}
Then I've used it like this
[AuthorizeRedirect(Roles = "Administrator")]
public class MyController : Controller
{
}
In this case I've decorated whole controller with this attribute. It can also be applied to single controller function, if necessary. Basically what it does is, it checks whether logged on user is in role Administrator. If it is not, user is redirected to "~/Error/Forbidden403" action (returning simple view displaying user has not enough permissions). Hope it helps.
You could also implement checking your own permissions, as you did in your code.