Centralized Authorization of Controllers and Actions (ASP.NET MVC 3) - asp.net-mvc-3

Are there any possible security issues or pitfalls to, within a custom AuthorizeAttibute (registered globally), apply authorization based on the controller type and action being called?
e.g. (not real code)
string controllerFullName=_filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.FullName;
string minRequiredRole = GetControllerMinRequiredRole(controllerFullName);
if(User.MeetsRoleRequirement(minRequiredRole))
{
//give access
}
else
{
//no you're not allowed
}

The main issue is with Authorization caching - so there are a few things to know. Check out the links I've posted here:
Creating a AuthorizeAttribute - what do I need to know?
Look at the code to the existing attribute and how it handles caching to ensure you arent causing the same issue the base attribute prevents.

Related

Play Framework 2.5 - How to share cookies in distinct domains

I'm facing some issues while trying to work with cookie in distincts domains. In my scenario I have two apps (app1 and app2). Basically, the app1 is doing an ajax request to the app2 in order to this one creates a cookie (i.e. dummyCookie). The app2 is an application running over the Play framework 2.5 for Java. I'm creating the cookie like this:
response().setCookie(Http.Cookie.builder("dummyCookie", "9e0a6b4c-58ed-b700-0000-015ec494956").build());
I'm using the plays's CORSFilter:
package myCustomFilters.filters;
import play.filters.cors.CORSFilter;
import play.http.DefaultHttpFilters;
import javax.inject.Inject;
public class Filters extends DefaultHttpFilters {
#Inject
public Filters(CORSFilter corsFilter) {
super(corsFilter);
}
}
In my application.conf I have this configuration:
play.http.filters = "myCustomFilters.Filters"
play.filters {
cors {
pathPrefixes = ["/"]
allowedOrigins = null
allowedHttpMethods = ["POST, GET, PUT, DELETE, OPTIONS"]
}
}
In the Chrome's console, in the Network tab, I could see the cookie in the response header.
If I check the Application tab, Cookies session, I couldn't see the cookie there:
I did some investigations and maybe the problem is related to the scenario "CORS + AJAX", since that I have one application calling (via ajax request) the another one to generate a cookie.
Guys, somebody already faced with this kind of scenario?
I had similar issues. I had to tweak the configuration in different ways to make that work in Chrome. And client request also need some specific changes (I use JQuery, and this kind of approach was necessary : Sending credentials with cross-domain posts?).
However, in the end, I discovered that Safari would most likely never work for me, as Apple now has specific rules to handle when cookies can be sent cross domain or not. Depending on what you are trying to achieve, this might lead you to consider a totally different approach.

How to use Breeze IQueryable with CORS?

I use a method to add CORS handlers to my response that is called by a client using Breeze.
You can read more about how I got that working here: Controller not filtering data in Breeze query in DotNetNuke Module
However, I noticed that while $filter works, $expand and $select do not.
So my question is: How can I use return a HttpResponseMessage Type and still use Breeze (I need to do this for CORS).
To prove this, I downloaded and changed the Todos sample:
Original method (works)
http://example/api/todos/todos?$select=isdone
[HttpGet]
public IQueryable<TodoItem> Todos()
{
return _contextProvider.Context.Todos;
}
My method with CORS wrapper (does not expand or select)
http://example/api/todos/TodosCors?$select=isdone
[HttpGet]
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public HttpResponseMessage TodosCors()
{
var response = Request.CreateResponse(HttpStatusCode.OK, (IQueryable<TodoItem>)_contextProvider.Context.Todos);
return ControllerUtilities.GetResponseWithCorsHeader(response);
}
public static HttpResponseMessage GetResponseWithCorsHeader(HttpResponseMessage response)
{
response.Headers.Add("Access-Control-Allow-Origin", "*");
return response;
}
I'm going to comment mainly on the CORS aspect of your question. The part about $expand and $select is addressed in the StackOverflow question to which you refer. In brief, [Queryable] is the Web API attribute which does not support $expand and $select. I think you want the [BreezeQueryable] attribute that does.
I can not say for sure but I do not believe the code you show is the proper way to implement CORS for the Web API. At least I've not seen it done this way.
There are two ways known to me; both involve adding message handlers.
The first is the way we did it in the Breeze Todo sample; the second is the with the Web API CORS support that is on the way.
The way we did it is simplistic but effective. We don't talk about it because we intend to defer to the the approved Web API way when it arrives (soon I hope).
In the Todo demo, look for App_Start/BreezeSimpleCorsHandler.cs. You can just copy it into your own App_Start folder with no changes except to the namespace.
Then your server has to call it. In the Todo sample we did so in the BreezeWebApiConfig.cs but you could put it in Global.asax or in anything that is part of the server boot logic.
// CORS enabled on this server
GlobalConfiguration.Configuration.MessageHandlers.Add(new BreezeSimpleCorsHandler());
As it happens, someone has tried Breeze with the forthcoming Web API CORS NuGet package ... and discovered a bug in Breeze. We have to work that through ... and we will. We really want that way to be THE way.
Until then, you can follow the Todo sample precedent.

Best way to deal with renaming a controller

Working with ASP.NET MVC3, site is in beta and customer decided to rename one of the controllers.
http://domain.com/foo[/*] -> http://domain.com/bar[/*]
What is the most straightforward way to handle redirecting so I don't break any foo bookmarks?
Keep the old controller around so the old URLs still work.
Or add a rewrite rule. Something like:
domain.com/foo(/[_0-9a-z-]+)
to:
domain.com/bar{R:1}
URL Rewrite in IIS
http://technet.microsoft.com/en-us/library/ee215194(WS.10).aspx
http://www.iis.net/download/URLRewrite
If you are using MVC.NET you probably already have URL Rewrite installed.
Another option would be to register a specific route for the old controller name in the Global.asax.cs.
routes.MapRoute(
"RenamedController", // Route name
"[OldControllerName]/{action}/{id}", // URL with parameters
new { controller = "[NewControllerName]", action = "Index", id = "" } // Parameter defaults
);
Add that before the standard default route, and your new controller should respond to both old and new names.
A 302 redirect would be fine, if you can figure out how to do that in IIS. This screenshot suggests it's not that arduous. Alternately, if you're using Castle Windsor you may want to register an interceptor that uses HttpResponse.Redirect()
REST standard suggests the best way to handle this issue is by returning a 301(Moved permanently request). Stack Overflow Post REST Standard
In .Net I recommend using Controller.RedirectToActionPermanent in your controller. See: ASP.NET, .NET Core
Code Example(should work for both ASP and Core):
public class MyController : ControllerBase
{
public IActionResult MyEndpoint(string routeValues1, string routeValues2)
{
return RedirectToActionPermanent("action", "controller", new { value1 = routeValues1, value2 = routeValues2 });
}
}
using MapRoute doesn't make sense in this case. MapRoute is really meant to provide a custom routing solution throughout the system. Its not really meant to deal with individual Redirects. As far as I'm aware it doesn't actually inform the user they are being redirected. See: Creating Custom Routes (C#)

ASP.NET MVC2 and Browser Caching

I have a web application that fetches a lot of content via ajax. For example when a user edits some data, the browser will send the changes using an ajax post and then do an ajax get to get fresh content and replace an existing div on the page with that content. This was working just find with MVC1, but in MVC2 I would get inconsistent results.
I've found that MVC1 by default included an Expires item in the response headers set to the current time, but in MVC2 the Expires header is missing. This is a problem with some browsers (IE8) actually using the cached version of the ajax get instead of the fresh version.
To deal with the problem I created a simple ActionFilterAttribute that sets the reponse cache to NoCache (see below), which works, but it seems kind of sillly to decorate every controller with this attribute. Is there a global way to set this for every controller?
Is this a bug in MVC2 and it really should be setting the expires on every ActionResult/view/page? Don't most MVC programs deal with data entry where stale data is a very bad thing?
Thanks
Dan
public class ResponseNoCachingAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
filterContext.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
}
}
Have you tried adding the following attribute to your controller?
[OutputCache(Location = OutputCacheLocation.None)]

StructureMap controller factory and null controller instance in MVC

I'm still trying to figure things out with StructureMap and one of the issues i'm running into is my Controller Factory class blowing up when a null controller type is passed to it. This only happens when the application builds for the first time, after which every subsequent build works fine. Even when i shutdown Visual Studio and reopen the project (I'm not running this in IIS). It's almost like there is some sort of caching going on. This is what the controller class looks like:
public class IocControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
try
{
return (Controller)ObjectFactory.GetInstance(controllerType);
}
catch (StructureMapException)
{
System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
throw;
}
}
}
What could be wrong? Do i need to have every controller registered? Thank you.
Most browser are looking for a favicon.ico when you load a site, and there is probably some caching involved with this behavior, this might explain the odd "Only fail on the first build" thing you mentionned.
In my case this was causing the problem of the null controller type in the controller factory.
Adding a routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" }); in global.asax makes the error go away, the request should fall through to the filesystem without MVC looking for a favico.ico controller in your code.
Here is a link to Gunnar Peipman post about this
I found out by overriding GetControllerType(string controllerName) in my custom controller factory class and checking what the controllerName value was for each request.
I ran into the same problem with a controller factory built around ninject.
It seems MVC will pass you null for controllertype when it can't resolve a route from the routing table or when a route specifies a none existing controller. I did two things to solve this. You might want to check your route table and add a catchall route that shows a 404 error page like described here .Net MVC Routing Catchall not working
You could also check with the routing debugger what goes wrong.
http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx
I was having the similar problem. I believe it was HTTP requests for nonexistent images, CSS files, etc.
We know the MVC routing first looks to see if the requested file physically exists. If it doesn't then the URL gets tested against the configured Routes. I think the request for an image that didn't physically exist was passed to the Routing engine, and didn't match any routes, so NULL was used.
So to fix it, use FireBug or something to watch for, and fix, broken HTTP requests. During development, I used a route like this to temporarily bypass these issues (all of my resource folders start with an underscore like _Images, _Styles, etc):
routes.IgnoreRoute("_*"); // TODO: Remove before launch
Hope this helps!
What I think you need to do is exactly the same thing that the default MVC controller factory does on the GetControllerInstance method. If you look at the Microsoft source code for DefaultControllerFactory at http://aspnetwebstack.codeplex.com/ you will see that the DefaultControllerFactory throws a 404 Exception when controllerType is null. Here is how we do it based on this information:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
var controller = ObjectFactory.GetInstance(controllerType);
return (IController)controller;
}
}
Basically this will ensure that, when user enters an invalid route the application handles it as a 404 error.

Resources