ASP.NET MVC, 'Ticket Required' Attribute
Asked Answered
M

2

6

I am attempting to build a system that allows users to perform certain actions, but their account must have a specific 'Ticket' per time they do it. For instance, suppose they wish to create a Product, they would need a CreateProductTicket.

I could simply do this with some 'if' statements, sure, but I want to try a bit more of a robust solution. My structure looks something like this...

interface ITicket<T> where T : ITicketable
{
}

My basic goal is to build an Attribute, perhaps like the following..

public class TicketRequiredAttribute : Attribute
{
 public TicketRequiredAttribute(ITicket<T> ticket)
 {
  if(ticket == null) 
    return;
  }
}

And to be able to decorate Controller or Repository Actions with this. So like ...

ProductsControlller

[TicketRequired(CreateProductTicket)]
public ActionResult CreateProduct(Product product)
{
 // ... **I am unsure how to tell if TicketRequired was true or not**
}

Problem 1

I'm not familiar enough with attributes to know how to tell if TicketRequired was 'met' or not. Can anyone enlighten me on this?

Problem 2

The problem I am running into is with database querying. I want to be able to check the user (IMembershipRepository has a GetUser method), but I'm not entirely certain how to do that through an attribute.

Using Castle.Windsor, I have my Dependency Injection set up to inject repositories into controllers. I suppose I could pass the IMembershipRepository through the TicketRequired constructor, but I have a feeling that will become very messy - and extremely unstable. Is there a more logical way to approach this?

Masculine answered 22/12, 2010 at 16:49 Comment(0)
C
6

You're almost there. You can find more details at http://www.asp.net/mvc/tutorials/understanding-action-filters-cs

I would only use the attribute on the action since the website is where I do all my authorization.

Here is a possible solution. I have not tested this, but it should work. You'll need to verify the way I'm redirecting, not sure if that's the proper way.

 public class TicketRequiredActionFilter : ActionFilterAttribute
 {
        private Type _ticketType;

  public TicketRequiredAttribute(Type ticketType)
  {
        _ticketRequired = ticketType;     
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
     UserServices userServices = GetUserServicesViaDIContainer(); // you'll need to figure out how to implement this 
     string userId = filterContext.HttpContext.User.Identity.Name
     bool hasTicket = userServices.HasTicket(_ticketType, (int)userId); // again, you'll need to figure out the exact implementation
     if(!hasTicket)
     {
        filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Home" }, {"action", "NoPermission" } })
     }
     else
     { 
        base.OnActionExecuting(filterContext);
     }     
  }
}

In your controller:

[TicketRequiredActionFilter(typeof(CreateProductTicket))]
public ActionResult MyMethod()
{
    // do stuff as if the person is authorized and has the ticket
}

If the user doesn't have the ticket, a redirect is issues;, otherwise, continue as normal.

Clintclintock answered 22/12, 2010 at 18:14 Comment(4)
Yes, this is exactly what I was looking for! I also found an article at lostechies.com/blogs/jimmy_bogard/archive/2010/05/03/… for making these ActionFilters utilize my Windsor Dependency Injection! Thank you so much, this has me on the right track. I still don't know how I'll handle this on the Repository Level, but I'll figure something out.Masculine
I wouldn't add it to the repository if I were you. If you do add it to the repository, you'll then need to figure out how to bubble up the error when someone tries to access something via the repository without a specific ticket. I think authorization is best left at the UI level.Clintclintock
Okay. I'll try to stick to that. Is it okay to design Filters that can use my DI containers, still?Masculine
DI containers are great and if you're using one in your application, then Action Filters shouldn't be any different - definitely use it.Clintclintock
O
1

This sounds very much like user roles.

How are you handling the user membership? If your using the built-in asp.net membership you can use roles. So each user will have a certain number of roles in your case one of the will be "CreateProductTicket" then you can decorate your action or controller with the Authorize attribute. Something like:

[Authorize(Roles="CreateProductTicket")]
public ActionResult CreateProduct(Product product)

If a user doesn't have the role or is not authorized then they can access the action.

Ophidian answered 22/12, 2010 at 17:2 Comment(2)
It has nothing to do with user roles. I am not attempting to do a role authorization system with this. This is for a completely different process.Masculine
What I am trying to do is more like Interceptors in NHibernate. I just am having trouble grasping the concept of how they work.Masculine

© 2022 - 2024 — McMap. All rights reserved.