Authorize Attribute not filtering on DbDataController - asp.net-mvc-4

I'm trying to use the new MVC4 DbDataController to expose a restful data api.
My problem is trying to secure this. I have created custom authorization attributes that derive from Authorize Attribute
public class AdminOnlyAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!IsAllowed()) {
filterContext.Result = new HttpUnauthorizedResult("Not logged in");
}
...
}
And that works fine when applied to my normal controller actions. I'm trying to use the same thing in my data service like this:
[AdminOnlyAttribute]
public class DataServiceController : DbDataController<AppBuilderDataContext>
{
[AdminOnlyAttribute]
public IQueryable<Thing> GetThings()
{
return DbContext.AllMyThings();
}
}
You can see I've tried my attribute on both the controller and the action, but it's not firing for either one. I've set a breakpoint inside my authorize attribute function, and it's not getting called.
I'm pretty sure Scott Guthrie said this was going to work. Am I doing it wrong, or do I need a completely different method to secure these?

To work with an DataController or any other type derived from ApiController your attribute must derive from System.Web.Http.AuthorizeAttribute

Related

Injecting IOptions<> into ApiKeyAuthorizeAttribute

I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.
However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.
Here is how my controller looks like now:
[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
//some endpoints here
}
ApiKeyAuthorize Class:
public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
//////This...
private readonly IOptions<MyConfig> _config;
public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
{
_config = config;
}
/////////...is what I am trying to accomplish
public void OnAuthorization(AuthorizationFilterContext context)
{
var request = context.HttpContext.Request;
var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);
if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
{
context.Result = new UnauthorizedResult();
}
}
}
My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.
Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:
[ServiceFilter(typeof(ApiAuthorizeAttribute))]
This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:
[ApiAuthorizeAttribute(Roles = "Admin")]
It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.

MVC 5 Custom Authentication

I have a legacy Application that we're converting to use the MVC 5 Application template. We have a custom API method, to keep the example simple let's just say it's signature is:
bool Login(username, password);
How can I set the User as logged in, so that I can use things like the [Authorize] attribute? For the moment we want the simplest method possible just to get us started developing the site.
I tried implementing this to set User.Identity manually. But this is then reset on every subsequent request.
In the end I extracted out the logic to the Account controller. This handles the Login and stores the result in the Session. Then I just needed to override the System.Web.Mvc.AuthorizeAttribute class and AuthoriseCore method as follows:
using System.Web;
using System.Web.Mvc;
namespace HomeHealth.Web.Infrastructure
{
public class HomeHealthAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return WebSession.SdkContext.IsAuthenticated;
}
}
}
It has some helper code to clean up accessing the Context from the session, but that's irrelevant. The point is that this is the Attribute/Method you probably want. You can then mark Controllers/Methods with the following:
[HomeHealthAuthorize]
public class PatientController : BaseController
Then all the checking/redirecting is done for you.

Preventing AllowAnonymous

I have a base controller which is globally marked as [Authorize]. Is there a way to prevent Controllers which inherit it from overriding the authorization requirement by simply adding the [AllowAnonymous] attribute?
Here is my exact scenario: I have three base controllers: one is for anonymous users, and two are for logged in users, both of which are globally decorated with [Authorize]. Each new controller that is created inherits from one of the base three, depending on which functionality is needed. One of the [Authorize] controllers contains "highly secure" functionality which absolutely should not be run by anonymous users. A developer inheriting from this "secure" base controller accidentally decorated some methods with [AllowAnonymous] which enabled anonymous users to potentially access the "secure" functionality in the base controller. It was caught in testing but I thought it would be a good idea to prevent that type of mistake, and I'm wondering if there is a simple way to do that. For now, I have taken all of the code inside of the secure base controller and wrapped it in blocks of:
if (Request.IsAuthenticated)
{
// do stuff
}
else
{
// redirect to login page, basically simulating what [Authorize] does
}
The above accomplishes what I want, however it kind of defeats the purpose of the global [Authorize] decoration in the first place. I'm envisioning something along the lines of:
[Authorize(AllowAnonymousOverride=false)] // this doesn't exist, but might be helpful
Is there a better way to accomplish this functionality?
The correct way to do this is to derive your own AuthorizeAttribute. The default AuthorizeAttribute looks like:
namespace System.Web.Mvc
{
public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
}
// This is the Important part..
bool flag = filterContext.ActionDescriptor
.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor
.IsDefined(typeof(AllowAnonymousAttribute), true);
if (flag)
{
return;
}
if (this.AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(
new HttpCacheValidateHandler(this.CacheValidateHandler), null);
return;
}
this.HandleUnauthorizedRequest(filterContext);
}
}
}
Derive your own:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public bool IsAllowAnonymousEnabled { get; set; }
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
}
// This is the Important part..
bool flag = IsAllowAnonymousEnabled
&& (filterContext.ActionDescriptor
.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor
.IsDefined(typeof(AllowAnonymousAttribute), true));
if (flag)
{
return;
}
if (this.AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(
new HttpCacheValidateHandler(this.CacheValidateHandler), null);
return;
}
this.HandleUnauthorizedRequest(filterContext);
}
}
Usage:
[CustomAuthorizeAttribute(IsAllowAnonymousEnabled = false)]
public class MyController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
// This will still execute Authorization regardless of [AllowAnonymous]
return View();
}
}
You can't call base.OnAuthorization() because it will Allow AllowAnonymous.
First, you seem to have a controller with some methods and then your approach is to inherit it so that the same methods are exposed. I wonder what's the point of having two or more controllers that expose the same data. Was is the mistake of that developer or rather it is a custom routine in your approach?
Then, you expect to have an attribute that prevents other attributes but this is clearly not possible in the language nor in the mvc framework.
Third, someone wrote a controller without unit tests or maybe with tests but no one verified these tests so that the issue was caught during manual testing phase. This indicates that the issue is wider and not only restricted to inheritance - suppose your developer wrote a controller that doesn't inherit anything and still exposes some critical data because of allow anonymous mark. Then what? Even if you have a remedy for your original issue, it wouldn't be able to catch the new possible issue.
My advice would be to have a custom analyzer attached to the post build event that scans all possible controllers and makes a list of all anonymous and restricted actions and compares it to a previously generated list. If there is a change, then an alert is created and someone has to resolve the issue manually, either by approving newly created actions or rejecting changes because a bug has been introduced.

Provide common data to all ASP.NET MVC 4 controller method

I am rewriting an ASP.NET webforms app in MVC4 and was wondering how to solve the following problem. It is a multi-tenant app, so part of the URL has the tenant NAME in it:
http://mysite/tenant/controller/action
But tenant is an abbreviation representing the tenant, but I'd like to always convert that to the corresponding integer id and use that throughout the code. What is the best way to write that convert code once and have some variable/property available to all controller methods.
public class DivisionController : Controller
{
//
// GET: /Division/
public ActionResult Index()
{
// I want this.TenantId to be available in all controller methods
FetchDivisions(this.TenantId);
return View();
}
Is a base controller the best way to handle this or filters or attributes?
Yes a base controller will handle this just fine. If you need to perform a database lookup to convert the abbreviation to the integer value you can use the OnActionExecuting event like so:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Lookup code here.
}

How can I kick the user if I change IsConfirmed column?

I use ASP.NET SimpleMembership..
My scenario;
The user login and then I change IsConfirmed column to false on webpages_Membership table..
And the user try to change page, the login page seems to the user..
Your most sensible options are to use any of the authentication related steps in Global.asax.cs, or to derive from AuthorizeAttribute. Given that non-confirmed users are going to have to get to somewhere (for example in order to confirm their account) then you probably don't want the former. With either approach their next request will get denied.
Therefore, I would just extend your [Authorize] attribute to do something like the following, and just use that in the appropriate Controllers and Actions instead of [Authorize] (I'm assuming C# as you didn't specify language in your tags):
public class AuthorizeIfConfirmedAttribute : AuthorizeAttribute {
protected override bool AuthorizeCore(HttpContextBase httpContext) {
if (!base.AuthorizeCore(httpContext)) return false;
System.Security.Principal.IIdentity user = httpContext.User.Identity;
return WebMatrix.WebData.WebSecurity.IsConfirmed(user.Name);
}
}
[AuthorizeIfConfirmed]
public class MyController { ... }
(If you want to use a custom property on your UserProfile class instead of IsConfirmed then you can simply adjust this code accordingly).
The advantage of this approach is that it keeps all your authorization logic in the usual place, and you can also combine it with role enforcement, e.g.:
[AuthorizeIfConfirmed(Roles = "admin")]
public class MyController { ... }
Note that if you use WebApi or SignalR you may have to include these checks in however you are performing request authorization for the apis as well.
I user Application_AuthenticateRequest in Global.asax.. Because my application needs authenticate on all pages..
protected void Application_AuthenticateRequest()
{
if (WebSecurity.IsAuthenticated)
{
bool isConfirmed = (..your codes here..)
if (isConfirmed == false)
{
WebSecurity.Logout();
}
}
}