I have a implemented a custom AuthorizeAtrribute class
public class AdminAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
}
}
Action method
[AdminAuthorize(Roles = "Admin")]
public ViewResult AdminOnly()
{
return View();
}
[AdminAuthorize(Roles = "Admin, Mod")]
public ViewResult Index()
{
return View();
}
When I have a user that IsAuthenticated but not in the Admin role I would like to redirect them to the Index page and not the logon page.
I've read the many other SO posts on this but my HandleUnauthorizedRequest() method is not firing.
this the code i always use when i work with active directory
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (base.AuthorizeCore(httpContext))
{
if (String.IsNullOrEmpty(Groups)) { return true; }
var groups = Groups.Split(',').ToList();
var context = new PrincipalContext(ContextType.Domain,"yourDomain");
var userPrincipal = UserPrincipal.FindByIdentity(context,IdentityType.SamAccountName,httpContext.User.Identity.Name);
foreach(var group in groups){ // this will check user if the right role in active directory
if(userPrincipal.IsMemberOf(context, IdentityType.Name, group)){
return true;
}
}
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var result = new ViewResult();
result.ViewName = "NotAuthorized";
result.MasterName = "_Layout";
filterContext.Result = result;
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
forgot to tell the Groups variable represent the field inside my Attribute
[AuthorizeAD(Groups = ConstantsADGroups.AdminGp)]
You are overriding a method that is later in the process than you think. You'll need to override one of these methods to implement your auth logic:
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//do custom work here
}
OR
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
//do custom work here
}
Related
My Question:
When user doesn't have Manager Role and Admin Role, I have to redirect to error page/some popup message. But when I checked if authorize "false" continuously windows security password windows its showing. When I entered user name and password again its showing windows security password.
Every action method I have to check and I need to show the message or error page. how to solve this issues?
Controller Code:
[AuthorizeUser("Manager","Admin")]
public ActionResult Contact()
{
return View();
}
C# Code:
public AuthorizeUserAttribute(params int[] roles)
{
allowedroles = roles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorize = false;
var getList = _objService.GetUserRoleDetail(CommonStaticHelper.getLoggedUser());
foreach (var role in allowedroles)
{
if (getList.Exists(m => m.RoleId == role))
{
return authorize = true; /* return true if Entity has current user(active) with specific role */
}
}
return authorize;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
/// Try this :
///Create an action :
public ActionResult Unauthorized()
{
return View();
}
//// now write below code for authorization
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
//redirect to the Unauthenticated page
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "Unauthorized"
}));
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// The user is not authenticated
return false;
}
else{
var getList =
_objService.GetUserRoleDetail(CommonStaticHelper.getLoggedUser());
foreach (var role in allowedroles)
{
if (getList.Exists(m => m.RoleId == role))
{
return authorize = true; /* return true if Entity has current
user(active) with specific role */
}
}
return authorize = false;
}
create your own Filter something like
public class AuthorityAttribute : AuthorizeAttribute
{
private readonly string[] allowedroles;
public AuthorityAttribute(params string[] roles)
{
this.allowedroles = roles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
foreach (var role in allowedroles)
{
if (PortalWebSessionManager.ActivePortalSettings.ActiveRoles != null)
{
foreach (IDynamics.IDynamicsPortal.DataComponent.Roles currentRole in PortalWebSessionManager.ActivePortalSettings.ActiveRoles)
{
if (currentRole.RoleName == role)
{
return true;
}
}
}
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
and call that filter
I'm trying to create a custom Authorize attribute to do the following:
If the user has a role of "Regular user" - he is redirected to /index/subscribe
All other users(Administrator,Subscriber) gets access to /Search/Index
This is when the user tries to open up the Search controller. I made the custom Authorize attribute like this:
public class DenyRegularUser : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("~/User/Logon");
return;
}
if (filterContext.HttpContext.User.IsInRole("Regular user"))
{
filterContext.Result = new RedirectResult("~/Index/Subscribe");
}
}
}
And this is my Search controller:
namespace WebApplication2.Controllers
{
[DenyRegularUser(Roles ="Regular user")]
public class SearchController : Controller
{
// GET: Search
public ActionResult Index()
{
return View();
}
}
}
But for some reason, even when I update the user's role from Regular user to Administrator or Subscriber, I get redirected to login page: /user/login...
This shouldn't happen as the login functionality works perfectly and I get the role of the user...
What am I missing out here??
This may help.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class DenyRegularUser : AuthorizeAttribute
{
public DenyRegularUser() :
base()
{
}
protected override bool IsAuthorized (System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (AuthorizeRequest(actionContext))
{
return true;
}
return false;
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Code to handle unauthorized request
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.TemporaryRedirect);
actionContext.Response.Headers.Add("Location", "~/Index/Subscribe");
}
private bool AuthorizeRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Write your code here to perform authorization
}
}
I believe the IsAuthorized method is the correct way to override the AuthorizeAttribute.
In an attempt to implement security in my web app, I created an attribute that derives from AuthorizeAttribute.
public class FunctionalityAttribute : AuthorizeAttribute
{
public string FunctionalityName { get; set; }
protected override bool IsAuthorized(HttpActionContext actionContext)
{
string adGroup = WebConfigurationManager.AppSettings[FunctionalityName];
if (actionContext.RequestContext.Principal.IsInRole(adGroup)) { return true; }
return false; // This causes a login dialog to appear. I don't want that.
}
}
And here is how it's used in my Web API method:
[Functionality(FunctionalityName = "GetApps")]
public IEnumerable<ApplicationDtoSlim> Get()
{
using (var prestoWcf = new PrestoWcf<IApplicationService>())
{
return prestoWcf.Service.GetAllApplicationsSlim().OrderBy(x => x.Name);
}
}
It actually works. But the issue is what happens when I'm not authorized:
I don't want that dialog to come up. I'm already signed in. I want to let the user know that they're not authorized. How do I make it so that login dialog doesn't come up?
You need to also override HandleUnauthorizedRequest
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
System.Web.Routing.RouteValueDictionary rd = null;
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//Redirect to Not Authorized
rd = new System.Web.Routing.RouteValueDictionary(new { action = "NotAuthorized", controller = "Error", area = "" });
}
else
{
//Redirect to Login
rd = new System.Web.Routing.RouteValueDictionary(new { action = "Login", controller = "Account", area = "" });
//See if we need to include a ReturnUrl
if (!string.IsNullOrEmpty(filterContext.HttpContext.Request.RawUrl) && filterContext.HttpContext.Request.RawUrl != "/")
rd.Add("ReturnUrl", filterContext.HttpContext.Request.RawUrl);
}
//Set context result
filterContext.Result = new RedirectToRouteResult(rd);
}
In HandleUnauthorizedRequest, use HttpStatusCode Forbidden because Unauthorized causes a login prompt to display. Here is the entire attribute class.
public class FunctionalityAttribute : AuthorizeAttribute
{
public string FunctionalityName { get; set; }
protected override bool IsAuthorized(HttpActionContext actionContext)
{
string adGroup = WebConfigurationManager.AppSettings[FunctionalityName];
if (actionContext.RequestContext.Principal.IsInRole(adGroup)) { return true; }
return false;
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
// Authenticated, but not authorized.
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
// Use Forbidden because Unauthorized causes a login prompt to display.
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
And this is how I'm handling it in my angular repository:
$http.get('/PrestoWeb/api/apps/')
.then(function (result) {
// do success stuff
}, function (response) {
console.log(response);
if (response.status == 403) {
$rootScope.setUserMessage("Unauthorized");
callbackFunction(null);
}
});
Is there a way to create a custom filter with an API controller to redirect to a MVC controller?
After looking around a bit his is what i have.
public class APIHasOneOfThesePermissions : ActionFilterAttribute
{
protected UserManager<ApplicationUser> UserManager { get; set; }
private SAMPortal.DAL.SAMPortalContext db = new DAL.SAMPortalContext();
public string[] Permissions { get; set; }
public APIHasOneOfThesePermissions(string[] Permissions)
{
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.db));
this.Permissions = Permissions;
}
public override void OnActionExecuting(HttpActionContext filterContext)
{
string userID = HttpContext.Current.User.Identity.GetUserId();
var CurrUser = db.Users.Include(u => u.Role.Permissions).Where(user => user.Id.Equals(userID)).FirstOrDefault();
bool hasPermission = false;
foreach (string x in Permissions)
{
if (hasPermission == false)
{
hasPermission = CurrUser.HasPermission(x);
}
}
if (hasPermission == false)
{
filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
base.OnActionExecuting(filterContext);
}
}
However when i execute the code it doesn't redirect them to the error page. Ideally i would like to redirect to a specify non-API controller is that possible?
I've created AuthorizeRedirectAttribute in one of my projects like this:
using System;
using System.Net;
using System.Web.Mvc;
namespace MyNamespace.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirectAttribute : AuthorizeAttribute
{
public string RedirectUrl = "~/Error/Forbidden403";
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
var httpContext = filterContext.RequestContext.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
// If AJAX request, just return appropriate code
if (request.IsAjaxRequest())
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
response.StatusCode = (int)HttpStatusCode.Forbidden;
else
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.SuppressFormsAuthenticationRedirect = true;
response.End();
}
// Otherwise check if authenticated, and if not redirect to specified url
if (httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.Redirect(RedirectUrl);
}
}
}
}
Then I've used it like this
[AuthorizeRedirect(Roles = "Administrator")]
public class MyController : Controller
{
}
In this case I've decorated whole controller with this attribute. It can also be applied to single controller function, if necessary. Basically what it does is, it checks whether logged on user is in role Administrator. If it is not, user is redirected to "~/Error/Forbidden403" action (returning simple view displaying user has not enough permissions). Hope it helps.
You could also implement checking your own permissions, as you did in your code.
I have created the filter below to restrict access to sections of my application to only users that are logged in. However, my app still complains that the user objects have not been instantiated before the filter fires the redirect. How can I make the redirect kick the user out before the action method has a chance to notice the objects are null?
Context
For completeness, it is worth mentioning:
UserSession.CurrentOrDefault();
Returns an object if it finds values stored in the current session, or null if the session doesn't exist.
The filter
public class RestrictAccess : ActionFilterAttribute
{
public UserRole RequiredRole { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
base.OnActionExecuting(filterContext);
return;
}
else
{
HttpContext.Current.Response.Redirect("/");
return;
}
}
HttpContext.Current.Response.Redirect("/Session/Create");
}
}
An example action method that complains:
[RestrictAccess]
public ActionResult Index()
{
var userSession = UserSession.CurrentOrDefault();
// This is the part that throws the exception. userSession.User is null here.
// My expectation was for this to be unreachable if user is null because of the filter.
var model = new IndexViewModel { User = userSession.User };
return View(model);
}
You should implement your own AuthorizeAttribute for that
public class Authorization : AuthorizeAttribute
{
public UserRole RequiredRole { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
return true;
}
else
{
return false;
}
}
return false;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
try
{
if (AuthorizeCore(filterContext.HttpContext))
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. 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. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else
{
filterContext.Result = new RedirectResult("/Session/Create");
}
}
catch (Exception)
{
filterContext.Result = new RedirectResult("/Session/Create");
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
When you call HttpContext.Current.Response.Redirect, you are going outside of the MVC architecture, so it does not know not to run the action method and so continues and errors.
Instead you should set the Result of your ActionExecutingContext filterContext to a RedirectResult like so:
public class RestrictAccess : ActionFilterAttribute
{
public UserRole RequiredRole { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
base.OnActionExecuting(filterContext);
return;
}
else
{
filterContext.Result = new RedirectResult("/");
return;
}
}
filterContext.Result = new RedirectResult("/Session/Create");
}
}