custom authorize attribute not working - authorize-attribute

I am using asp.net mvc4 and facing problem while creating custom authorize attribute.
The problem i am facing is that it keep coming on this "OnAuthorization" function and not redirecting to appropriate area.
This is what i am trying to do:-
This is my custom authorize attribute:-
public class BusinessAuthorizeFilter:IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// if action or its controller has AllowAnonymousAttribute do nothing
if filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute),
true) ||filterContext.ActionDescriptor.ControllerDescriptor.IsDefined
(typeof(AllowAnonymousAttribute), true))
return;
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
RedirectToArea("Login", "Account", "");
return;
}
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (filterContext.HttpContext.User.IsInRole("Owner"))
route = new RouteValueDictionary{ {"action", "Index"},
{"controller", "HomeAdmin"},
{"area", "Admin"}
}
else if (filterContext.HttpContext.User.IsInRole("Agent"))
route = new RouteValueDictionary{ {"action", "Index"},
{"controller", "HomeAgent"},
{"area", "Agent"}
}
else
route = new RouteValueDictionary{ {"action", "Index"},
{"controller", "HomeSalesRep"},
{"area", "SalesRep"}
}
}
filterContext.Result = new RedirectToRouteResult(route);
}
Please let me know how to make it work.
Thanks in advance.

i got my code working with below thing(although have some question which i'll post as other question):-
public override void OnAuthorization(AuthorizationContext filterContext)
{
// if action or its controller has AllowAnonymousAttribute do nothing
if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
return;
bool isAuthorize = base.AuthorizeCore(filterContext.HttpContext);
if (!isAuthorize==true)
{
var result = new ViewResult();
result.ViewName = "../Error/Unauthorized";
filterContext.Result = result;
return;
}
}
Actually instead of redirecting user here, i simply check whether he's an authorized user or not.

Related

Redirect to a view Action Filter Attribute

How can I redirect logged in users (in every page) to a view asking them to complete their info?
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (userIsLoggedIn)
{
filterContext.Result = new RedirectResult(userInfoView);
}
base.OnActionExecuting(filterContext);
}
You can't redirect to a view, you need to redirect to an action method and that action method would return the view:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (userIsLoggedIn)
{
filterContext.Result = new RedirectToAction("UserInfoActionName", "UserInfoControllerName");
}
base.OnActionExecuting(filterContext);
}
This blog explains exactly the same problem.
RedirectToAction is avaible in MVC Core. I archived this as follows in MVC 4:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!condition)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary{ { "controller", "YourController" },
{ "action", "YourAction" }
});
}
}
This will redirect you to the given action then to the relevant view.

asp.net mvc redirect to login on session expiry

I am trying to do a simple redirect to login page if session expires in asp.net mvc 4.5. I am trying it as follows.
Global.asax
protected void Session_OnEnd(object sender, EventArgs e)
{
Response.RedirectToRoute("Default"); //default is route
}
but here null exception comes and response object is not found. I have seen this post but could not figure out as it is pretty broad.
UPDATE
I have tried applying filter as follows now it does not crash but also does not redirect to error page.
SessionTimeoutAttribute
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["SchoolCode"] == null)
{
filterContext.Result = new RedirectResult("~/Views/Shared/Error");
return;
}
base.OnActionExecuting(filterContext);
}
Added Attribute to Controller class
[SessionTimeout]
public class MapsController : Controller{}
Why doesnt it redirect?
I had to change my code a little
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["SchoolCode"] == null)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(
new { action = "Login", controller = "Account" }));
//return;
}
base.OnActionExecuting(filterContext);
}
ignore Session_OnEnd code its no use.

Redirect to external url from OnActionExecuting?

I need to redirect to an external url (let's say "www.google.com") from OnActionExecuting method. Right now I'm using something like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
var redirectUrl = "www.google.com";
try
{
var isAjaxRequest = filterContext.HttpContext.Request.IsAjaxRequest();
if (isAjaxRequest)
{
filterContext.HttpContext.Response.StatusCode = SessionController.CustomHttpRedirect;
filterContext.HttpContext.Response.StatusDescription = redirectUrl;
filterContext.Result = new JsonResult
{
Data = new { Redirect = redirectUrl },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
filterContext.Result = new RedirectResult(redirectUrl, true);
}
return;
}
else
{
throw new LoggedOutException();
}
}
catch
{
throw new LoggedOutException();
}
}
}
The problem is that it's not redirecting me to "www.google.com" but it's redirecting to "http://localhost:1234/www.google.com" (I try it locally).
There is any way to solve this ?
Thanks
The problem was verry easy to solve:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
var redirectUrl = "http://www.google.com";
try
{
var isAjaxRequest = filterContext.HttpContext.Request.IsAjaxRequest();
if (isAjaxRequest)
{
filterContext.HttpContext.Response.StatusCode = SessionController.CustomHttpRedirect;
filterContext.HttpContext.Response.StatusDescription = redirectUrl;
filterContext.Result = new JsonResult
{
Data = new { Redirect = redirectUrl },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
filterContext.Result = new RedirectResult(redirectUrl, true);
}
return;
}
else
{
throw new LoggedOutException();
}
}
catch
{
throw new LoggedOutException();
}
}
}
All I had to do was that when I assigned the value to "redirectUrl", I had tu put http before wwww. This mus be put if you use a SSL conenction and you're trying to redirect from mvc to another domain.
Instead of using:
filterContext.Result = new RedirectResult("www.google.com", true);
Try the following:
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "External" , ReturnURL = "www.google.com"}));
and in your (Home) controller create an action called (External) and from there redirect to your external url:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult External(string ReturnURL){
return Redirect(ReturnURL);
}
}
You can't directly perform a server side redirect from an ajax response. You could, however, return a JsonResult with the new url and perform the redirect with javascript. see this answer

How to display exceptions in the same View?

I search for a generic way to display thrown exceptions without redirecting to an error page but displaying it in the same view. I tried these below:
1) I firstly tried to handle them by adding a custom filter in global.asax and overriding public override void OnException(ExceptionContext filterContext) in my Attribute class but in that way, I couldn't fill filterContext.Result in the way I want since the old model of the view is not reachable so I could only redirect to an error page but that's not what I want.
2) Then I tried to catch the exceptions on my BaseController(All of my controllers inherits from it). I again override public override void OnException(ExceptionContext filterContext) in my controller and put exception details etc. in ViewBag and redirected the page to the same view by filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.Path ); but ViewBag contents are lost in the redirected page so I can't think any other way?
How can I achieve that? Code Sample that I wrote in my BaseController is below:
protected override void OnException(ExceptionContext filterContext) {
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
//filterContext.Result = new ViewResult
//{
// ViewName = actionName,
// ViewData = new ViewDataDictionary<??>(??),
// TempData = filterContext.Controller.TempData,
//};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
ModelState.AddModelError("Error", filterContext.Exception.Message);
ViewBag.das = "dasd";
filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.Path);
}
Maybe you could set a property in your BaseController class to have the name of the view that you want to use, setting that in whatever controller action handles the request. Then in OnException() you could have a method, that redirects to a controller action, that just returns a View that corresponds to the view name? Each controller action would have to set a default view name before it does anything else because only it knows what view it will call if any, and what view it likely was invoked by.
You'd need some sort of BaseController action that returns the new View.
The route(s) may or many not need configuration to have some sort of optional parameter(s) that you could set to be what error information you want to send to your view. For example, in the default route:
routes.MapRoute(RouteNames.Default,
"{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = "", errorInfo = UrlParameter.Optional}
BaseController:
protected ActionResult ErrorHandler()
{
ViewBag.das = (string)filterContext.RouteData.Values["errorInfo"];
return View(ViewName);
}
protected string ViewName { get; set; }
protected void GoToErrorView(ExceptionContext context, string exceptionData)
{
var actionName = "ErrorHandler";
var newVals = new RouteValueDictionary();
newVals.Add("errorInfo", exceptionData);
this.RedirectToAction(actionName, newVals);
}
In BaseController.OnException():
// ...
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
ModelState.AddModelError("Error", filterContext.Exception.Message);
// anything else you need to do to prepare what you want to display
string exceptionData = SomeSortOfDataYouWantToPassIntoTheView;
this.GoToErrorView(filterContext, exceptionData);
}
In the specific controllers that inherit from BaseController that are returning an ActionResult specifically a ViewResult:
[HttpGet]
public ActionResult Index()
{
ViewName = <set whatever view name you want to here>
// code here, including preparing the Model
// ...
var model = new MyViewModel();
model.SomethingIWantToGiveTheView = someDataThatItNeeds;
// ...
return View(<model name>, model);
}
I found the solution a while ago and add the solution so that it may help the others. I use TempData and _Layout to display errors:
public class ErrorHandlerAttribute : HandleErrorAttribute
{
private ILog _logger;
public ErrorHandlerAttribute()
{
_logger = Log4NetManager.GetLogger("MyLogger");
}
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
{
return;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = filterContext.Exception.Message
}
};
filterContext.HttpContext.Response.StatusCode = 500;
}
// log the error using log4net.
_logger.Error(filterContext.Exception.Message, filterContext.Exception);
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] != "XMLHttpRequest")
{
if (filterContext.Controller.TempData["AppError"] != null)
{
//If there is a loop it will break here.
filterContext.Controller.TempData["AppError"] = filterContext.Exception.Message;
filterContext.HttpContext.Response.Redirect("/");
}
else
{
int httpCode = new HttpException(null, filterContext.Exception).GetHttpCode();
switch (httpCode)
{
case 401:
filterContext.Controller.TempData["AppError"] = "Not Authorized";
filterContext.HttpContext.Response.Redirect("/");
break;
case 404:
filterContext.Controller.TempData["AppError"] = "Not Found";
filterContext.HttpContext.Response.Redirect("/");
break;
default:
filterContext.Controller.TempData["AppError"] = filterContext.Exception.Message;
//Redirect to the same page again(If error occurs again, it will break above)
filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.RawUrl);
break;
}
}
}
}
}
And in Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
var httpContext = ((MvcApplication)sender).Context;
var ex = Server.GetLastError();
httpContext.ClearError();
httpContext.Response.Clear();
httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
httpContext.Response.TrySkipIisCustomErrors = true;
var routeData = new RouteData();
routeData.Values["controller"] = "ControllerName";
routeData.Values["action"] = "ActionName";
routeData.Values["error"] = "404"; //Handle this url paramater in your action
((IController)new AccountController()).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}

How do I return a static page as a ViewResult in a global error filter (MVC4)?

I am trying to write an FilterAttribute, IExceptionFilter global error handling attribute which should serve up a custom error view. I override public void OnException(ExceptionContext context) and do
context.HttpContext.Response.Clear();
context.HttpContext.Response.TrySkipIisCustomErrors = true;
context.HttpContext.Response.StatusCode = 403;
context.ExceptionHandled = true;
context.Result = new ViewResult { ... not sure what to put here... }
which is where I am stuck.
Everything I have tried so far causes me to see internal server error pages. I suspect I want something like
context.Result = new ViewResult
{
ViewName = "~/Errors/ReadOnlyMode.cshtml",
}
However that didn't seem to work (internal server errors...).
This should work
context.Result = new ViewResult
{
ViewName = "~/Views/Errors/ReadOnlyMode.cshtml",
}
context.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "Controller", "Errors" },
{ "Action", "ReadOnlyMode" }
});
This works fine for me.

Resources