Deny: Revisited

In a previous post, I talked about the deny option and how it’s evaluated. The documentation on MSDN is talking about conflicting permissions on the same level, however, in TFS2008 SP1, permissions are evaluated bottom up and the first match wins. If a user, Eve, is denied read on $/ but is a member of [Proj]\Readers (which has Read access), the read permission will be the first match and Eve will be to read $/Proj recursively. If you need to deny users read permission, you have to make sure to remove them from all groups that has read access. The reason behind this design is that administrators usually want to allow permissions for certain projects to certain users and the rest should be denied, consider the following tree structure:

$/
    Proj A
    Proj B
    Proj C

You may want to grant Eve read permission on Proj B and only Proj B. To do so, you should deny Eve all permissions on $/ then allow Eve read permission on Proj B. Since permissions are evaluated bottom-up on first-match basis, Eve has read access to proj B, while the denied permissions are effective on both existing projects and future projects D, E, F, ..etc.

Let me explain again why the behavior is as such. If you have 100 projects on your server and you want to allow Eve read access on one project only, then you can deny at the $/ level then allow on the $/Proj level. However, if we don't permit you to do so, you'd have to go through 99 projects to deny read instead. And by the way, TFS behavior is the same as Windows when you set permissions on folders.

Please note that there are two types of permissions inheritance, member of a group (subjects of the ACL) inheritance, and namespace (objects of the ACL) inheritance. As a member of a group (directly or indirectly), you inherit permissions. The same thing applies to namespaces, if it's a child of a parent folder, it inherits the parent's permissions (this can be turned off on an item basis using tf perm itemSpec /inherit:no).

IMO, the right thing to do here is to create the groups correctly, use Windows and/or TFS groups to isolate users with different permissions.

If you still want set the permission on all exisiting projects individually, here's a script that can help:

FOR /F "delims=$ tokens=*" %i IN ('tf dir $/ ^| find /v "/" ^| find /v "(s)"') DO tf perm /deny:* /group:"[Server]\TFS External Users" "$/%i"