In TFS 2005 and 2008, workspaces in version control have the following limitations.
When we speak of “using” a workspace, that covers any of the following sorts of operations:
We lifted both of these limitations in TFS 2010. Adding the ability to change the owner of a workspace was not difficult, but allowing workspaces to be used by more than just their owner required us to make workspaces fully securable objects. We leveraged the new security service to make these changes and settled on four permissions that a user can have for a workspace. Those permissions are: Read, Use, CheckIn, and Administer.
TFS 2010 does not ship with a full UI for manipulating workspace permissions. Users are limited to choosing between three "permission profiles" for their workspaces; a permission profile is essentially a template for the workspace's access control list. The default permission profile is "Private workspace". A private workspace has the same effective behavior as a workspace in TFS 2005 and 2008: the workspace can only be used by its owner. The access control list for a private workspace has only one entry, granting all permissions to the owner -- for example, John Smith:
YOURDOMAIN\johnsmith: Read, Use, CheckIn, Administer
The two other permission profiles we ship are "Public-limited" and "Public". These two profiles continue to grant all permissions to the owner. They additionally grant any valid user additional permissions. For public-limited, other users are granted the Read and Use permissions on the workspace. For a fully public workspace, every valid user of the team project collection has the same permissions as the owner: Read, Use, CheckIn, Administer.
You'll need to log onto the machine which has the public workspace. After starting Visual Studio 2010 and connecting to the server which has a public workspace, you'll be able to see the workspace in the appropriate drop-down combo boxes in the Source Control Explorer and Pending Changes toolwindows.
Users of Visual Studio 2008 and earlier will not be able to see public workspaces belonging to other users. They will continue to see only their own workspaces.
In this example, I made the shared workspace "Public-limited." You'll see that because I lack the Administer permission for this workspace, I can only view the workspace's mappings, owner, comment, and permissions profile. The controls are read-only.
Again, you'll need to log onto the machine which has the public workspace. Once you've started a Visual Studio 2010 command prompt, cd to a mapped path for the workspace. In my case, this is D:\Proj. You can see below that I tried to run a command, but it failed to determine the workspace. This is because the local workspace cache file is per-user and this user has never heard of the workspace in question. I can either:
I chose to run “tf workspaces”. We can see that the workspace belonging to the other user is displayed, confirming that I have access to the public workspace. Now my "tf get" command succeeds.
If user B shelves changes out of user A's workspace, user B is the owner of the shelveset that was created. Likewise, if user B checks in changes from user A's workspace, user B is recorded as the user who committed the changes.
Individual pending changes do not have owners -- only workspaces have owners. If user A checks out file.txt for edit in user B's workspace, some UI components may state that "file.txt is opened for edit by user B," even though user A is the one who pended the change. It would be more accurate to state that "file.txt is opened for edit in workspace X, which is owned by user B."
The TFS 2005/2008 error message "TF14091: You cannot perform this operation on workspace {0} because you are not the owner of the workspace." is no longer issued by TFS 2010. Instead, you will receive the following message indicating which workspace permission the operation demanded:
TF204017: The operation cannot be completed because the user ({0}) does not have one or more required permissions ({1}) for workspace {2}.
In the example below, the workspace was switched back to "Private" just before the command was issued.
All versions of TFS from 2005 through 2010 have a global privilege called "Administer Workspaces," or AdminWorkspaces for short. By default this privilege is granted to team project collection administrators. Users possessing the AdminWorkspaces privilege automatically get the Administer permission on all workspaces in the system, even if they ordinarily would not receive it. This enables administrators to purge old workspaces in the system, and take ownership of workspaces belonging to employees on vacation, or contractors who are no longer with the team. Users with the AdminWorkspaces privilege can also create workspaces on behalf of other users.
If I am granted the AdminWorkspaces privilege and go back to the earlier example, where I was locked out of the Edit Workspace dialog because the workspace was "Public-limited", we can see that I now have full access. My effective permissions are "Read, Use, Administer". If I want the CheckIn permission, I could grant it to myself by changing the permission profile of the workspace to "Public".
While the UI restricts you to the three in-box permission templates, the server has full support for custom access control lists on workspaces. You can query the full access control list by using the security service and the version control service together. The GUID of the security namespace for workspaces is a well-known constant, and the version control client object model has the token in that namespace for any given workspace, accessible through the Workspace.SecurityToken property. The following code sample shows how to extract and display the access control list for the workspace inferred by the current directory.
(This code sample needs assembly references to MS.TeamFoundation.Common, MS.TeamFoundation.Client, MS.TeamFoundation.VersionControl.Common, and MS.TeamFoundation.VersionControl.Client.)
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.VersionControl.Common;
using Microsoft.TeamFoundation.VersionControl.Client;
…
// Use the current directory to infer the workspace and TFS team project collection.
WorkspaceInfo wi = Workstation.Current.GetLocalWorkspaceInfo(Environment.CurrentDirectory);
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(wi.ServerUri);
VersionControlServer vcs = tpc.GetService<VersionControlServer>();
Workspace workspace = vcs.GetWorkspace(wi);
// Get the identity service for the TPC.
IIdentityManagementService ims = tpc.GetService<IIdentityManagementService>();
// Get the security service for the TPC and use it to get the workspace security namespace.
ISecurityService security = tpc.GetService<ISecurityService>();
SecurityNamespace workspaceSecurityNamespace =
security.GetSecurityNamespace(SecurityConstants.WorkspaceSecurityNamespaceGuid);
// Get the access control list for the workspace, using the workspace's security token in the namespace.
AccessControlList acl = workspaceSecurityNamespace.QueryAccessControlList(workspace.SecurityToken,
null, false);
// Get the full TeamFoundationIdentity objects for the IdentityDescriptor of each access control entry.
List<IdentityDescriptor> descriptors = new List<IdentityDescriptor>();
foreach (AccessControlEntry ace in acl.AccessControlEntries)
descriptors.Add(ace.Descriptor);
TeamFoundationIdentity[] identities = ims.ReadIdentities(descriptors.ToArray(), MembershipQuery.None,
ReadIdentityOptions.None);
// Display the access control list to the user.
Console.WriteLine("Access control list for " + workspace.DisplayName + Environment.NewLine);
int i = 0;
foreach (AccessControlEntry ace in acl.AccessControlEntries)
{
if (null == identities[i])
Console.WriteLine(" " + ace.Descriptor.Identifier + ":");
else
Console.WriteLine(" " + IdentityHelper.GetDomainUserName(identities[i]) + ":");
if (0 != ace.Allow)
Console.WriteLine(" Allow: " + ((WorkspacePermissions)ace.Allow).ToString());
if (0 != ace.Deny)
Console.WriteLine(" Deny: " + ((WorkspacePermissions)ace.Deny).ToString());
i++;
}
Going back to our public-limited workspace example, we can now use this code to visualize the full access control list for the workspace.
We can take the above code fragment one step further and manipulate the access control list. Let’s remove the access control entry for [Collection0]\Project Collection Valid Users and replace it with an entry that explicitly gives full permissions only to REDMOND\vseqa1. Performing this operation requires Administer permission on the workspace, either explicitly or through the AdminWorkspaces global privilege.
// Use the current directory to infer the workspace and TFS team project collection.
WorkspaceInfo wi = Workstation.Current.GetLocalWorkspaceInfo(Environment.CurrentDirectory);
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(wi.ServerUri);
VersionControlServer vcs = tpc.GetService<VersionControlServer>();
Workspace workspace = vcs.GetWorkspace(wi);
// Get the security service for the TPC and use it to get the workspace security namespace.
ISecurityService security = tpc.GetService<ISecurityService>();
SecurityNamespace workspaceSecurityNamespace =
security.GetSecurityNamespace(SecurityConstants.WorkspaceSecurityNamespaceGuid);
// Get the access control list for the workspace, using the workspace's security token in the namespace.
AccessControlList acl = workspaceSecurityNamespace.QueryAccessControlList(workspace.SecurityToken,
null, false);
WorkspacePermissions allWorkspacePermissions = WorkspacePermissions.Read | WorkspacePermissions.Use |
WorkspacePermissions.CheckIn | WorkspacePermissions.Administer;
acl.RemoveAccessControlEntry(new IdentityDescriptor(IdentityConstants.TeamFoundationType,
GroupWellKnownSidConstants.EveryoneGroupSid));
acl.SetAccessControlEntry(new AccessControlEntry(tpc.AuthorizedIdentity.Descriptor,
(int)allWorkspacePermissions, 0), false);
workspaceSecurityNamespace.SetAccessControlList(acl);
After running this code I can see that the workspace access control list now has two entries – one for the workspace owner and one for me (REDMOND\vseqa1).
Because the workspace’s access control list no longer matches one of the predefined permission profiles (templates), the permissions profile in the Edit Workspace is shown as “Custom permissions”.
If you leave the workspace permissions combo box set to “Custom permissions,” then you can change any other property of the workspace without overwriting your customized access control list. But if you later want to go back to one of the pre-defined permission profiles, just select it from the combo box and hit OK. Your custom access control list will be wiped and replaced.
Version control permission checks will still succeed, even if the owner ACE does not exist in the security service’s access control list. The version control service still knows that you are the owner of the workspace and therefore have full permissions. You will be locked out of making changes to the access control list through the security service until the owner ACE is restored.
To restore the owner ACE, open the Edit Workspace dialog and make any change (change the comment, for example). When you click OK and the workspace is updated on the server, the owner ACE will be restored.