Like the title says, i need to get the members of a group from my Active directory.
Code:
using(var p_con = new PrincipalContext(ContextType.Machine))
{
var grps = GroupPrincipal.FindByIdentity(p_con, IdentityType.Sid, "S-1-5-21-205523278-2745993604-4001200492-1027");
var users = grps.GetMembers();
}
But my code throws the follwing error in the Membersproperty of the 'grps' var.
Members = 'grps.Members' threw an exception of type
'System.TypeLoadException'
If i try it the other way, searching for the groups of a member, i get the same error.
using (var p_con = new PrincipalContext(ContextType.Machine))
{
var up = new UserPrincipal(p_con);
using (var search = new PrincipalSearcher(up))
{
foreach (var user in search.FindAll())
{
var _grp = user.GetGroups();
}
}
}
The group/user it self is correctly loaded except the Users\Groups.
Am i missing something in the setup?
I am using ASP.NET Core 2 and the current Windows.Compatibility Pack (which includes the current verion of the DirectoryServices).
The authentication runs via Http.sys
Related
I'm switching an API from .NET Framework to .NET Core and running into some differences in the behavior of Directory.Services.AccountManagement vs System.IO.FileSystem.AccessControl in .NET Core 3.1.
The issue in the .NET Core code is I'm trying to get the Active Directory group name (or user name) for each rule for a file. I can get the sid, but the FileSystemAccessRule.IdentifyReference.Translate() method throws an exception for AD groups.
Here's what I have: I can get the SID in the id variable, but I need the actual group name (or user name)
using System.Security.AccessControl;
using System.Security.Principal;
namespace ConsoleCoreAclsPoc
{
class Program
{
static void Main(string[] args)
{
var fn = args[0];
var rules = new FileSecurity(fn, AccessControlSections.All | AccessControlSections.Access)
.GetAccessRules(true,true,typeof(NTAccount));
foreach (AuthorizationRule rule in rules)
{
FileSystemAccessRule fileRule = rule as FileSystemAccessRule;
if (fileRule != null)
{
var id = fileRule.IdentityReference;//.Translate(typeof(NTAccount));
var read = fileRule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute);
var write = fileRule.FileSystemRights.HasFlag(FileSystemRights.Modify);
var admin = fileRule.FileSystemRights.HasFlag(FileSystemRights.FullControl);
if (!admin)
{
// do stuff to the non-admins
}
}
}
}
}
}
I tested your code and found an exception in Access Control Sections. The compiler does not allow access to all security When you want to access the permissions of a file. Change AccessControlSections to AccessControlSections.Access. Then I did not find any error.
var rules = new FileSecurity(fn, AccessControlSections.Access)
.GetAccessRules(true, true, typeof(NTAccount));
I am a writing a .Net application using the VSTS/TFS Rest .Net libraries and in one place I need to update workitems' System.AssignedTo field values and while I do want to adhere to the new(ish), unique displayname rules for identity work item fields, I have a hard time finding a method to get the Unique display name(s) for given identities.
The old / client object model does have an explicit helper method to get these unique names, but I have not found any rest endpoint nor client api method that would provide the same.
So I am wondering, given a list of identities, how do I get their corresponding unique display names which I can use to unambiguously set identity work item fields?
String collectionUri = "http://collectionurl/";
VssCredentials creds = new VssClientCredentials();
creds.Storage = new VssClientCredentialStorage();
VssConnection connection = new VssConnection(new Uri(collectionUri), creds);
TeamHttpClient thc = connection.GetClient<TeamHttpClient>();
List<IdentityRef> irs = thc.GetTeamMembersAsync("ProjectName","TeamName").Result;
foreach (IdentityRef ir in irs)
{
Console.WriteLine(ir.UniqueName);
Console.WriteLine(ir.DisplayName);
}
You could try the code below to get unique name:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
TfsConfigurationServer tcs = new TfsConfigurationServer(new Uri("http://tfsserver:8080/tfs"));
IIdentityManagementService ims = tcs.GetService<IIdentityManagementService>();
TeamFoundationIdentity tfi = ims.ReadIdentity(IdentitySearchFactor.AccountName, "[TEAM FOUNDATION]\\Team Foundation Valid Users", MembershipQuery.Expanded, ReadIdentityOptions.None);
TeamFoundationIdentity[] ids = ims.ReadIdentities(tfi.Members, MembershipQuery.None, ReadIdentityOptions.None);
foreach (TeamFoundationIdentity id in ids)
{
if (id.Descriptor.IdentityType == "System.Security.Principal.WindowsIdentity")
{
Console.WriteLine(id.DisplayName);
Console.WriteLine(id.UniqueName);
}
}
Console.ReadLine();
}
}
}
foreach (var workItem in workItems)
{
if (workItem.Fields.ContainsKey("System.AssignedTo"))
{
var person = (IdentityRef)workItem.Fields["System.AssignedTo"];
string codereview_reviewer = person.DisplayName;
Console.WriteLine(codereview_reviewer);
}
}
There was this approach in previous versions -
var terms = new GetTermsOperation("Raven/DocumentsByEntityName", "Tag", "", 1024);
But now it doesn't work. I tried to use another command:
var op = new GetCollectionStatisticsOperation();
var collectionStats = store.Maintenance.Send(op);
But it throws an error - System.ArgumentNullException: 'Value cannot be null.
Parameter name: key'
Then i found out how to get the all collections from the browser admin panel:
from #all_docs select distinct #metadata.#collection
How to translate that snippet to c# code?
If you don't have a database assigned at the document store level, you need to specify it explicitly, like so:
var collectionStats = store.Maintenance.ForDatabase("db-name").Send(op);
I found a clue - my DocumentStore variable didn't had an assigned Database ( it was assigned in OpenSession constructor):
//Wrong variant
IDocumentStore store = new DocumentStore()
{
Urls = new string[] { Host }, /*Database = "testdb"*/
}
using (IDocumentSession session = store.OpenSession(dbName))
{
//some code
}
//Good variant
IDocumentStore store = new DocumentStore()
{
Urls = new string[] { Host }, Database = "testdb"
}
using (IDocumentSession session = store.OpenSession())
{
//some code
}
I am trying to programmatically add a parent-child relationship between two work items. I am using the Microsoft Team Foundation and Visual Studio Services libraries to export and import TFS 2015 and VSTS backlog objects.
https://learn.microsoft.com/en-us/vsts/integrate/concepts/dotnet-client-libraries
https://www.visualstudio.com/en-us/docs/integrate/api/wit/samples#migrating-work-items
I have worked through obtaining a VssConnection to my servers and getting a WorkItemTrackingHttpClient to execute Wiql queries and create work items. I also have a query to identify the parent of a target work item.
What I cannot figure out is how to add the link between child work items and their parent. I do not know the correct JsonPatchDocument item path to add the parent, or the correct property or method on an existing WorkItem to update it with a parent link. Does anyone have documentation links or specific information on adding a parent relationship to a work item using these libraries?
Here are some code excerpts for context:
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
// ...
var sourceConnection = new VssConnection(new Uri(_sourceTsUrl), new VssClientCredentials());
var targetConnection = new VssConnection(new Uri(_targetTsUrl), new VssClientCredentials());
var sourceClient = sourceConnection.GetClient<WorkItemTrackingHttpClient>();
var targetClient = targetConnection.GetClient<WorkItemTrackingHttpClient>();
// ...
var queryResults = sourceClient.QueryByWiqlAsync(query).Result;
var ids = queryResults.WorkItems.Select(x => x.Id).ToList();
var items = sourceClient.GetWorkItemsAsync(ids);
foreach (var item in items.Result)
{
// ...
var patchItem = new JsonPatchDocument();
foreach (var fieldName in item.Fields.Keys)
{ patchItem.Add(new JsonPatchOperation() { Path = $"/fields/{fieldName}", Value = item.Fields[fieldName], Operation = Operation.Add }); }
// TODO - add patch field(?) for parent relationship
var parentResults = sourceClient.QueryByWiqlAsync(parentQuery).Result;
// ...
var task = targetClient.CreateWorkItemAsync(patchItem, targetProject, itemType, validateOnly, bypassRules, suppressNotifications);
var newItem = task.Result;
// TODO - alternatively, add parent via the returned newly generated WorkItem
}
Addendum:
I've tried adding the following code, but the changes do not get committed to the remote object, it only exists in local memory, and I cannot find a method to push the changes/updates.
if (!string.IsNullOrWhiteSpace(mappedParentUrl))
{
if (newItem.Relations == null)
{ newItem.Relations = new List<WorkItemRelation>(); }
newItem.Relations.Add(new WorkItemRelation() { Rel = "Parent", Title = mappedParentTitle, Url = mappedParentUrl });
}
Refer to this code to create task work item with parent link (Update it to meet your requirement):
var url = new Uri("https://XXX.visualstudio.com");
var connection = new VssConnection(url, new VssClientCredentials());
var workitemClient = connection.GetClient<WorkItemTrackingHttpClient>();
string projectName = "[project name]";
int parentWITId = 771;//parent work item id
var patchDocument = new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchDocument();
patchDocument.Add(new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation() {
Operation=Operation.Add,
Path= "/fields/System.Title",
Value="parentandchildWIT"
});
patchDocument.Add(new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/relations/-",
Value = new
{
rel = "System.LinkTypes.Hierarchy-Reverse",
url = connection.Uri.AbsoluteUri+ projectName+ "/_apis/wit/workItems/"+parentWITId,
attributes = new
{
comment = "link parent WIT"
}
}
});
var createResult = workitemClient.CreateWorkItemAsync(patchDocument, projectName, "task").Result;
This question already has answers here:
UserPrincipals.GetAuthorizationGroups An error (1301) occurred while enumerating the groups. After upgrading to Server 2012 Domain Controller
(10 answers)
Closed 8 years ago.
Background:
I've been using UserPrincipal.GetAuthorizationGroups for a while now to check permissions in 2 different applications. They have been working fine for several years. Recently some users have been getting the error mentioned in the title (System.DirectoryServices.AccountManagement.PrincipalOperationException) while others have not. I have a suspicion that it might be related to a new domain controller that was added running on Windows Server 2012 because the problems started the day after it was added. The full error is listed below:
Exception:
System.DirectoryServices.AccountManagement.PrincipalOperationException:
An error (1301) occurred while enumerating the groups. The group's
SID could not be resolved.
at System.DirectoryServices.AccountManagement.SidList.TranslateSids(String target, IntPtr[] pSids)
at System.DirectoryServices.AccountManagement.SidList..ctor(SID_AND_ATTR[] sidAndAttr)
at System.DirectoryServices.AccountManagement.AuthZSet..ctor(Byte[] userSid, NetCred credentials,
ContextOptions contextOptions, String flatUserAuthority, StoreCtx userStoreCtx, Object userCtxBase)
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ...p)
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups
Question:
How do I fix this?
I've found an alternative using DirectorySearcher:
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
DirectorySearcher searcher = new DirectorySearcher(
new DirectoryEntry("LDAP://" + domain.Name));
searcher.Filter = String.Format(
"(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{0}*)))",
"Current User Login Name");
return searcher;
}
);
var directoryEntriesFound =
allSearcher.SelectMany(searcher =>
searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var memberOf = directoryEntriesFound.Select(entry =>
{
using (entry)
{
return new
{
Name = entry.Name,
GroupName = ((object[])entry.Properties["MemberOf"].Value)
.Select(obj => obj.ToString())
};
}
}
);
foreach (var user in memberOf)
{
foreach (var groupName in user.GroupName)
{
if (groupName.Contains("Group to Find"))
{
// Do something if the user is in that group
}
}
}
check this answer:
UserPrincipals.GetAuthorizationGroups An error (1301) occurred while enumerating the groups. After upgrading to Server 2012 Domain Controller
MS fix http://support.microsoft.com/kb/2830145
I had same exception. If someone don't wanna used "LDAP", use this code. Cause I'm had nested groups, I'm used GetMembers(true) and it's little bit longer in time than GetMembers().
or download fix from here like said #Tilo: http://support.microsoft.com/kb/2830145
public bool IsMember(UserPrincipal user, string groupName)
{
try
{
var context = new PrincipalContext(ContextType.Domain, Environment.UserDomainName);
var group = GroupPrincipal.FindByIdentity(context, groupName);
if (group == null)
{
//Not exist
}
else
{
if (group.GetMembers(true).Any(member => user.SamAccountName.ToLower() == member.SamAccountName.ToLower()))
{
return true;
}
}
}
catch (Exception exception)
{
//exception
}
return false;
}