Cannot Cause Custom AuthorizeAttribute to be Called - authorize-attribute

The authentication works. All good there, not worried about that, I get a 401 if I don't send a proper token.
What I want to do is control REST API methods by user. So I am given to understand the way to do that is to put an attribute on the method that is derived from AuthorizeAttribute.
I am trying to achieve a dirt-simple way of denying access to the MakeComplexNote method.
The problem is that the code in the attribute is never called. Ever. Except the constructor.
I'm not concerned about the merits of what I am trying to do. I just want to know the mechanics. I am tired of fighting with this. How do I do it with the least amount of code possible?
Here is my controller:
using FleetApi.AuthProvider;
using System.Web.Http;
namespace FleetApi.Controllers
{
public class MakeSimpleNoteRequest
{
public string Content { get; set; }
}
public class MakeSimpleNoteResponse
{
public string FinalNote { get; set; }
}
[Authorize]
[RoutePrefix("api/notes")]
public class NotesController : ApiController
{
[HttpPost]
[Route(nameof(MakeSimpleNote))]
public MakeSimpleNoteResponse MakeSimpleNote([FromBody] MakeSimpleNoteRequest request)
{
return new MakeSimpleNoteResponse()
{
FinalNote = request?.Content?.ToUpper(),
};
}
[HttpPost]
[Route(nameof(MakeComplexNote))]
[FleetAuthorize]
public MakeSimpleNoteResponse MakeComplexNote([FromBody] MakeSimpleNoteRequest request)
{
return new MakeSimpleNoteResponse()
{
FinalNote = "COMPLEX:" + (request?.Content?.ToUpper()),
};
}
}
}
And here is the custom attribute
using System;
using System.Web;
using System.Web.Mvc;
namespace FleetApi.AuthProvider
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class FleetAuthorizeAttribute : AuthorizeAttribute
{
public FleetAuthorizeAttribute()
{
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return false;
}
}
}

I think I finally found the answer. I needed to use
using System.Web.Http;
instead of
using System.Web.Mvc;
to get the base class for my attribute
using System;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace FleetApi.AuthProvider
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class FleetAuthorizeAttribute : AuthorizeAttribute
{
public FleetAuthorizeAttribute()
{
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
return false;
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
actionContext.Response.Content = new StringContent("{}");
}
}
}

Related

.NET MVC 5.2 Inheriting attribute Routes from Base Controller

I have a base controller that contains several actions that I would like to use attribute routing on, and not have to override these methods in the controllers that inherit from the base.
As of .NET MVC 5.2 this should be possible according to this: http://www.asp.net/mvc/overview/releases/whats-new-in-aspnet-mvc-52
The example provided shows how to use a class level attribute, but I would like to Implment it at the action level. Has anyone successfuly implemented inheritance of attributes on the action level?
I see another answer .NET WebAPI Attribute Routing and inheritance claims this is possible in with web API controllers, but can it be done using the standard MVC controller?
[InheritedRoute("attributerouting/{controller}/{action=Index}/{id?}")]
public abstract class BaseController : Controller
{
}
public class BlogController : BaseController
{
public string Index()
{
return "Hello from blog!";
}
}
public class StoreController : BaseController
{
public string Index()
{
return "Hello from store!";
}
}
[AttributeUsage(AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class InheritedRouteAttribute : Attribute, IDirectRouteFactory
{
public InheritedRouteAttribute(string template)
{
Template=template;
}
public string Name { get; set; }
public int Order { get; set; }
public string Template { get; private set; }
public new RouteEntry CreateRoute(DirectRouteFactoryContext context)
{
// context.Actions will always contain at least one action - and all of the
// actions will always belong to the same controller.
var controllerDescriptor=context.Actions.First().ControllerDescriptor;
var template=Template.Replace("{controller}",
controllerDescriptor.ControllerName);
IDirectRouteBuilder builder=context.CreateBuilder(template);
builder.Name=Name;
builder.Order=Order;
return builder.Build();
}
}
// Custom direct route provider which looks for route attributes of type
// InheritedRouteAttribute and also supports attribute route inheritance.
public class InheritedDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList
GetControllerRouteFactories(ControllerDescriptor controllerDescriptor)
{
return controllerDescriptor
.GetCustomAttributes(typeof(InheritedRouteAttribute), inherit: true)
.Cast()
.ToArray();
}
}
I think I have the action level working with the following code:
public class InheritedDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(ActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes(typeof(IDirectRouteFactory), inherit: true).Cast<IDirectRouteFactory>().ToArray();
}
}
and call:
routes.MapMvcAttributeRoutes(new InheritedDirectRouteProvider());
this lets me inherit the controller and its routines from an abstract controller, simplified example:
// Inherited class needs to define [RoutePrefix("childPrefix")]
public abstract class ChildBaseController<TChildEntity> : BaseController where TChildEntity : ChildEntity
{
public ChildBaseController(IUnitOfWork DAL) : base(DAL) { }
protected abstract GenericChildRepository<TChildEntity> ChildRepository { get; }
protected abstract string[] GetCreateBindFields();
protected abstract string[] GetEditBindFields();
[Route("{valueId}")]
public ActionResult Index(int valueId)
{
ViewBag.ValueId = valueId;
return View(ChildRepository.Get().Where(cr => cr.ValueId == valueId));
}
... bunch more CRUD actions with [Route(...)] ...
}
inherited class:
namespace Web.Frontend.Controllers
{
[RoutePrefix("Fields")]
public class FieldsController : ChildBaseController<Field>
{
public FieldsController(IUnitOfWork DAL) : base(DAL) { }
protected override GenericChildRepository<Field> ChildRepository
{
get
{
return DAL.Fields;
}
}
protected override string[] GetCreateBindFields()
{
return new string[] { ... };
}
protected override string[] GetEditBindFields()
{
return new string[] { ... };
}
}
}

How can I incorporate this Castle Windsor DI code into my Controller and Repository code?

Note: I can't bountify this question yet (it's too new), but I will reward a good answer with 50 points, and a great answer with 100 (when possible).
I need to incorporate DI into my Web API project. I currently have the expected Model and Controller folders/classes, along with corresponding Repository classes.
That seemed to work well for awhile, but now I need to use DI with the Controllers so that I can pass an Interface type to the Controllers' constructor.
I'm struggling with just how to implement this; that is, how to incorporate the DI "extravaganza" into my existing Model/Controller/Repository structure. I have example DI code, but I don't know just how it should be applied to my project.
Perhaps some code is in order to try to make this clear. I will show a simple sample of what I've got, followed by the DI code I'd like to somehow incorporate into it / with it.
Here is the existing Model/Controller/Repository code:
MODEL
public class Department
{
public int Id { get; set; }
public int AccountId { get; set; }
public string Name { get; set; }
}
CONTROLLER
public class DepartmentsController : ApiController
{
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
{
return _deptsRepository.Get(ID, CountToFetch);
}
public void PostDepartment(int accountid, string name)
{
_deptsRepository.PostDepartment(accountid, name);
}
public HttpResponseMessage Post(Department department)
{
// Based on code 2/3 down http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC4-and-WebAP?msg=4727042#xx4727042xx
department = _deptsRepository.Add(department);
var response = Request.CreateResponse<Department>(HttpStatusCode.Created, department);
string uri = Url.Route(null, new { id = department.Id });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
REPOSITORY
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;User ID=BlaBlaBla...
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT td_department_accounts.dept_no,
IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name
FROM t_accounts INNER JOIN td_department_accounts ON
t_accounts.account_no = td_department_accounts.account_no ORDER BY
td_department_accounts.dept_no";
cmd.CommandType = CommandType.Text;
conn.Open();
int i = 1;
using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
{
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
int deptNum = oleDbD8aReader.GetInt16(0);
string deptName = oleDbD8aReader.GetString(1);
Add(new Department { Id = i, AccountId = deptNum, Name,
deptName });
i++;
}
}
}
}
}
public int Get()
{
return departments.Count;
}
private Department Get(int ID) // called by Delete()
{
return departments.First(d => d.Id == ID);
}
public IEnumerable<Department> Get(int ID, int CountToFetch)
{
return departments.Where(i => i.Id > ID).Take(CountToFetch);
}
public Department Add(Department dept)
{
if (dept == null)
{
throw new ArgumentNullException("Department arg was null");
}
// This is called internally, so need to disregard Id vals that already exist
if (dept.Id <= 0)
{
int maxId = departments.Max(d => d.Id);
dept.Id = maxId + 1;
}
if (departments != null) departments.Add(dept);
return dept;
}
public void PostDepartment(int accountid, string name)
{
int maxId = departments.Max(d => d.Id);
Department dept = new Department();
dept.Id = maxId + 1;
dept.AccountId = accountid;
dept.Name = name;
departments.Add(dept);
}
public void Post(Department department)
{
int maxId = departments.Max(d => d.Id);
department.Id = maxId + 1;
departments.Add(department);
}
public void Put(Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == department.Id);
departments[index] = department;
}
public void Put(int id, Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == id);
departments[index] = department;
}
public void Delete(int id)
{
Department dept = Get(id);
departments.Remove(dept);
}
And now here is the DI code that I want to incorporate
Classes in the DIInstallers folder:
IDepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public interface IDepartmentProvider
{
// These are the methods that are in the sample example IAuthProvider interface; I don't know what I need yet, though...
//bool Authenticate(string username, string password, bool createPersistentCookie);
//void SignOut();
}
}
DepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public class DepartmentProvider : IDepartmentProvider
{
// TODO: Implement methods in IDepartmentProvider, once they have been added
}
}
DepartmentProviderInstaller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace HandheldServer.DIInstallers
{
public class DepartmentProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(IDepartmentProvider))
.WithServiceAllInterfaces());
// If I declare/implement more interface types (other than IDepartmentProvider), I assume there would be another container.Register() call for each of them?
}
}
}
Classes in the DIPlumbing folder:
WindsorCompositionRoot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.Windsor;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace HandheldServer.DIPlumbing
{
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
}
WindsorControllerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace HandheldServer.DIPlumbing
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
}
The Global.asax.cs file
using System;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Castle.Windsor;
using Castle.Windsor.Installer;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Http.Dispatcher;
using HandheldServer.DIPlumbing;
namespace HandheldServer
{
public class WebApiApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
BootstrapContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
// Code that runs when an unhandled error occurs
void Application_Error(object sender, EventArgs e)
{
// Get the exception object.
Exception exc = Server.GetLastError();
log.Error(exc.Message);
// Clear the error from the server
Server.ClearError();
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
}
protected void Application_End()
{
container.Dispose();
}
}
}
So, I think I've basically got the code I need, but how to fold the DI code into my previous (Model/Controller/Repository) code is the part that has me stumped.
You can simply use WebApiContrib.IoC.CastleWindsor (Nuget).
This test should give you an idea of how to use it.

How do I pass variables to a custom ActionFilter in ASP.NET MVC app

I have a controller in my MVC app for which I'm trying to log details using a custom ActionFilterAttribute, by using the onResultExecuted method.
I read this tutorial to understand and write my own action filter. The question is how do I pass variables from the controller to the action filter?
I want to get the input variables with which a controller is called. Say, the username/user ID.
If (in some situations) an exception is thrown by any controller method, I would want to log the error too.
The controller -
[MyActionFilter]
public class myController : ApiController {
public string Get(string x, int y) { .. }
public string somemethod { .. }
}
The action filter -
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void onActionExecuted(HttpActionExecutedContext actionExecutedContext) {
// HOW DO I ACCESS THE VARIABLES OF THE CONTROLLER HERE
// I NEED TO LOG THE EXCEPTIONS AND THE PARAMETERS PASSED TO THE CONTROLLER METHOD
}
}
I hope I have explained the problem here. Apologies if I'm missing out some basic objects here, I'm totally new to this.
Approach - 1
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
Action Method
[MyActionFilter]
public ActionResult Index()
{
ViewBag.ControllerVariable = "12";
return View();
}
If you pay attention to the screenshot, you can see the ViewBag information
Approach - 2
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
//Your Properties in Action Filter
public string Property1 { get; set; }
public string Property2 { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
Action Method
[MyActionFilter(Property1 = "Value1", Property2 = "Value2")]
public ActionResult Index()
{
return View();
}
I suggest another approach, and it is passing parameters to Action Filter as constractor.
[PermissionCheck(Permissions.NewUser)]
public ActionResult NewUser()
{
// some code
}
Then in the ActionFilter:
public class PermissionCheck : ActionFilterAttribute
{
public Permissions Permission { get; set; }
public PermissionCheck(Permissions permission)
{
Permission = permission;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (/*user doesn't have that permission*/)
{
filterContext.Result = new RedirectToRouteResult
(
new RouteValueDictionary
(
new {
controller = "User",
action = "AccessDeny",
error = "You don't have permission to do this action"
}
)
);
base.OnActionExecuting(filterContext);
}
}
}
Which Permissions is an ENUM like:
enum Permissions {NewUser, Edit, Delete, Update, ...}

Get Session from HttpActionContext

I'm trying to create a permission attribute to configure in each action of my controllers so this custom attribute should take the sessionId from the user.
My code is like that:
public class PermissionChecker: ActionFilterAttribute
{
private int _permissionId { get; set; }
private IUserSelectorService _userService { get; set; }
public PermissionChecker(int permissionId)
{
_permissionId = permissionId;
_userService = new UserSelectorService();
}
public PermissionChecker(int permissionId, IUserSelectorService userService)
{
_permissionId = permissionId;
_userService = userService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (_userService.HasPermission(_permissionId, /* here I must pass the session["Id"]*/)){
base.OnActionExecuting(actionContext);
return;
}
throw new HttpException(401, "Unauthorized");
}
}
Use this
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
var id = filterContext.HttpContext.Session["Id"];
}
}
EDIT
Given the fact that you're using MVC 4 and you don't have
public override void OnActionExecuting(ActionExecutingContext filterContext)
Try using
System.Web.HttpContext.Current.Session
if you are trying to access using ActionFilterAttribute then OnActionExecting event it wont give the accessibility of HttpContext with System.Web.Http.
Instead of that If you are trying to access using System.Web.Mvc it will provide you the current session with onActionExecting event with help of ActionExecutingContext class.

ASP.NET MVC: Controller.HandleUnknownAction 404 or 405?

I'm overriding ASP.NET MVC's Controller.HandleUnknownAction(string actionName) method. It's being called when an action is not found and also when an HTTP method is not allowed. How can I distinguish between the two? I'd like to return a 404 when and action is not found and 405 when a method is note allowed.
The simplest way I can think of is to create custom action filter. This will allow you to return http status code result if method is not allowed
public class HttpPostFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!(filterContext.RequestContext.HttpContext.Request.GetHttpMethodOverride().Equals("post", StringComparison.InvariantCultureIgnoreCase)))
{
filterContext.Result = new HttpStatusCodeResult(405);
}
}
}
Or better, create more generic version of it, much like AcceptVerbsAttribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class AllowMethodsAttribute : ActionFilterAttribute
{
public ICollection<string> Methods
{
get;
private set;
}
public AllowMethodsAttribute(params string[] methods)
{
this.Methods = new ReadOnlyCollection<string>(methods);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
if (!this.Methods.Contains(httpMethodOverride, StringComparer.InvariantCultureIgnoreCase))
{
filterContext.Result = new HttpStatusCodeResult(405);
}
}
}
And use it like
[AllowMethods("GET")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
Customizing attribute to take HttpVerbs as parameter is up to you.

Resources