The journeylism of @yreynhout

On CQRS, DDD(D), ES, …

Trench Talk – Authorization

For a long time now, I’ve been dealing with the subject of authorization and its bigger brother, authentication. Not that dealing with these makes me an expert, mind you. But it did arouse my interest in the areas of RBAC (role based access control) and family (hierarchical RBAC, administrative RBAC, attribute based RBAC, …), RAD (resource access decision) from OMG fame, XACML (eXtensible Access Control Markup Language) from OASIS, and a few others I’ve lost track of over the years. Why do we embed authorization into software, though? I can understand the need in military and nuclear facilities, or when company secrets are involved. But beyond that it’s mainly a game of fear, risk, trust and workflow. In general, in end-user facing systems, there are two forces at play: one is the top-down, enterprisy, we-want-control-over-who-sees-or-does-what and the other is the this-is-my-stuff-and-i-say-who-gets-to-see-it-and-manipulate-it. It’s not uncommon to find both in one software system you’re working on. The reason is quite simple: administration overhead. Imagine controlling each and every employee’s role(s), exceptions to such role(s), role transitions over time, etc … and you’ve got 90,000 of ’em. While some organizations are – believe it or not – willing to pay money to their IT department (in a low-cost country, no doubt) to take care of this task, others have flattened their structure or adopted controlled delegation to overcome this. I guess, a lot depends on “the culture within”. Bottom line, to me, is that fine-grained, top-down control doesn’t scale very well – administratively speaking – and is entangled with topics of trust, risk, and – for those at the bottom – frustration. This is usually the point where we tend to see the “owner-centric” approach to authorization, where “owners” impose who gets to see or manipulate a particular resource or set of resources they own or co-own. If we’re lucky, that is.

Authorization models are fascinating. There have been many attempts at abstracting this domain and treating it as a “separate” problem. All in all we’ve been pretty successful at doing that. Think of all the products out there that incorporate the notion of storing and enforcing authorization “policies”. I’ve always been in environments where I couldn’t use off-the-shelf products for this particular problem. Mostly because it would have meant coupling to a particular technology and the inflexibility/inadequacy they brought with them. So, yes, in case you’re wondering, I’ve reinvented this wheel a couple of times. Why? Because, amongst other reasons, the data to base the access decision on was tightly coupled to the domain at hand. Depending on the required “freshness” of said data with regard to securing a particular resource you will want to design it one way or another.

What I like about Domain Driven Design is that it embraces the idea of entertaining multiple models to solve a particular problem. Of course, with choice comes responsibility, trying things out, failing, heck, even stupidity, but above all learning from those. Being focused and doing small, controlled experiments – verbally at the whiteboard or textual as in a code probe – may mitigate the risk of going down the wrong path. Such endeavors depend on the skill and experience of those involved, IMO. Yet by no means is success guaranteed. Such is life … how depressing. Still, let’s look at the bright side … of life, that is.

Make the implicit explicit

Let’s look at a small example … Imagine a piece of software that allowed one or more people to collaborate on an art design. Below I’ve coded up the use case of inviting somebody to collaborate with somebody else on a particular design.

public class InviteArtDesignCollaboratorHandler : Handles<InviteArtDesignCollaborator>
{
  //invite somebody to collaborate with you on a certain art design.
  public void Handle(InviteArtDesignCollaborator message)
  {
    var inviter = this.personRepository.Get(new PersonId(message.InviterId));
    var invitee = this.personRepository.Get(new PersonId(message.InviteeId));
    var artDesign = this.artDesignRepository.Get(new ArtDesignId(message.ArtDesignId));
    var invitation = artDesign.InviteCollaborator(inviter, invitee);
    this.invitationRepository.Add(invitation);
  }
}

If we zoom in on the authorization aspect of this use case, what do we see in the above code? Nothing, that’s right. Who can invite collaborators to an art design? The original creator of such a design IF and only IF he is still a participant of the collection the art design is part of. Who can be invited to an art design? Somebody who already participates in the collection the art design is part of. Obviously, I’m making this up as I go. Yet, these aren’t uncommon requirements to come across. Let’s see how this manifests itself intermingled with the above code.

public class InviteArtDesignCollaboratorHandler : Handles<InviteArtDesignCollaborator>
{
  //invite somebody to collaborate with you on a certain art design.
  public void Handle(InviteArtDesignCollaborator message)
  {
    var inviter = this.personRepository.Get(new PersonId(message.InviterId));
    var invitee = this.personRepository.Get(new PersonId(message.InviteeId));
    var artDesign = this.artDesignRepository.Get(new ArtDesignId(message.ArtDesignId));
    var collection = this.collectionRepository.Get(artDesign.CollectionId);
    if (!artDesign.WasOriginallyCreatedBy(inviter))
      throw new NotAuthorizedException("The inviter is not the original creator of the art design.");
    if (!collection.HasAsParticipant(inviter))
      throw new NotAuthorizedException("The inviter is not a participant of the collection the art design is part of.");
    if (!collection.HasAsParticipant(invitee))
      throw new NotAuthorizedException("The invitee is not a participant of the collection the art design is part of.");
    var invitation = artDesign.InviteCollaborator(inviter, invitee);
    this.invitationRepository.Add(invitation);
  }
}

What if we were to separate the authorization responsibility from the actual use case? Why would we do that? Does changing the rules about who can be invited and who can invite fundamentally change this use case? Aren’t the axis of change different for these two? If yes, then the next piece of code might make more sense. Mind you, it’s a matter of preference at this point. Requirements might push it more in one or the other direction as to “the code feeling natural”.

public class InviteArtDesignCollaboratorAuthorizer : Authorizes<InviteArtDesignCollaborator>
{
  //invite somebody to collaborate with you on a certain art design.
  public void Authorize(InviteArtDesignCollaborator message)
  {
    var inviter = this.personRepository.Get(new PersonId(message.InviterId));
    var invitee = this.personRepository.Get(new PersonId(message.InviteeId));
    var artDesign = this.artDesignRepository.Get(new ArtDesignId(message.ArtDesignId));
    var collection = this.collectionRepository.Get(artDesign.CollectionId);
    if (!artDesign.WasOriginallyCreatedBy(inviter))
      throw new NotAuthorizedException("The inviter is not the original creator of the art design.");
    if (!collection.HasAsParticipant(inviter))
      throw new NotAuthorizedException("The inviter is not a participant of the collection the art design is part of.");
    if (!collection.HasAsParticipant(invitee))
      throw new NotAuthorizedException("The invitee is not a participant of the collection the art design is part of.");
  }
}

public class InviteArtDesignCollaboratorHandler : Handles<InviteArtDesignCollaborator>
{
  //invite somebody to collaborate with you on a certain art design.
  public void Handle(InviteArtDesignCollaborator message)
  {
    var inviter = this.personRepository.Get(new PersonId(message.InviterId));
    var invitee = this.personRepository.Get(new PersonId(message.InviteeId));
    var artDesign = this.artDesignRepository.Get(new ArtDesignId(message.ArtDesignId));
    var invitation = artDesign.InviteCollaborator(inviter, invitee);
    this.invitationRepository.Add(invitation);
  }
}

We can now separate the authorization test specifications – who can perform the use case under what circumstances – from the actual use case specifications – what is the side effect of the use case, when can it happen/can’t it happen depending on current state. Is doing all this desirable? I guess it depends on what you are getting out of it.
Fundamentally, those query methods (“WasOriginallyCreatedBy”, “HasAsParticipant”) are using projected and provided state (input) to determine what their boolean return value should be. If we could live with relaxed consistency in this regard, we could even project these values “at another point in time” and use those instead of these query methods. Heck, we’d be able to ditch these query methods from our model altogether. But again, it largely depends on what kind of consistency you are expecting vis-à-vis the aggregate you are affecting. It’s also debatable whether we are dealing with a different model or not. After distillation, would these query methods still be there? Why? Are they essential or circumstantial? These aren’t questions a blog post can answer, I’m afraid. They are highly contextual.

Keep it simple when appropriate

In scenarios with simpler requirements we could choose an approach reminiscent of aspect oriented programming or pipes and filters. Below are a couple of examples. The assumption is that some piece of code will inspect the handler’s Handle method, look for the Authorization attribute and instantiate either a decorating handler or a pipeline component to represent the authorization based on the attribute’s properties.

public class StartSeasonPortfolioHandler : Handles<StartSeasonPortfolio>
{
  //start a new season portfolio for a subsidiary.
  [Authorize(Role="SubsidiaryAdministrators")]
  public void Handle(StartSeasonPortfolio message)
  {
    var subsidiaryId = new SubsidiaryId(message.SubsidiaryId);
    var season = Season.From(subsidiary, message.Season);
    if (this.portfolioRepository.HasPortfolioForSeason(subsidiaryId, season)) 
      throw new SeasonPortfolioAlreadyStartedException("A portfolio was already started for the specified season of the subsidiary.");
    var subsidiary = this.subsidiaryRepository(subsidiaryId);
    var portfolio = subsidiary.StartPortfolio(new PortfolioId(message.PortfolioId), new PortfolioName(message.Name), season);
    this.portfolioRepository.Add(porfolio);
  }
}

This example codifies the caller must be member of the role ‘SubsidiaryAdministrators’. Interestingly enough, you can’t really tell who “the caller” is from the above code, now can you? This was less obvious in the previous art design example. The assumption there was that the “inviter” was “the caller“. How do you know that for sure, though? It’s just data I could have easily spoofed. Alas, authentication – proving the caller’s identity – and message integrity – did somebody tamper with this message – are beyond the scope of what I want to discuss here (*). For now, let’s assume we know who the ambient caller is.

public class StartSeasonPortfolioHandler : Handles<StartSeasonPortfolio>
{
  //start a new season portfolio for a subsidiary.
  [Authorize(Permission="CanStartSeasonPortfolio")]
  public void Handle(StartSeasonPortfolio message)
  {
    //ommitted for brevity - same as above
  }
}

When dealing with fixed or hard coded roles becomes cumbersome administratively speaking, the next thing you’ll see is this shift in focus towards permission to perform a certain operation. It’s obviously easier, since I could be member of multiple, dynamic roles that have the CanStartSeasonPortfolio access decision set to allowed. Yet, what if multiple roles had conflicting access decisions? You’d need a strategy to resolve such issues. In finance, accounting, health care and military environments you may even encounter separation of duty.

public class StartSeasonPortfolioHandler : Handles<StartSeasonPortfolio>
{
  //start a new season portfolio for a subsidiary.
  [Authorize]
  public void Handle(StartSeasonPortfolio message)
  {
    //ommitted for brevity - same as above
  }
}

Command messages could prove to be natural authorization boundaries. In such case, the name of the message translates to a particular permission, being a very conventional way of modeling authorization. If only it was always this simple 🙂

One thing that is very different from the art design example is that the notion of authorization wasn’t very coupled to the model we were dealing with. It felt somewhat more natural to tuck it away behind an attribute. There was no urge to consider it part of the model that dealt with the use case itself. Why is that? I think because no decisions were made based on state that was part of the model.

(*) If you’ve been infatuated with OAUTH, OpenID, Kerberos, PKI, etc. in the past, I doubt I could bring anything new to the table.

Not just for writing

Upon till now we’ve been very focused on preventing callers from invoking behavior they are not allowed to. However formidable that may be, I often find that’s only half of the story. When dealing with users or automated services, we want to guide them towards the happy path, no? We want to tell them: to the best of my knowledge, at the point in time you’re asking me, these are the state transitions you are allowed to perform. That’s when it hit me. The same authorization decisions you’re performing on the write side are also required on the read side, albeit in a form that could be very different. Obviously, we’re dealing with more latency here. As soon as you’ve computed or queried the access decision, it could be modified behind your back. It may have to travel across a network, it may have to manifest itself in a user interface (e.g. enable or disable a button/link) or await a user’s decision, it may manifest itself in an automated service choosing an alternate path through its logic. It’s a hint/clue, at best. Yet, often end users are quite content with these decisions.
That said, to me the key differentiator is the fact that permissions are not limited to state changing behavior. Whether or not you are allowed to view a particular piece of information is something that usually only manifests itself on the read side. Also note that the read side is often less harsh. It doesn’t throw an exception at you, rather it filters or hides information from you. Often I find that you’ll have a mixture of view and behavior permissions, especially on the read side.
Below you’ll find a very technology specific example of how that might manifest itself in a web api.

[RoutePrefix("portfolios")]
public class PortfolioResourceController : ApiController
{
  [Route("{id}")]
  public IHttpActionResult Get(string id)
  {
    var identity = new ClaimsIdentity(RequestContext.Principal.Identity);
    return this.portfolioQueries.
      ById(id).
      Select(_ => Ok(_.CompleteAuthorization(identity, this.Url))).
      DefaultIfEmpty(NotFound()).
      Single();
  }
}

public class PortfolioResource 
{
  public PortfolioResource CompleteAuthorization(ClaimsIdentity identity, UrlHelper helper)
  {
    var links = new List<Link>();
    //Note: CanEdit|Delete|ViewPortfolioItems are extension methods on ClaimsIdentity that, internally, 
    //      use claims to answer the authorization requests in a similar way as how the
    //      write side would ask them.
    if(identity.CanEditPortfolio(Id)) 
      links.Add(new Link { Rel = "edit", Href = helper.GetLink<PortfolioResourceController>(_ => _.Put(Id)) });
    if(identity.CanDeletePortfolio(Id)) 
      links.Add(new Link { Rel = "delete", Href = helper.GetLink<PortfolioResourceController>(_ => _.Delete(Id)) });
    if(identity.CanViewPortfolioItems(Id)) 
      links.Add(new Link { Rel = "items", Href = helper.GetLink<PortfolioResourceController>(_ => _.GetItems(Id)) });

    return new PortfolioResource(Id, Name, Season, SubsidiaryId, links.ToArray());
  }
}

What’s important to realize, is that we’ll probably start looking for similarity and deduplication (dare I say DRY) of the same logic that happens to be invoked on the write and read side, and trying to find a common home for them. Over the past months I saw an entire model emerge to deal with this and something as difficult as the art design example. Again, I’m pretty sure this isn’t the only way how I could’ve modeled it, but for now there were strong indicators this was a viable option. Below another, more convoluted – it’s the last, I promise – example of what I’ve come up with.

public class PortfolioAccessPolicy
{
  public bool CanEdit(Subject subject)
  {
    return 
      subject.CanEditPortfolio(RolePermissionSet) && 
      subject.IsStarterOfPortfolio(StarterId);
  }

  public bool CanDelete(Subject subject)
  {
    return 
      subject.CanDeletePortfolio(RolePermissionSet) && 
      subject.IsStarterOfPortfolio(StarterId);
  }

  public bool CanViewItems(Subject subject)
  {
    return 
      subject.CanViewPortfolioItems(RolePermissionSet) ||
      subject.IsPortfolioSupervisor();
  }
}

//Usage on the read side
public class PortfolioResource 
{
  public PortfolioResource CompleteAuthorization(Subject subject, PortfolioAccessPolicy policy, UrlHelper helper)
  {
    var links = new List<Link>();
    if(policy.CanEdit(subject)) 
      links.Add(new Link { Rel = "edit", Href = helper.GetLink<PortfolioResourceController>(_ => _.Put(Id)) });
    if(policy.CanDelete(subject)) 
      links.Add(new Link { Rel = "delete", Href = helper.GetLink<PortfolioResourceController>(_ => _.Delete(Id)) });
    if(policy.CanViewItems(subject)) 
      links.Add(new Link { Rel = "items", Href = helper.GetLink<PortfolioResourceController>(_ => _.GetItems(Id)) });

    return new PortfolioResource(Id, Name, Season, SubsidiaryId, links.ToArray());
  }
}

//Usage on the write side
public class EditPortfolioAuthorizer : Authorizes<EditPortfolio>
{
  public void Authorize(Subject subject, EditPortfolio message)
  {
    var policy = this.policyRepository.Get(new PortfolioId(message.PortfolioId));
    if(!policy.CanEditPortfolio(subject))
      throw new NotAuthorizedException("The caller is not authorized to edit this portfolio.");
  }
}

Conclusion

My only hope in writing this down is that it will make you think about how you’re modeling authorization and the realization that there is no one way of modeling it. Whether you’re rolling your own or dealing with something off the shelf, know that your requirements will dictate a lot. If you’re in an environment where reads and writes are separated but crave some commonality, know there are ways to share. I do not pretend to have all the answers, merely sharing what I’ve learned so far and what worked for me.

Advertisements

One response to “Trench Talk – Authorization

  1. yreynhout July 22, 2014 at 14:10

    For anyone wondering, the access policy above is very much a custom way of doing attribute/policy based access control (for which XACML is a “handle with gloves” standard). I find in line of business applications these things do not change that easily. As such coding them up using a “real” programming language – implying xml is not a real programming language – is far easier on the eye.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: