The HttpForbiddenHandler Class is sealed however I'd like to create a class that behaves like it. Something like this:
public class ForbiddenHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// do the 403 here somehow
}
public bool IsReusable
{
get { return true; }
}
}
How would I cause that 403 redirect?
If you simply want to send the 403 status code:
context.Response.Status = "403 Forbidden";
Also, you might want to write some message to the client:
context.Response.Write("This is very much forbidden!");
If you wish to redirect the user to the 403 custom error page, configured in your web.config or machine.config, you should be able to do it like this:
throw new HttpException(403, "Forbidden");
Related
I have created the web api with simple autorization via autorization requirments. My requirments code looks like:
public class TestRequirement : IAuthorizationRequirement { }
public class TestHandler : AuthorizationHandler<TestRequirement> {
protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context, TestRequirement requirement) {
//context.Succeed(requirement); --#1
//context.Fail(); --#2
/*if (context.Resource is AuthorizationFilterContext mvcContext) {--#3
mvcContext.Result = new UnauthorizedResult();
}*/
return Task.CompletedTask;
}
}
Also, I updated Startup.ConfigureServices(...):
services.AddAuthorization(o => o.AddPolicy("Test", p => p.Requirements.Add(new TestRequirement())));
services.AddSingleton<IAuthorizationHandler, TestHandler>();
And I added apropriate attribute to controller: [Authorize(Policy = "Test")]
If I uncomment block #1 - it works as expected (I get my data). But when my code fails requiremnt (I comment #1), I get 500 Internal Server Error.
Then, I tried to explicit fail the requirment (uncomment block #2) - same result. I know it isn't recommended but I wanted to try.
After this, I tried the more ugly workaround, I commented #2 and uncommented block #3. I got same 500 status code.
Just for fun, I implemented resources filter with the same behavior:
public class TestResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context) {
context.Result = new UnauthorizedResult();
}
public void OnResourceExecuted(ResourceExecutedContext context) {
}
}
Then, I replaced on controller my authorize attribute with [TestResourceFilter] and got 401 Unauthorized as expected. But it the bad way to use resource filters.
What is wrong with my requirement implementation? And why I get 500 instead of 401 (or 403)?
EDIT: I found InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. in my log.
I saw samples with cookies scheme, but it isn't suitable for me. Because I want to implement stateless calls.
poke's commnets pointed me that I implemented my functionality in wrong way. I tried to handle the security checking on authorization level, but I had to do it on authentication level. So my final code looks like:
public class TestHandlerOptions : AuthenticationSchemeOptions { }
internal class TestHandler : AuthenticationHandler<TestHandlerOptions> {
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
if (await SomeCheckAsync()) {
var identity = new ClaimsIdentity(ClaimsName);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), null, ClaimsName);
return AuthenticateResult.Success(ticket);
}
return AuthenticateResult.Fail("Missing or malformed 'Authorization' header.");
}
}
Add next in ConfigureServices in Startup class:
services.AddAuthentication(options => options.AddScheme(SchemeName, o => o.HandlerType = typeof(TestHandler)));
And authorize attribute looks like [Authorize(AuthenticationSchemes = SchemeName)]
I'm working on a project using ASP.NET 5 and I'm writing a web api.
I've inherited some code and database stored procedures that use raiserror to indicate something is wrong (username/password incorrect, expired license etc).
The stored procedure returns nothing to uniquely identify that error, except the message text.
I want to be able to return a HTTP UNAUTHORIZED response, but also shuttle the error message along to the client too.
The built in IActionResult HttpUnauthorized() method doesn't allow for a reason to be given.
So I wrote my own ActionResult that looks like this:
public class UnauthorizedWithMessageResult : IActionResult
{
private readonly string _message;
public UnauthorizedWithMessageResult(string message)
{
_message = message;
}
public async Task ExecuteResultAsync(ActionContext context)
{
using (var sw = new HttpResponseStreamWriter(context.HttpContext.Response.Body, Encoding.UTF8))
{
await sw.WriteLineAsync(_message);
}
await new HttpUnauthorizedResult().ExecuteResultAsync(context);
}
}
The problem is that the client is receiving a 200-OK like everything is fine.
I've stepped through this and after the delegation to HttpUnauthorizedResult has been done, the status code is indeed set to 403.
It looks like Web API is (at some point) seeing that there's content in the body of the response and decides that means everything is OK and resets the status code.
Is there any way to get around this without having to resort to sending the message as a header or something? (or is that the correct way to do this?)
You can do this:
return new ObjectResult("The message") {
StatusCode = (int?) HttpStatusCode.Unauthorized
};
It works this way:
public IActionResult IWillNotBeCalled()
{
return new UnauthorizedWithMessageResult("MY SQL ERROR");
}
public class UnauthorizedWithMessageResult : IActionResult
{
private readonly string _message;
public UnauthorizedWithMessageResult(string message)
{
_message = message;
}
public async Task ExecuteResultAsync(ActionContext context)
{
// you need to do this before setting the body content
context.HttpContext.Response.StatusCode = 403;
var myByteArray = Encoding.UTF8.GetBytes(_message);
await context.HttpContext.Response.Body.WriteAsync(myByteArray, 0, myByteArray.Length);
await context.HttpContext.Response.Body.FlushAsync();
}
}
You have to set the StatusCode before setting the body and you have to flush the body stream to be sure that the content will be set inside the response.
Hope it helps :)
You can return any status code you want, like so:
return new HttpStatusCodeResult(403);
I have a simple controller :
public class UsersController : ApiController
{
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Login([FromBody] UserLogin userLogin)
{
var userId = UserCleaner.Login(userLogin.MasterEntity, userLogin.UserName, userLogin.Password, userLogin.Ua);
if (userId == null) return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User not authorized");
return Request.CreateResponse(HttpStatusCode.OK, Functions.RequestSet(userId));
}
}
As you can see , only POST is currently available .
But when I invoke a GET in a browser (just for checking):
http://royipc.com:88/api/users
I get :
{"Message":"The requested resource does not support http method
'GET'."}
It is clear to me why it happens. But I want to return a custom exception when it happens.
Other answers here at SO doesn't show how I can treat this kind of situation (not that i've found of, anyway)
Question
How (and where) should I catch this kind of situation and return custom exception (HttpResponseMessage) ?
NB
I don't want to add a dummy GET method just for "catch and throw". tomorrow there can be a GET method. I just want to catch this Exception and return my OWN !
You may need to inherit from ApiControllerActionSelector class which is what the Web API uses to select the required action.
then you can replace the default IHttpActionSelector by your new action selector like that. config.Services.Replace(typeof(IHttpActionSelector), new MyActionSelector());
check this url for full example: http://www.strathweb.com/2013/01/magical-web-api-action-selector-http-verb-and-action-name-dispatching-in-a-single-controller/
You can build custom Exception filters in ASP.Net WebAPI. An exception filter is a class that implements the IExceptionFilter interface. To create a custom exception filter you can either implement the IExceptionFilter interface yourself or create a class that inherits from the inbuilt ExceptionFilterAttribute class. In the later approach all you need to do is override the OnException() method and plug-in some custom implementation.
public class MyExceptionFilter:ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An unhandled exception was thrown by the Web API controller."),
ReasonPhrase = "An unhandled exception was thrown by the Web API controller."
};
context.Response = msg;
}
}
you would likely want to test for conditions and generate the exact exception, but this is a bare example.
To use the exception class, you can either register it in the Global.asax, or as an attribute on a specific class or method.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new WebAPIExceptionsDemo.MyExceptionFilter());
AreaRegistration.RegisterAllAreas();
...
}
}
or
[MyExceptionFilter]
public class UsersController : ApiController
{
...
}
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
{
//.....
}