He who permissions least, permissions best

I've heard a few questions and comments about our permission model recently. For example, some folks have asked why user in two groups, one granted a permission and one denied the same permission, is denied the permission rather than granted it. The answer lies in our permissioning model.

Lets first define a few terms to get us all on the same page. Note that these are not necessarily official terms, they're just the terms I've used when discussing permissions with folks on the team here:

  • Identity: A user or group that may be used with the permission system. Groups are both external groups (e.g. active directory security groups) and TFS groups (e.g. [SERVER]\Team Foundation Administrators) 
  • Securable object: An item in Team Foundation which may have permission rights granted or denied to perform certain operations with the object. Examples are the folders and files stored in Version Control, Area nodes, Projects, and the Team Foundation Server itself.
  • Explicit vs. Implicit: An explicit perimission for a given identity is assigned directly to that identity, while an implicit permission is assigned to a group the identity is a member of (either direct membership or through nested membership)
  • Inherited vs. Immediate: While Team Foundation Server global and project permissions don't have a notion of inheritence, permissions on items stored in Version Control do. An inherited permission is assigned to one of the parents of a given securable object, while an immediate permission is set on the securable object itself.

Overall, we follow the same permissioning scheme as the Windows OS. A permission may be allowed, denied, or unset. Unless a permission is allowed (explicitly or implicitly, through inheritence or immediately on the securable object), the permission is denied. The only exception is for Administrator users who are always granted permissions (for Version Control, admin users are users who are Machine Administrators on the AT). Hence, if you don't want users to be able to be able to perform a given action, simply don't grant them permission to do so and don't add them to a group that's been granted that permission.

From that notion comes the title of this post- keep your permission set as minimal as possible, and you won't run into situations where a user is a member of 27 groups, half of which are granted some set of permissions, half of which are denied some overlapping set, and all of which cause your head to spin.

Now, that's not to say that you shouldn't set permissions on objects. Certain permissions are necessary to use the system in a reasonable way. Just remember that as soon as a user is denied a permision, whether explicitly or implicitly and whether the permission is inherited or immediate, no amount of grants at any level to any group will override the deny setting. Denied permissions are the trump card and should be used to specifically lock resources and components. Since all denies trump all allows aside from administrator status, users may be unable to perform actions if they're in two groups that have different permission settings.

Let's walk through a simple example. Suppose you have four different groups- developers, testers, contractor developers, and contractor testers. Due to corporate policy, contractor developers may be disallowed from directly checking in changes and locking files, while contractor testers may be disallowed from viewing the developer source code. Lets also suppose that the two contractor groups have been added to the other two groups (contractor developers to developers and contractor testers to testers). If your product code resides under the Version Control path $/AcmeCode/Product/, you could set permissions on the folder as follows:

  1. allow the "developers" group read, label, lock, pendchange, and checkin access
  2. deny the "contract developers" group checkin and lock access
  3. allow the "testers" group read access
  4. deny the "contract testers" group read access

All other permissions (e.g. "Undo other users changes") are unset for all four of these groups, so are treated as denied permissions unless the users are members of other groups. This permission set will completely lock out the "contract testers" group from that version control path, while the "contract developers" can read, label, and pend changes which they will then shelve to have a full developer check in.

An alternate method would be to make the developers group a member of the contractor developers group (and not vice versa, only granting the "contractor developers" read, label, and pendchange permission while adding the lock and checkin permissions to the developer group. Of course, this seems a little weird from the perspective of someone looking at group containment and could therefore get confusing.

So, that's a little long winded, but I hope it gets the point across and helps you make informed decisions about what our permissions mean.