I need to implement a ActionFilterAttribute [POST] ActionResult() in the controller. The problem is that I try to “redirect” to a page if validation failed... But it does not work. Validation runs, but then returns to the ActionResult() next line and finally when the view is returned, only then “redirected” to the page listed in the validation. Ultimately what I need is to stop the ActionResult() statements and “redirect” to the page listed in the validation. I tried OnActionExecuting() and OnActionExecuted() but does not work any
I need to...
filterContext.HttpContext.Response.Redirect (loginUrl, true);
Run away, “redirecting” the page indicated
My code:
[HelperSomeValidations("")]
[HttpPost]
public ActionResult Create(Pais pais)
{
try
{
PaisBLL.saveNew(pais);
}
catch (Exception ex)
{
ViewBag.error = ex;
return View(“Error”);
}
return RedirectToAction(“Index”);
}
public class HelperSomeValidations : ActionFilterAttribute
{
public HelperSomeValidations(String permiso)
{
this.permiso = permiso;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.Session["coco"];
if (user == null) //validates if the user just login
{
//send them off to the login page
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content(“~/Usuario/Login”);
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
if (permission != “”)
{
//does some validations with “permission”
}
}
}
}
Thks!
I know this doesn't solve the problem you have posted but I feel it's a better solution. I would personally use an AuthoriseAttribute here instead as this is what it's designed todo.
public class Authorise : AuthorizeAttribute
{
private readonly string _permissionSystemName;
public Authorise()
{
}
public Authorise(string permissionSystemName)
{
_permissionSystemName = permissionSystemName;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//DO some logic and return True or False based on whether they are allowed or not.
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
area = filterContext.HttpContext.Request.RequestContext.RouteData.Values["area"],
controller = "Generic",
action = "PermissionDenied"
})
);
}
}
Usage would be along the lines of:
[Authorise("SomePermissionName")]
public class MyController : Controller
{
}
Instead of calling filterContext.HttpContext.Response.Redirect(loginUrl, true), you need to set the filterContext.Result to a RedirectResult.
Related
I would like to redirect the unauthorized user to a unauthorizedpage. Authorization is Roles based. For example [Authorize(Roles = "Admin")] I'm looking for a general solution. I don't want to write a redirect in every controllor/endpoint. I'm using Windows Auth with the Negotiate protocol.
I'm using the AuthorizeAttribute. When the user is unauthorized for a view then the application shows a blank HTML page to the user.
I tried multiple CustomAttributes, Configure AccesDeniedPaths, HandleUnauthorizedRequest. But every way ended in a blank HTML page.
I hope someone has an solution.
CustomAuthorizeAttribute
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
public string Roles { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//if user isn't logged in.
if (filterContext.HttpContext.User.Identity == null || !filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/Unauthorized/");
}
var user = filterContext.HttpContext.User;
//Check user rights here
if (!user.IsInRole(Roles))
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.Result = new RedirectResult("/Unauthorized/");
}
}
}
Controller
[CustomAuthorize(Roles = "Admin")]
[HttpGet]
public IActionResult Index()
{
return View();
}
ASP.NET has a defualt [Authorize] attribute which you can add to an arbitrary action.
Eg:
[HttpGet]
[Authorize]
public async Task<ActionResult<IEnumerable<Classes>>> GetClasses()
{
return await _context.Classes.ToListAsync();
}
This will by default redirect the user to the login page if an unauthorized user tries to access a forbidden page.
However, if you want to redirect to a custom page. You could try writing a custom filter attribute like this.
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity == null || !filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl + "?returnUrl=" +
filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
}
//Check user rights here
if (userNotRight)
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
and use it in a controller like this,
[HttpGet]
[CustomAuthorize]
public async Task<ActionResult<IEnumerable<Classes>>> GetClasses()
{
return await _context.Classes.ToListAsync();
}
If you would like to authorize based on roles for the custom filter attribute. You could either
1. write a custom authorize attribute and add it along with the filter attribute.
public class AuthorizeAdmin : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// The user is not authenticated
return false;
}
var user = httpContext.User;
if (user.IsInRole("Admin")) // Your desired role
{
return true;
}
}
}
controller:
[HttpGet]
[CustomAttribute]
[AuthorizeAdmin]
public async Task<ActionResult<IEnumerable<Classes>>> GetClasses()
{
return await _context.Classes.ToListAsync();
}
2. you could directly check it along with the custom filter attribute
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
// Check if is in "Admin" role
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// The user is not authenticated
return false;
}
var user = httpContext.User;
if (user.IsInRole("Admin")) // Your desired role
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity == null || !filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new
RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl
+ "?returnUrl=" +
filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
}
//Check user rights here
if (userNotRight)
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
}
controller:
[HttpGet]
[CustomAttribute]
public async Task<ActionResult<IEnumerable<Classes>>> GetClasses()
{
return await _context.Classes.ToListAsync();
}
I've tried creating an easy filter to see if the user is in a role called "System Administrator", basically short hand for having to do [Authorize(Roles = "System Administrator")]. I thought it would be fairly simple, but I'm also fairly new to MVC so perhaps I'm overlooking something.
Here's my code:
using System.Web.Mvc;
namespace site_redesign_web.Filters
{
public class SystemAdminFilter : ActionFilterAttribute
{
string SysAdminRole = "System Administrator";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.RequestContext.HttpContext.User != null)
{
var userSysAdmin = filterContext.RequestContext.HttpContext.User.IsInRole(SysAdminRole) == true;
filterContext.ActionParameters["IsSysAdmin"] = userSysAdmin;
}
}
}
}
Can some one suggest where am I going wrong? A huge plus would be if the person isn't a System Administrator it would direct them to Home/NoPermissions.
Thank you!
Updated: Fixing all issues.
AJ. Here you go...
Finally fixed the problem
using ActionFilterAttribute
using System.Web.Mvc;
namespace site_redesign_web.Filters
{
public class SystemAdminFilter : ActionFilterAttribute
{
string SysAdminRole = "System Administrator";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.RequestContext.HttpContext.User != null)
{
var userSysAdmin = filterContext.RequestContext.HttpContext.User.IsInRole(SysAdminRole) == true;
if(!userSysAdmin)
{
filterContext.Result = new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary{
{"controller", "Home"},
{"action", "Index"}
});
}
}
}
}
}
and your Controller should be
[SystemAdminFilter] // at controller level
public SomeController : Controller
{
}
or you can also use it for a particular Action by annotating like this
public SomeController : Controller
{
[SystemAdminFilter] // at Action level
public ActionResult SomeAction()
{
// perform your actions
}
It will work because I manually passed in the User with his role in the Application_AuthorizeRequest in Global.asax
protected void Application_AuthorizeRequest(Object sender, EventArgs e)
{
FormsAuthenticationTicket formsAuthenticationTicket = new FormsAuthenticationTicket("Aravind", true, 30);
FormsIdentity formsIdentityId = new FormsIdentity(formsAuthenticationTicket);
GenericPrincipal genericPrincipal = new GenericPrincipal(formsIdentityId, new string[] { "SystemUser" }); //TEST with this redirected to Home Index place
HttpContext.Current.User = genericPrincipal ;
}
The next test I made was with this
GenericPrincipal genericPrincipal = new GenericPrincipal(formsIdentityId, new string[] { "System Administrator" }); //TEST with this did not perform an action
Since you are dealing with authorization, I would extend AuthorizeAttribute instead of ActionFilterAttribute which is mode general. You need to override only one method - HandleUnauthorizedRequest which is executed when authorization fails. Default implementation of AuthorizeAttribute already handles role based authorization for you.
public class SystemAdminAttribute : AuthorizeAttribute
{
private const string SysAdminRole = "System Administrator";
public SystemAdminFilter()
{
//this defines the role that will be used to authorize the user:
this.Roles = SysAdminRole;
}
//if user is not authorized redirect to "Home/NoPermissions" page
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Home", action = "NoPermissions" }));
}
}
}
Once your attribute is implemented, decorate corresponding Controllers or Actions with it:
[SystemAdmin]
public SysAdminController : Controller
{
}
Hello I have a web api controller inside a mvc web site.
I'm trying to allow access to the controller using 2 rules:
User is admin or the request came from local computer;
I'm new to AuthorizationFilterAttribute but I tried to write one that limit access
to local request only:
public class WebApiLocalRequestAuthorizationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (actionContext.Request.IsLocal())
{
return;
}
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Content = new StringContent("Username and password are missings or invalid");
}
}
Then I decorated my controller with 2 attributes as
[Authorize(Roles = "Admin")]
[WebApiLocalRequestAuthorizationFilter]
public class ContactController : ApiController
{
public ContactModel Get(int id)
{
ContactsService contactsService = new ContactsService();
return contactsService.GetContactById(id).Map<ContactModel>();
}
}
But as I suspected , now, in order to access the controller I need to be admin and the request should be made from localhost. How can I do it?
Kind regards,
Tal Humy
One solution is to create a class that inherits from AuthorizeAttribute
e.g. something like this
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool accessAllowed = false;
bool isInGroup = false;
List<string> roleValues = Roles.Split(',').Select(rValue => rValue.Trim().ToUpper()).ToList();
foreach (string role in roleValues)
{
isInGroup = IdentityExtensions.UserHasRole(httpContext.User.Identity, role);
if (isInGroup)
{
accessAllowed = true;
break;
}
}
//add any other validation here
//if (actionContext.Request.IsLocal()) accessAllowed = true;
if (!accessAllowed)
{
//do some logging
}
return accessAllowed;
}
...
}
Then you can use it like so:
[MyAuthorizeAttribute(Roles = "Support,Admin")]
In the above code, IdentityExtensions checks for, and caches, ActiveDirectory roles which also allows us to fake the current user having roles by changing the cache.
I am trying to customize my Authorize attribute so that it redirects the user to appropriate page if he is not authorized.
This is my code till now:
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public string ErrorMessage { get; set; }
public string WebConfigKey { get; set; }
private const string UnauthorizedAccessMessage = "UnauthorizedAccessMessage";
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
HttpContext.Current.Session["foo"] = "bar";
base.HandleUnauthorizedRequest(filterContext);
if (string.IsNullOrEmpty(WebConfigKey))
throw new ArgumentNullException("WebConfigKey parameter is missing. WebConfigKey should give the actual page/url");
string configValue = ConfigurationManager.AppSettings[WebConfigKey];
if (string.IsNullOrEmpty(configValue))
throw new Exception(WebConfigKey + "'s value is null or empty");
if (!configValue.StartsWith("http"))
HttpContext.Current.Response.Redirect(WebUIUtils.GetSiteUrl() + configValue);
else
HttpContext.Current.Response.Redirect(configValue);
filterContext.Controller.TempData[UnauthorizedAccessMessage] = ErrorMessage;
HttpContext.Current.Session[UnauthorizedAccessMessage] = ErrorMessage;
}
}
Problem is whatever I store in Session or TempData in this method gets lost when the user arrives in some action method in controller after redirect is done from this method. I checked Session.Keys/TempData.Keys etc. But all values are lost. Probably something is happening in base.HandleUnauthorizedRequest(filterContext);. But I guess that calling to base is important.
Can anybody tell me the exact reason of this behavior and how do I prevent it from happening?
Form authorization and Session are distinct concepts for IIS. You can be authorized but your session can be not valid (for example try to restart the application pool).
Try with this custom attribute:
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result == null)
{
if (filterContext.HttpContext.Session != null )
{
//add checks for your configuration
//add session data
// if you have a url you can use RedirectResult
// in this example I use RedirectToRouteResult
RouteValueDictionary rd = new RouteValueDictionary();
rd.Add("controller", "Account");
rd.Add("action", "LogOn");
filterContext.Result = new RedirectToRouteResult("Default", rd);
}
}
}
}
I'm using an ActionFilterAttribute to do custom authentication logic. The Attribute will only be used on a derived Controller class that contains my authentication logic.
Here's my Controller, derived from my custom controller class, and a sample attribute:
public class MyController : CustomControllerBase
{
[CustomAuthorize(UserType = UserTypes.Admin)]
public ActionResult DoSomethingSecure()
{
return View();
}
}
Here's an example of my ActionFilterAttribute:
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
public MyUserTypes UserType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
myUser user = ((CustomControllerBase)filterContext.Controller).User;
if(!user.isAuthenticated)
{
filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
}
}
}
Works great.
Here's the question: Can I demand that this attribute ONLY be used on Actions in my custom controller type?
You can put the ActionFilter on the class itself. All actions in the class will realize the ActionFilter.
[CustomAuthorize]
public class AuthorizedControllerBase : CustomControllerBase
{
}
public class OpenAccessControllerBase : CustomControllerBase
{
}
public class MyRealController : AuthorizedControllerBase
{
// GET: /myrealcontroller/index
public ActionResult Index()
{
return View();
}
}
Based on the comments and the constraints of my system, I took a hybrid approach. Basically, if the request comes through via a cached route or the "User" is not set for any reason, authentication fails in the proper way.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private MyUser User { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
//Lazy loads the user in the controller.
User = ((MyControllerBase)filterContext.Controller).User;
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = false;
string retLink = httpContext.Request.Url.AbsolutePath;
if(User != null)
{
isAuthorized = User.IsValidated;
}
if (!isAuthorized)
{
//If the current request is coming in via an AJAX call,
//simply return a basic 401 status code, otherwise,
//redirect to the login page.
if (httpContext.Request.IsAjaxRequest())
{
httpContext.Response.StatusCode = 401;
}
else
{
httpContext.Response.Redirect("/login?retlink=" + retLink);
}
}
return isAuthorized;
}
}