As described in the linked posts, I am not satisfied with the built in AuthorizeAttribute, because it always redirects to the declared login-page. I need different behaviour between 401-NotAuthenticated (I know it is called Unauthorized) and 403-Forbidden. Forbidden shouldn't link to the login-page.
As in this or this solutions suggested, I implemented a custom attribute called AuthenticateAndAuthorizeAttribute inheriting from AuthorizeAttribute.
public class AuthenticateAndAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext) {
if (filterContext.HttpContext.Request.IsAuthenticated) {
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
All in all it does what it is supposed to do. But, because only the HttpStatusCode of the response is changed, I get an empty page. I know it is because there is no custom error page defined in the web.config. Instead we use the solution with "overriding" the method protected void Application_Error(object sender, EventArgs e) in the global.asax. This enables us, to create different error pages for default requests and ajax requests.
Unfortunatly this does not work when just setting the status code of the response. So I implemented the attribute, with throw HttpExceptions.
public class AuthenticateAndAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
if (filterContext.HttpContext.Request.IsAuthenticated) {
throw new HttpException((int)System.Net.HttpStatusCode.Forbidden, Forbidden!");
} else {
throw new HttpException((int)System.Net.HttpStatusCode.Unauthorized, "Not Authenticated!");
}
}
}
Now I wonder, if this is a common/correct way to deal with authentication and authorization or if I break some built-in (security)features of asp.net? Could someone see any problems with this solution?
Related
I am creating my own custom HandleError attribute on MVC.
public class MVCError : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//Supposed to remove session here
}
}
But it seems that I cannot use the Session to remove specific session on my website. Is this possible? Or do I need to clear my session on Global.asax file:
protected void Application_Error()
{
Session.Remove("Check");
Debug.WriteLine("An error has occurred.");
}
You can clear use HttpContext.Current object HttpContext.Current.Session.Remove("Check");
public class MVCError : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//Supposed to remove session here
HttpContext.Current.Session.Remove("Check");
}
}
I am troubleshooting a ASP.NET MVC application and on one server the OnActionExecuting is not firing. It has been a long time since I looked at filters. What can keep the OnActionExecuting from running? The effect in our application is the user context never really gets set up (Initialize)... so everything redirects the user back to the login page.
Here is the code of the filter. Note "Jupiter" was the codename of the project
public class JupiterAuthenticationFilter : IActionFilter
{
private readonly IJupiterContext _jupiterContext;
public JupiterAuthenticationFilter(IJupiterContext jupiterContext)
{
if (jupiterContext == null)
{
throw new ArgumentNullException("jupiterContext");
}
_jupiterContext = jupiterContext;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_jupiterContext.Initialize();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
It can happen when your Controller has System.Web.MVC implementation, but ActionFilter has System.Web.Http.
For some reason, only the method OnAuthorization is being invoked, but AuthorizeCore not.
this is how I call it:
[AuthorizeWithRoles(Roles = "Affiliate")]
public string TestOnlyAffiliate()
{
return "ok";
}
this is the actual attribute.
public class AuthorizeWithRolesAttribute : AuthorizeAttribute
{
public string Roles { get; set; }
//
//AuthorizeCore - NOT INVOKING!
//
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
}
}
You're not supposed to override OnAuthorization. It deals with potential caching issues and calls AuthorizeCore.
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1acb241299a8#src/System.Web.Mvc/AuthorizeAttribute.cs
// 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.
Put your custom logic in AuthorizationCore.
Not sure if this helps you at all, but I ran into this same thing and determined that, at least for my purposes, I didn't need to override AuthorizeCore at all. I'm not sure why it's there, to be honest. As MSDN says, OnAuthorization is invoked "when a process requests authorization." This means that it will be invoked for any method that has your AuthorizeWithRoles attribute. You can put your custom code within OnAuthorization to check whether or not the user has permission, and since you can get the httpContext from filterContext, there's really no need for AuthorizeCore. Here's a simple example that works for me:
public class LoginRequired : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Common.ValidateCurrentSession(filterContext.HttpContext))
{
//this is valid; keep going
return;
}
else
{
//this is not valid; redirect
filterContext.Result = new RedirectResult("/login");
}
}
}
I hope that helps. Besides that, obviously you'll need to declare that OnAuthorization is an override.
EDIT: I believe the base OnAuthorization method is what calls into AuthorizeCore. Since you're overriding OnAuthorization, obviously that call is lost. I believe overriding AuthorizeCore would only be relevant if you left OnAuthorization alone or if you called base.OnAuthorization(filterContext) within the overridden method.
In my asp.net mvc3 application, I have a custom Authorization Attribute as seen below.
public class CustomAuthorize : AuthorizeAttribute
{
public IAccountRepository AccountRepository { get; set; }
public CustomAuthorize()
{
this.AccountRepository = new UserModel();
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
base.AuthorizeCore(httpContext);
return AccountRepository.isEnabled(HttpContext.Current.User.Identity.Name);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
}
}
I have the [CustomAuthorize] tag on my controller actions, and the AuthorizeCore method works fine - it performs the logic I want it to (making sure the account is actually enabled), and then returning as such.
However, the overridden HandleUnauthorizedRequest method, which as I understand it should allow me to control the behaviour of an unauthorized request, is not running at all. I put a breakpoint there, I put code in there, I access my application unauthorized, and the code never runs.
What am I missing?
EDIT: I did some more research and found a few other people who had this problem, but no solution unfortunately.
EDIT2: Sample code
[CustomAuthorize]
public class UserController: Controller
{
public UserController()
{
//do stuff here
}
}
EDIT 3: #Fabio
Here's what I'm trying to do. I have a login page (forms auth) that works fine - it calls my custom login, and then calls my AuthorizeCore override. My application uses a large amount of ajax calls, and my eventual goal is for whenever a user is using the application, and the administrator disables them, making an ajax call after being disabled (though still being logged in) should log them out. However, in order to do this, i want to return a custom response if the user is making an ajax call, and for that, I need to ovverride HandleUnauthorizedRequest. But my Authorize Core (and by extension HandleUnauthorizedRequest) are being ignored if the user is logged in (despite the fact that I have customauthorize tags on all of my controller actions that the ajax is calling).
In short: I want to authorize the user on every request, not just the login request (which seems to be what the membership provider is doing right now)
I ended up changing my approach a fair bit. I implemented individual permissions checking, and then that caused AuthorizeCore to be called every time (and not be cached, which I guess was what was happening before).
Interestingly enough, putting a breakpoint on the HandleUnauthorizedRequest override still doesn't break, but putting it inside the method will. Strange, and threw me off for a bit, but I've solved it now.
Code if anyone is interested:
public class CustomAuthorize : AuthorizeAttribute
{
public string Permissions { get; set; }
private IAccountRepository AccountRepository { get; set; }
private string[] permArray { get; set; }
private string reqStatus { get; set; }
public CustomAuthorize()
{
this.AccountRepository = new UserModel();
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
base.AuthorizeCore(httpContext);
if (Permissions != null) {
permArray = Permissions.Trim().Split(' ');
if (AccountRepository.isEnabled(httpContext.User.Identity.Name)) {
this.reqStatus = "permission";
return AccountRepository.hasPermissions(permArray);
} else {
return false;
}
} else {
return AccountRepository.isEnabled(httpContext.User.Identity.Name);
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (this.reqStatus == "permission") {
filterContext.Result = new RedirectResult(MvcApplication.eM.cause("no_permission", "redirect"));
} else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
And then I decorated the controller with this:
[CustomAuthorize(Permissions="test_perm")]
This may be a stupid answer/question but is AccountRepository.isEnabled method returning false so that the HandleUnauthorizedRequest can be executed?
If it's returning true, then the HandleUnauthorizedRequest method won't be executed.
I'm creating an attribute so that whenever an exception occurs on my site, I'll receive an email detailing the exception. I've got so far but my Attribute code doesn't seem to fire if an exception occurs:
public class ReportingAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// This will generate an email to me
ErrorReporting.GenerateEmail(filterContext.Exception);
}
}
Then above my Controller I'm doing:
[ReportingAttribute]
public class AccountController : Controller
The other way to do it is ofcourse putting ErrorReporting.GenerateEmail(ex) inside my catch blocks? There must be a simpler way? Thats why I thought of creating the Attribute to handle this
For the purpose of logging all uncaught exceptions, you can define the following method in your Global.asax.cs file:
private void Application_Error(object sender, EventArgs e)
{
try
{
//
// Try to be as "defensive" as possible, to ensure gathering of max. amount of info.
//
HttpApplication app = (HttpApplication) sender;
if(null != app.Context)
{
HttpContext context = app.Context;
if(null != context.AllErrors)
{
foreach(Exception ex in context.AllErrors)
{
// Log the **ex** or send it via mail.
}
}
context.ClearError();
context.Server.Transfer("~/YourErrorPage");
}
}
catch
{
HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
Attribute just by itself can not define a behaviour, but its used for make some marks over your code data. You should write a code, where you
get an exception
check for given attribute presence in the method that raised an exception
if it is present, collect and send the data you need.
Why not create an base controller:
public ApplicationBaseController : Controller
{
public override void OnException(ExceptionContext context)
{
//Send your e-mail
}
}
And derive your controller from ApplicationBaseController
public HomeController : ApplicationBaseController
{
//.....
}