TFS11 API: Managing Team Administrators

Getting and setting Team Administrators

It’s time for yet another blog post about the new Team Foundation Server 11 beta API. This post is somewhat related to one of the first posts from the TFS 11 API series, where I’ve discussed how to retrieve team members that are working on your team. If you have read that post, you mush have noticed that I haven’t even mentioned that each team also has one or more team administrators. There are two reasons why I didn’t write about them in the first post. One reason is that I didn’t want to write a post that was too big. Secondly, I didn’t know how to do this stuff back then. I have since then figured it out and I’ve decided to share what I’ve learned with you!

Reading the Team Administrators

Retrieving a list of team administrators for your team project is not really that difficult, it’s just not as obvious as you might expect. I mean, I couldn’t find a GetTeamAdmins() method or perhaps a IsTeamAdmin property. But then again, I was sure it had to be implemented using some kind of security mechanism, such as access control lists or similar. That’s why I’ve fired up Reflector on my TFS server machine and had a look at the server-side assemblies. You know, the ones whose names start with Microsoft.TeamFoundation.Server. I’ve then noticed that there is in fact an AccessControlList (ACL) object used when loading team administrators into the team’s model (WebAccess is an MVC application, remember) and I was able to use this to locate a piece of equivalent code that can be used on the client end. So, I’ll show you the snippet first and explain later:

static void Main(string[] args)
{
	// Connect to the TFS server and get the team project URI.
	var collection = GetServer("server_uri");
	var projectUri = GetProjectUri(collection, "project_name");
  
	// Retrieve the default team.
	TfsTeamService teamService = collection.GetService<TfsTeamService>();
	TeamFoundationTeam defaultTeam = teamService.GetDefaultTeam(projectUri, null);

	// Get security namespace for the project collection.
	ISecurityService securityService = collection.GetService<ISecurityService>();
	SecurityNamespace securityNamespace = securityService.GetSecurityNamespace(FrameworkSecurity.IdentitiesNamespaceId);

	// Use reflection to retrieve a security token for the team.
	MethodInfo mi = typeof(IdentityHelper).GetMethod("CreateSecurityToken", BindingFlags.Static | BindingFlags.NonPublic);            
	string token = mi.Invoke(null, new object[] { defaultTeam.Identity }) as string;

	// Retrieve an ACL object for all the team members.
	var allMembers = defaultTeam.GetMembers(collection, MembershipQuery.Expanded).Where(m => !m.IsContainer);
	AccessControlList acl = securityNamespace.QueryAccessControlList(token, allMembers.Select(m => m.Descriptor), true);

	// Retrieve the team administrator SIDs by querying the ACL entries.
	var entries = acl.AccessControlEntries;
	var admins = entries.Where(e => (e.Allow & 15) == 15).Select(e => e.Descriptor.Identifier);

	// Finally, retrieve the actual TeamFoundationIdentity objects from the SIDs.
	var adminIdentities = allMembers.Where(m => admins.Contains(m.Descriptor.Identifier));        
}

Note: in order to get this code to actually compile, don’t forget to add the following references:

  • Microsoft.TeamFoundation.Common.dll
  • Microsoft.TeamFoundation.Client.dll

Alright, let’s examine what this code snippet does and how. The first ten or so lines should be pretty straightforward. First, we establish a connection to a TFS server specified by it’s URI and retrieve a URI for a team project of a specified name. We then acquire TfsTeamService, which we use to retrieve the default team on the team project. You can read more about the TfsTeamService and what it does here.

The next step is to retrieve an instance of the ISecurityService, which we need to get a SecurityNamespace object. As the documentation says, this object is used for managing (query, get, remove) permissions by utilizing AcessControlList objects. Since we are interested in managing our teams permissions, we use the identities namespace. This is because the Team Foundation Team is wrapped around a TFS security group, which is in turn a TFS identity.

Now comes a slight twist. If you’ve had a look at the MSDN documentation for SecurityNamespace, you’ll notice that almost all methods require a token to be passed in. This token can be retrieved from the team identifier, so it strikes me as a bit odd that the token is required throughout the API, as opposed to either the team identity or the identifier (the SID). There is even a IdentityHelper class available (and it’s public), which contains a useful static method called CreateSecurityToken. This method is not public (that’s why you won’t see it in the MSDN documentation), but it’s very easy to get a hold of it using some good old reflection (which is awesome). So, we retrieve the method and invoke it, passing it the team identity and acquire the security token this way.

In the next step, we retrieve all the available team members, while ignoring any other security groups which may also be contained in the team. Again, for more details about querying a team for it’s members, have a look at this post. We then retrieve an ACL for the team, which will contain a single AccessControlEntry object for each team member. From what I could see using Reflector, it seems that any user which has the lowest four bits set in the Allow property of it’s ACL entry is in fact a team administrator. This is why we perform the “detection” by logically AND-ing the Allow value with the number 15. Complicated stuff, right?

Retrieving the actual TeamFoundationIdentity objects (ie. the team member objects) from the identifiers is then just icing on the cake.

Adding and removing Team Administrators

Once you have all the code from the first snippet set up, adding or removing a team administrator i fairly easy. We can simply use the SetPermissions() or RemovePermissions() methods of the SecurityNamespace object. Here’s a short example of how to add a new team administrator:

IdentityDescriptor descriptor = GetMemberDescriptor(memberId);
securityNamespace.SetPermissions(token, descriptor, 15, 0, false);

The first parameter is the security token, the second one is a descriptor that specifies an identity you want to set permissions for. The third and fourth parameters are the values of the Allow and Deny flags. Since we want to make the specified identity a team administrator we set these values to 15 (team admin) and 0 (deny none), respectively. The final parameter flags if the permissions should be set using the merge mode. If set to true, all the permissions in both the Allow and Deny fields will be merged with already existing values (similarly to logically OR-ing the flags). Since we don’t really care about the other flags, it doesn’t matter which value we use for this flag.

And finally, to remove an identity from the team administrators list, use something like this:

IdentityDescriptor descriptor = GetMemberDescriptor(memberId);
securityNamespace.RemovePermissions(token, descriptor, 15);

Once again, really simple stuff. We just need to pass in the security token, the target identity descriptor and the permissions we want to remove from the specified identity.

Conclusion

You have reached the end of today’s post. If you’re interested in reading more about the new TFS 11 API, please feel free to check out the other posts from the series. As always, your comments and suggestions a more than welcome!

You can download the working source code from the post here.

Be Sociable, Share!
  • Flo

    Hi,

    thank you very much for this blog post.

    I tried to do exactly what your doing here, but it doesn’t work. I can’t get the method info for the “CreateSecurityToken”-methode. The variable “mi” is always null in my code.
    Any idea why?

    In addition do you know any quick way just to check if an user is admin on my project or not?

    Best regards
    Flo