Get the route when unauthenticated/logout with mvc? - asp.net

I have this piece of code:
public class Authenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect("/");
}
}
}
I was wondering if it is possible to make it redirect to the view for action="Login" controller="AdminLogin"? And how do I pass some message to the login view that tells "you need to login to access that" or similar?
/M

Here is how I solved the redirect-part:
public class Authenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
RedirectToRoute(filterContext,
new
{
controller = "AdminLogin",
action = "AdminLogin"
});
}
}
private void RedirectToRoute(ActionExecutingContext context, object routeValues)
{
var rc = new RequestContext(context.HttpContext, context.RouteData);
string url = RouteTable.Routes.GetVirtualPath(rc,
new RouteValueDictionary(routeValues)).VirtualPath;
context.HttpContext.Response.Redirect(url, true);
}
}
Not sure if it is optimal but seems to do the job correctly

Related

Variable in Web Api filter being ignored

I would like to understand why the variable IgnoreRequest is always false after setting it to true in my web api 2.2.
The filter:
public class RestrictToCandidatePlus : ActionFilterAttribute
{
public virtual bool IgnoreRequest { get; set; }
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool validAccType = 2 == 1; //original code hidden
if (!IgnoreRequest && !validAccType)
{
HandleUnauthorizedRequest(actionContext);
return;
}
base.OnActionExecuting(actionContext);
}
private void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse<String>(HttpStatusCode.Unauthorized, "Invalid account type");
}
}
And the controller:
[Filter1]
[Filter2]
[RestrictToCandidatePlus]
public class PlusCandidateController : ApiController
{
[RestrictToCandidatePlus(IgnoreRequest = true)]
[HttpPost]
public HttpResponseMessage SetInterest([FromBody] SetInterestModel model)
{
//some logic
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I have exactly the same logic with the filters on my MVC5 application and it works like charm. I understand that filters in MVC are not the same used in the Web Api, but I think that I should be able to set the IgnoreRequest variable to true.
As you can see, I cannot use OverrideActionFiltersAttribute, otherwise it will disable Filter1 and Filter2, which is not what I want.
IgnoreRequest is always false because the controller level attribute is being evaluated first and you are error handling out before can evaluate the action attribute..
Instead of using a bool to override or block your attribute evaluation you can override the ActionFilterAttribute by using OverrideActionFiltersAttribute.
This will override any attribute inheriting from ActionFilterAttribute defined at the controller level.
Here is an updated attribute:
public class RestrictToCandidatePlus : ActionFilterAttribute
{
// dont need this
//public virtual bool IgnoreRequest { get; set; }
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool validAccType = 2 == 1; //original code hidden
//if (!IgnoreRequest && !validAccType)
if(!validAccType)
{
HandleUnauthorizedRequest(actionContext);
return;
}
base.OnActionExecuting(actionContext);
}
private void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse<String>(HttpStatusCode.Unauthorized, "Invalid account type");
}
}
Updated controller:
[RestrictToCandidatePlus]
public class PlusCandidateController : ApiController
{
[OverrideActionFiltersAttribute]// <== new attribute here
[HttpPost]
public HttpResponseMessage SetInterest([FromBody] SetInterestModel model)
{
//some logic
return Request.CreateResponse(HttpStatusCode.OK);
}
}
You can create your own attribute class inheriting from OverrideActionFiltersAttribute if you want to give it your own name like [OverrideCandidatePlus].
There is a great blog post here that explains how this works.
Note:
I have tested this with WebApi 2.2
I finally found the issue. The problem was that the same filter was running twice, starting from the Action (HttpResponseMessage) then the Controller. So if I set the IgnoreRequest = true in the controller level I could see the value set to true.
But the main problem was that the filter was running twice, so to fix this I had to override the following property:
public class RestrictToCandidatePlus : ActionFilterAttribute
{
public virtual bool IgnoreRequest { get; set; }
public override bool AllowMultiple { get { return false; } } // <= HERE!
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool validAccType = 2 == 1; //original code hidden
if (!IgnoreRequest && !validAccType)
{
HandleUnauthorizedRequest(actionContext);
return;
}
base.OnActionExecuting(actionContext);
}
private void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse<String>(HttpStatusCode.Unauthorized, "Invalid account type");
}
}

Dependency injection inside a FilterAttribute in ASP.NET MVC 6

I'm struggling with ASP.NET MVC 6 (beta 4 release) trying to inject a service within a controller filter attribute of type AuthorizationFilterAttribute.
This is the service (it has another service injected)
public class UsersTableRepository
{
private readonly NeurosgarContext _dbContext;
public UsersTableRepository(NeurosgarContext DbContext)
{
_dbContext = DbContext;
}
public ICollection<User> AllUsers
{
get
{
return _dbContext.Users.ToList();
}
}
//other stuff...
}
This is the ConfigureServices method in Startup class for services enabling
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddSingleton<NeurosgarContext>(a => NeurosgarContextFactory.GetContext());
services.AddSingleton<UifTableRepository<Nazione>>();
services.AddSingleton<UsersTableRepository>();
}
A simple "dummy" controller with two filters defined on it. You can notice that I already done DI inside this controller by decorating the property with [FromServices]and it works.
[Route("[controller]")]
[BasicAuthenticationFilter(Order = 0)]
[BasicAuthorizationFilter("Admin", Order = 1)]
public class DummyController : Controller
{
[FromServices]
public UsersTableRepository UsersRepository { get; set; }
// GET: /<controller>/
[Route("[action]")]
public IActionResult Index()
{
return View();
}
}
Doing the same DI within BasicAuthenticationFilterdoes not work and at runtime UserRepository property is a null reference.
public class BasicAuthenticationFilterAttribute : AuthorizationFilterAttribute
{
[FromServices]
public UsersTableRepository UsersRepository { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!Authenticate(filterContext.HttpContext))
{
// 401 Response
var result = new HttpUnauthorizedResult();
// Add the header for Basic authentication require
filterContext.HttpContext.Response.Headers.Append("WWW-Authenticate", "Basic");
filterContext.Result = result;
//if (!HasAllowAnonymous(context))
//{
// base.Fail(context);
//}
}
}
// ...
}
Any idea about how solve this?
Refrain from injecting dependencies into your attributes as explained here. Make your attributes passive, or make your attribute a humble object as described here.
var dependencyScope = context.HttpContext.RequestServices;
var usersRepository = dependencyScope.GetService(typeof(UsersTableRepository)) as UsersTableRepository;
// usersRepository is now ready to be used
So your BasicAuthenticationFilter will look like this:
public class BasicAuthenticationFilterAttribute : AuthorizationFilterAttribute
{
public UsersTableRepository UsersRepository { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
var dependencyScope = context.HttpContext.RequestServices;
UsersRepository = dependencyScope.GetService(typeof(UsersTableRepository)) as UsersTableRepository;
if (!Authenticate(filterContext.HttpContext))
{
// 401 Response
var result = new HttpUnauthorizedResult();
// Add the header for Basic authentication require
filterContext.HttpContext.Response.Headers.Append("WWW-Authenticate", "Basic");
filterContext.Result = result;
//if (!HasAllowAnonymous(context))
//{
// base.Fail(context);
//}
}
}
// ...
}

How to use Exception filters in MVC 5

How i can implement Exception Filters in MVC5.
I want to throw the exception to NLog and redirect the page to a default error page which displays "Something is gone wrong"
I have a Filter class as follows
using System;
using System.Diagnostics;
using System.Security.Policy;
using System.Web.Mvc;
using System.Web.Mvc.Filters;
using System.Web.Routing;
using System.Web.UI.WebControls;
using Delivros.UI.Controllers;
using Delivros.UI.Areas.User.ViewModel;
using System.Web;
namespace Delivros.UI.Filters
{
public class CustomAuthenticationFilter : IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
Debug.WriteLine("OnAuthenticationChallenge : MyAuthenticationFilter");
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MyAuthorizationFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.Cookies[System.Configuration.ConfigurationManager.AppSettings[Convert.ToString(CookieField.cookieName)]] == null)
{
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserRegistration" } ,
{"Area","User"}
});
}
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomActionFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuted(filterContext);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserRegistration" } ,
{"Area","User"}
});
// ActionResult home = new HomeController().Index();
}
}
public class MyResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
}
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserLogOut" } ,
{"Area","User"}
});
}
}
}
But nothing is redirecting to the page...
You could derive your own HandleErrorAttribute
public class NLogExceptionHandlerAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
// log error to NLog
base.OnException(filterContext);
}
}
Then register it globally
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new NLogExceptionHandlerAttribute());
...
}
By default, the HandleErrorAttribute will display the Error view located in the ~/Views/Shared folder but if you wanted to display a specific view you can set the View property of the attribute.
I believe it should be this code:
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new {
action = "UserLogOut",
controller = "User",
area = "User"
}));
}
}
You may add an additional "if (!filterContext.ExceptionHandled)" statement before logging the values inside the result to make sure that the exception's unhandled for the moment.
Exception filters are run only when unhandled exception has been thrown inside an action method. As you asked, here is an example to redirect to another page upon exception:
public class MyExceptionAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if(!filterContext.ExceptionHandled)
{
filterContext.Result = new RedirectResult("~/Content/ErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
}
Now, to apply this filter to either controllers or individual actions, put [MyException] on them.
You may need to check the occurence of an specific Exception inside the if clause.
e.g.:
if(... && filterContext.Excaption is ArgumentOutOfRangeException)
To return a View as Exception Response:
filterContext.Result = new RedirectResult("/Home/ErrorAction");
other alternatives you might use to redirect are:
new RedirectToRouteResult{ ... }
new ViewResult{ ... }
Below worked for me. A few things to note 1) RedirectResult points to a controller action, not a view 2) you need to set filterContext.ExceptionHandled = true; or your exception view / page will not display.
public class ErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Log.Logger.Error(filterContext.Exception, "An Unhandled exeption occured durring the execution of a request");
filterContext.Result = new RedirectResult("~/MyControler/MyErrorAction");
filterContext.ExceptionHandled = true;
}
}
Ofcourse you will also need to register the Error attribute using the GlobalFilters object from your Global.asax as shown below.
protected void Application_Start()
{
GlobalFilters.Filters.Add(new ErrorAttribute());
...
}

How do I get my EntitySetController to be visible to my route?

I created an EntitySetController that looks like this:
public class OrdersController : EntitySetController<Order,Guid>
{
private readonly PizzaCompanyEntities _context = Factories.DataFactory.GetPizzaContext();
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
public override IQueryable<Order> Get()
{
return _context.Orders;
}
protected override Order GetEntityByKey(Guid key)
{
var result = _context.Orders.FirstOrDefault(o => o.Id == key);
if (result == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return result;
}
}
In an existing MVC 4 web application.
I configure the route as follows:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapODataRoute("PizzaApi", "odata", GetImplicitEdm());
}
private static IEdmModel GetImplicitEdm()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Order>("Orders");
builder.EntitySet<Pizza>("Pizzas");
builder.EntitySet<Pizzas_To_Orders>("PizzasToOrders");
builder.EntitySet<Size>("Sizes");
builder.EntitySet<Status>("Statuses");
builder.EntitySet<Pizzas_To_Toppings>("PizzasToToppings");
return builder.GetEdmModel();
}
}
And execute the configuration as follows:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
But when I execute my route at http://localhost:29064/odata/Orders I am getting a 404 and a message "The controller for path /odata/Orders was not found or does not implement IController.
I cannot figure out what I am missing to get the route registered and the controller running. I have done a similar application from scratch and have not had this trouble.
How do I get my OData route working?

Creating an action filter attribute that bypasses the actual execution of the action and returns a value for it

Can I create an ActionFilterAttribute that bypasses the actual execution of the action and returns a value for it?
Yes, it is possible. You can set the result of the filter context provided to you when overriding OnActionExecuting in ActionFilterAttribute.
using System.Web.Mvc;
public sealed class SampleFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting( ActionExecutingContext filterContext )
{
filterContext.Result = new RedirectResult( "http://google.com" );
}
}
In the source, you can see that setting the Result property of the filter context changes the flow.
From System.Web.Mvc.ControllerActionInvoker:
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation)
{
filter.OnActionExecuting(preContext);
if (preContext.Result != null)
{
return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */)
{
Result = preContext.Result
};
}
// other code ommitted
}
You can, like this:
1) Redirects to some action and then return some value:
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
/*If something happens*/
if (/*Condition*/)
{
/*You can use Redirect or RedirectToRoute*/
filterContext.HttpContext.Response.Redirect("Redirecto to somewhere");
}
base.OnActionExecuting(filterContext);
}
}
2) Write some value direcly into the request and Ends it sending to the client:
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
/*If something happens*/
if (/*Condition*/)
{
filterContext.HttpContext.Response.Write("some value here");
filterContext.HttpContext.Response.End();
}
base.OnActionExecuting(filterContext);
}
}

Resources