protected static void ProcessCollection(TfsTeamProjectCollection collection) { Console.WriteLine(String.Format("Processing collection {0}", collection.DisplayName)); Console.WriteLine(String.Empty); // Get Core, security, and identity services ISecurityService securityService = collection.GetService <ISecurityService>(); SecurityNamespace hooksSecurity = securityService.GetSecurityNamespace(ServiceHooksSecurityNamespaceId); IIdentityManagementService2 identityService = collection.GetService <IIdentityManagementService2>(); ProjectHttpClient projectClient = collection.GetClient <ProjectHttpClient>(); IEnumerable <TeamProjectReference> projects = projectClient.GetProjects(stateFilter: Microsoft.TeamFoundation.Common.ProjectState.WellFormed).Result; // Iterate over each project, check SH permissions, and grant if needed foreach (var project in projects) { Console.WriteLine(String.Format("Project {0} ({1})", project.Name, project.Id)); var groups = identityService.ListApplicationGroups(project.Id.ToString(), ReadIdentityOptions.None, null, Microsoft.TeamFoundation.Framework.Common.IdentityPropertyScope.Both); String adminGroupName = String.Format("vstfs:///Classification/TeamProject/{0}\\Project Administrators", project.Id); try { TeamFoundationIdentity adminGroup = groups.First(g => String.Equals(g.UniqueName, adminGroupName, StringComparison.InvariantCultureIgnoreCase)); Console.WriteLine(" - Checking Project Administrators group permissions"); AccessControlEntry ace = new AccessControlEntry(adminGroup.Descriptor, 7, 0); // 7 = view, create, delete String securityToken = "PublisherSecurity/" + project.Id; bool hasPermission = hooksSecurity.HasPermission(securityToken, adminGroup.Descriptor, 7, false); if (!hasPermission) { Console.WriteLine(" - Missing. Granting..."); hooksSecurity.SetAccessControlEntry(securityToken, ace, true); // check permission again after granting hasPermission = hooksSecurity.HasPermission(securityToken, adminGroup.Descriptor, 7, false); if (hasPermission) { Console.WriteLine(" - Granted"); } else { Console.WriteLine(" - Still does not have permission. Check to make sure it has not been explicitly denied."); } } else { Console.WriteLine(" - Already has permission"); } } catch (Exception ex) { Console.WriteLine(String.Format("Admin group: Not found! ({0})", ex.Message)); } Console.WriteLine(""); } }
public static object Run(ExportADGroupsOptions opts, string logPath) { Telemetry.Current.TrackEvent("Run-ExportADGroupsCommand"); string exportPath = CreateExportPath(logPath, "ExportADGroups"); Trace.Listeners.Add(new TextWriterTraceListener(Path.Combine(exportPath, "ExportADGroups.log"), "ExportADGroupsCommand")); Stopwatch stopwatch = Stopwatch.StartNew(); ////////////////////////////////////////////////// StreamWriter sw = File.CreateText(Path.Combine(exportPath, "AzureADGroups.csv")); sw.AutoFlush = true; using (var csv = new CsvWriter(sw)) { csv.WriteHeader <AzureAdGroupItem>(); TfsTeamProjectCollection sourceCollection = new TfsTeamProjectCollection(opts.CollectionURL); sourceCollection.EnsureAuthenticated(); IIdentityManagementService2 sourceIMS2 = (IIdentityManagementService2)sourceCollection.GetService(typeof(IIdentityManagementService2)); List <CatalogNode> sourceTeamProjects = sourceCollection.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.TeamProject }, false, CatalogQueryOptions.None).ToList(); if (opts.TeamProject != null) { sourceTeamProjects = sourceTeamProjects.Where(x => x.Resource.DisplayName == opts.TeamProject).ToList(); } int current = sourceTeamProjects.Count(); foreach (CatalogNode sourceTeamProject in sourceTeamProjects) { Trace.WriteLine(string.Format("---------------{0}\\{1}", current, sourceTeamProjects.Count())); Trace.WriteLine(string.Format("{0}, {1}", sourceTeamProject.Resource.DisplayName, sourceTeamProject.Resource.Identifier)); string projectUri = sourceTeamProject.Resource.Properties["ProjectUri"]; TeamFoundationIdentity[] appGroups = sourceIMS2.ListApplicationGroups(projectUri, ReadIdentityOptions.None); foreach (TeamFoundationIdentity appGroup in appGroups.Where(x => !x.DisplayName.EndsWith("\\Project Valid Users"))) { Trace.WriteLine(string.Format(" {0}", appGroup.DisplayName)); TeamFoundationIdentity sourceAppGroup = sourceIMS2.ReadIdentity(appGroup.Descriptor, MembershipQuery.Expanded, ReadIdentityOptions.None); foreach (IdentityDescriptor child in sourceAppGroup.Members.Where(x => x.IdentityType == "Microsoft.TeamFoundation.Identity")) { TeamFoundationIdentity sourceChildIdentity = sourceIMS2.ReadIdentity(IdentitySearchFactor.Identifier, child.Identifier, MembershipQuery.None, ReadIdentityOptions.ExtendedProperties); if ((string)sourceChildIdentity.GetProperty("SpecialType") == "AzureActiveDirectoryApplicationGroup") { Trace.WriteLine(string.Format(" Suspected AD Group {0}", sourceChildIdentity.DisplayName)); csv.WriteRecord <AzureAdGroupItem>(new AzureAdGroupItem { TeamProject = sourceTeamProject.Resource.DisplayName, ApplciationGroup = sourceTeamProject.Resource.DisplayName, Account = (string)sourceChildIdentity.GetProperty("Account"), Mail = (string)sourceChildIdentity.GetProperty("Mail"), DirectoryAlias = (string)sourceChildIdentity.GetProperty("DirectoryAlias") }); } } } current--; sw.Flush(); } } sw.Close(); // current--; //} ////////////////////////////////////////////////// stopwatch.Stop(); Trace.WriteLine(string.Format(@"DONE in {0:%h} hours {0:%m} minutes {0:s\:fff} seconds", stopwatch.Elapsed)); Trace.Listeners.Remove("ExportADGroupsCommand"); return(0); }
private static readonly string SpecialGroupName = "Service Hooks Administrators"; // assumed to be a collection-level group containing people that will have management permissions for SH in each project public void Run(Uri collectionUri) { Console.WriteLine("Utility to remove Service Hooks management permissions from the Project Administrators groups."); Console.WriteLine(""); Console.WriteLine(" All projects in account/collection: " + collectionUri); Console.WriteLine(""); Console.WriteLine("WARNING! This operation will remove the permissions.\n\n Are you sure you want to continue (Y/N)?"); int confirmChar = Console.In.Read(); if (confirmChar != 'y' || confirmChar != 'Y') { return; } if (collectionUri != null) { TfsTeamProjectCollection connection = new TfsTeamProjectCollection(collectionUri); // Get Core, security, and identity services ISecurityService securityService = connection.GetService <ISecurityService>(); SecurityNamespace hooksSecurity = securityService.GetSecurityNamespace(ServiceHooksSecurityNamespaceId); IIdentityManagementService2 identityService = connection.GetService <IIdentityManagementService2>(); ProjectHttpClient projectClient = connection.GetClient <ProjectHttpClient>(); IEnumerable <TeamProjectReference> projects = projectClient.GetProjects(stateFilter: Microsoft.TeamFoundation.Common.ProjectState.WellFormed).Result; // Iterate over each project, check SH permissions, and remove if the project administrators group has access foreach (var project in projects) { // Remove manage permissions from the project's administrators group (but leave it "view" access) Console.WriteLine(String.Format("Project {0} ({1})", project.Name, project.Id)); var groups = identityService.ListApplicationGroups(project.Id.ToString(), ReadIdentityOptions.None, null, Microsoft.TeamFoundation.Framework.Common.IdentityPropertyScope.Both); String adminGroupName = String.Format("vstfs:///Classification/TeamProject/{0}\\Project Administrators", project.Id); try { TeamFoundationIdentity adminGroup = groups.First(g => String.Equals(g.UniqueName, adminGroupName, StringComparison.InvariantCultureIgnoreCase)); Console.WriteLine(" - Checking Project Administrators group permissions"); String securityToken = "PublisherSecurity/" + project.Id; bool hasPermission = hooksSecurity.HasPermission(securityToken, adminGroup.Descriptor, ManagePermissions, false); // Project admin group has "manage" permissions for SH in the project if (hasPermission) { // Remove manage permissions from the project's administrators group (but leave it "view" access) Console.WriteLine(" - Has permissions. Removing..."); // Give the admin group only view permissions hooksSecurity.SetPermissions(securityToken, adminGroup.Descriptor, ViewPermissions, 0, false); // check permission again after granting hasPermission = hooksSecurity.HasPermission(securityToken, adminGroup.Descriptor, ManagePermissions, false); if (!hasPermission) { Console.WriteLine(" - Verified permissions correctly removed."); } else { Console.WriteLine(" - Project Administrators Group still has manage permissions."); } } else { Console.WriteLine(" - Does not have permissions to manage service hook subscriptions."); } } catch (Exception ex) { Console.WriteLine(String.Format("Admin group: Not found! ({0})", ex.Message)); } Console.WriteLine(""); } // Grant the group manage permissions across the entire collection TeamFoundationIdentity specialGroup = identityService.ReadIdentity(IdentitySearchFactor.DisplayName, SpecialGroupName, MembershipQuery.None, ReadIdentityOptions.None); if (specialGroup != null) { Console.WriteLine("Granting full manage permissions to: {0}", specialGroup.UniqueName); String rootSecurityToken = "PublisherSecurity/"; hooksSecurity.SetPermissions(rootSecurityToken, specialGroup.Descriptor, ManagePermissions, 0, false); } else { Console.WriteLine("Could not find this group."); } } }
public override int RunInternal(ExportAzureADOptions opts) { opts.OutPath = opts.OutPath ?? this.LogPathRoot; StreamWriter sw = File.CreateText(Path.Combine(opts.OutPath, "IdentityList.csv")); sw.AutoFlush = true; using (var csv = new CsvWriter(sw)) { csv.WriteHeader <AzureAdGroupItem>(); TfsTeamProjectCollection sourceCollection = new TfsTeamProjectCollection(opts.CollectionURL); sourceCollection.EnsureAuthenticated(); IIdentityManagementService2 sourceIMS2 = (IIdentityManagementService2)sourceCollection.GetService(typeof(IIdentityManagementService2)); List <CatalogNode> sourceTeamProjects = sourceCollection.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.TeamProject }, false, CatalogQueryOptions.None).ToList(); if (opts.TeamProject != null) { sourceTeamProjects = sourceTeamProjects.Where(x => x.Resource.DisplayName == opts.TeamProject).ToList(); } int current = sourceTeamProjects.Count(); foreach (CatalogNode sourceTeamProject in sourceTeamProjects) { Trace.WriteLine(string.Format("---------------{0}\\{1}", current, sourceTeamProjects.Count())); Trace.WriteLine(string.Format("{0}, {1}", sourceTeamProject.Resource.DisplayName, sourceTeamProject.Resource.Identifier)); string projectUri = sourceTeamProject.Resource.Properties["ProjectUri"]; TeamFoundationIdentity[] appGroups = sourceIMS2.ListApplicationGroups(projectUri, ReadIdentityOptions.None); foreach (TeamFoundationIdentity appGroup in appGroups.Where(x => !x.DisplayName.EndsWith("\\Project Valid Users"))) { Trace.WriteLine(string.Format(" {0}", appGroup.DisplayName)); TeamFoundationIdentity sourceAppGroup = sourceIMS2.ReadIdentity(appGroup.Descriptor, MembershipQuery.Expanded, ReadIdentityOptions.None); foreach (IdentityDescriptor child in sourceAppGroup.Members.Where(x => x.IdentityType == "Microsoft.TeamFoundation.Identity" || x.IdentityType == "Microsoft.IdentityModel.Claims.ClaimsIdentity")) { TeamFoundationIdentity sourceChildIdentity = sourceIMS2.ReadIdentity(IdentitySearchFactor.Identifier, child.Identifier, MembershipQuery.None, ReadIdentityOptions.ExtendedProperties); var SpecialType = (string)sourceChildIdentity.GetProperty("SpecialType"); var Account = (string)sourceChildIdentity.GetProperty("Account"); object DirectoryAlias; object Mail; sourceChildIdentity.TryGetProperty("DirectoryAlias", out DirectoryAlias); sourceChildIdentity.TryGetProperty("Mail", out Mail); switch (SpecialType) { case "AzureActiveDirectoryApplicationGroup": Trace.WriteLine(string.Format(" Found AD Group {0}", sourceChildIdentity.DisplayName)); csv.WriteRecord <AzureAdGroupItem>(new AzureAdGroupItem { TeamProject = sourceTeamProject.Resource.DisplayName, ApplciationGroup = appGroup.DisplayName, Account = Account, Mail = (string)Mail, DirectoryAlias = (string)DirectoryAlias }); break; case "Generic": if (sourceChildIdentity.IsContainer) { Trace.WriteLine(string.Format("Skipping {0} | {1} - TF GROUP", SpecialType, Account)); } else { Trace.WriteLine(string.Format(" Found AD User {0}", sourceChildIdentity.DisplayName)); csv.WriteRecord <AzureAdGroupItem>(new AzureAdGroupItem { TeamProject = sourceTeamProject.Resource.DisplayName, ApplciationGroup = appGroup.DisplayName, Account = Account, Mail = (string)Mail, DirectoryAlias = (string)DirectoryAlias }); } break; default: Trace.WriteLine(string.Format("Skipping {0} | {1} - UNKNOWN", SpecialType, Account)); break; } } } current--; sw.Flush(); } } sw.Close(); return(0); }