views:

704

answers:

4

Hello,

I have an ASP.NET MVC application using Authorization Attributes on Controllers and Actions. This has been working well but a new wrinkle has shown up.

Object: Shipment

Roles: Shipping, Accounting, General User

The Shipment moves through a workflow. In state A it can be edited by Shipping only. In state B it can be edited by Accounting only.

I have a ShipmentController, and an Edit Action. I can put an Authorization attribute to limit the Edit action to only those two roles, but this doesn't distinguish between which state the Shipment is in. I would need to do some Authorization inside the action before the service call to determine if the user is really authorized to execute the edit action.

So I'm left with two questions:

1) Whats a good way to have authorization inside an Action. The Controller Action calls to a service, and the service then makes appropriate calls to the Shipment object (update quantity, update date, etc). I know for sure I want the Shipment object to be agnostic of any authorization requirements. On the other hand, I don't have a real grasp if I would want the service object to know about authorization or not. Are there any good patterns for this?

2) Is my problem actually a symptom of bad design? Instead of ShipmentController should I have a StateAShipmentController and StateBShipmentController? I don't have any polymorphism built into the Shipment object (the state is just an enum), but maybe I should and maybe the Controllers should reflect that.

I guess I'm after more general solutions, not a specific one for my case. I just wanted to provide an example to illustrate the question.

Thanks!

+1  A: 

Your authorization attribute could get the Shipment from action parameters or route data and then make a decision.

For number 1, there are a number of patterns that enable rich behavior in domain objects. In double-dispatch, you pass a reference to a service abstraction (interface) to a method on the object. Then it can do its thing. You can also write an application service that takes the Shipment and does the work.

On number 2, not necessarily. You may need to abstract the concept of a "contextual Shipment" into a service that figures out which Shipment context you're in. But I'd YAGNI that until you need it again.

Matt Hinze
+1  A: 

You could take a look at Rhino.Security, it could be used to implement user authorization in these kinds of scenarios.

Igor Brejc
+2  A: 

I don't see a problem with an Action method doing further authorization checks within it. You can leverage the Roles provider for the fine-grained authorization you're looking for. Please excuse my syntax here - it's likely rough and I haven't tested this.

[Authorize(Roles="Shipping, Accounting")]
public ActionResult Edit(int id)
{
    Shipment shipment = repos.GetShipment(id);


    switch (shipment.State)
    {
         case ShipmentState.A:
         if (Roles.IsUserInRole("Shipping"))
                return View(shipment);
         else
                return View("NotAuthorized");
         break;
         case ShipmentState.B:
         if (Roles.IsUserInRole("Accounting"))
                return View(shipment);
         else
               return View("NotAuthorized");
         break;
         default:
              return View("NotAuthorized");
     }
}
Josh E
While that is a "simple" solution, it smells a bit. Hardcoding the authorization on the controller violates the SRP, and should at the very least be refactored out into its own class.
Bruno Lopes
This example almost cries out for a State pattern implementation.
Paul
heh yeah it sure does! Simplistic as it was, I wanted to make sure I addressed the OP's question, rather than get bogged down in details.
Josh E
+1  A: 

In addition to the answer above, you can return a HttpUnauthorizedResult instead of creating your own view for NotAuthorized. This will redirect to the login page and behave just like the usual [Authorize] attribute

chris166