Este equipo de redactores ha estado mucho tiempo buscando para darle solución a tu pregunta, te dejamos la resolución por eso nuestro deseo es serte de mucha ayuda.
Solución:
Parece que no hay forma de convertir el formato de nombre de usuario sin involucrar una consulta a Active Directory. Dado que ese es el caso, no es necesario crear WindowsPrincipal
para verificar la pertenencia al grupo, ya que probablemente necesitaría otra conexión a AD.
Usando el System.DirectoryServices.AccountManagement
espacio de nombres puede obtener el UPN del usuario y comprobar la pertenencia al grupo.
string accountName = @"DOMAINuser";
var groupNames = new[] "DOMAINDomain Users", "DOMAINGroup2" ; // the groups that we need to verify if the user is member of
// cannot create WindowsIdentity because it requires username in form [email protected] but the passed value will be DOMAINuser.
using (var pc = new PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain, Environment.UserDomainName))
using (var p = UserPrincipal.FindByIdentity(pc, accountName))
// if the account does not exist or is not an user account
if (p == null)
return new string[0];
// if you need just the UPN of the user, you can use this
////return p.UserPrincipalName;
// find all groups the user is member of (the check is recursive).
// Guid != null check is intended to remove all built-in objects that are not really AD gorups.
// the Sid.Translate method gets the DOMAINGroup name format.
var userIsMemberOf = p.GetAuthorizationGroups().Where(o => o.Guid != null).Select(o => o.Sid.Translate(typeof(NTAccount)).ToString());
// use a HashSet to find the group the user is member of.
var groups = new HashSet(userIsMemberOf, StringComparer.OrdinalIgnoreCase);
groups.IntersectWith(groupNames);
return groups;
Esto funciona bien pero implica una consulta al directorio activo / tienda SAM (según el contexto) …
private WindowsIdentity GetWindowsIdentity(
string userName)
using (var user =
UserPrincipal.FindByIdentity(
UserPrincipal.Current.Context,
IdentityType.SamAccountName,
userName
) ??
UserPrincipal.FindByIdentity(
UserPrincipal.Current.Context,
IdentityType.UserPrincipalName,
userName
))
return user == null
? null
: new WindowsIdentity(user.UserPrincipalName);
Tomé DsCrackNames en el ejemplo pinvoke.net y lo modifiqué para convertirlo del nombre nt4 a UPN. Es un poco descuidado y es posible que desee limpiar. Para ello también tiene que golpear a la DS. Tienen el indicador DS_NAME_FLAG_SYNTACTICAL_ONLY que se puede usar para no acceder al directorio, pero no creo que funcione aquí.
class Entry
const uint NO_ERROR = 0;
[DllImport("ntdsapi.dll", CharSet = CharSet.Auto)]
static public extern uint DsCrackNames(
IntPtr hDS,
DS_NAME_FLAGS flags,
DS_NAME_FORMAT formatOffered,
DS_NAME_FORMAT formatDesired,
uint cNames,
string[] rpNames,
out IntPtr ppResult // PDS_NAME_RESULT
);
[DllImport("ntdsapi.dll", CharSet = CharSet.Auto)]
static public extern void DsFreeNameResult(IntPtr pResult /* DS_NAME_RESULT* */);
public enum DS_NAME_ERROR
DS_NAME_NO_ERROR = 0,
// Generic processing error.
DS_NAME_ERROR_RESOLVING = 1,
// Couldn't find the name at all - or perhaps caller doesn't have
// rights to see it.
DS_NAME_ERROR_NOT_FOUND = 2,
// Input name mapped to more than one output name.
DS_NAME_ERROR_NOT_UNIQUE = 3,
// Input name found, but not the associated output format.
// Can happen if object doesn't have all the required attributes.
DS_NAME_ERROR_NO_MAPPING = 4,
// Unable to resolve entire name, but was able to determine which
// domain object resides in. Thus DS_NAME_RESULT_ITEM?.pDomain
// is valid on return.
DS_NAME_ERROR_DOMAIN_ONLY = 5,
// Unable to perform a purely syntactical mapping at the client
// without going out on the wire.
DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6,
// The name is from an external trusted forest.
DS_NAME_ERROR_TRUST_REFERRAL = 7
[Flags]
public enum DS_NAME_FLAGS
DS_NAME_NO_FLAGS = 0x0,
// Perform a syntactical mapping at the client (if possible) without
// going out on the wire. Returns DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING
// if a purely syntactical mapping is not possible.
DS_NAME_FLAG_SYNTACTICAL_ONLY = 0x1,
// Force a trip to the DC for evaluation, even if this could be
// locally cracked syntactically.
DS_NAME_FLAG_EVAL_AT_DC = 0x2,
// The call fails if the DC is not a GC
DS_NAME_FLAG_GCVERIFY = 0x4,
// Enable cross forest trust referral
DS_NAME_FLAG_TRUST_REFERRAL = 0x8
public enum DS_NAME_FORMAT
// unknown name type
DS_UNKNOWN_NAME = 0,
// eg: CN=User Name,OU=Users,DC=Example,DC=Microsoft,DC=Com
DS_FQDN_1779_NAME = 1,
// eg: ExampleUserN
// Domain-only version includes trailing '\'.
DS_NT4_ACCOUNT_NAME = 2,
// Probably "User Name" but could be something else. I.e. The
// display name is not necessarily the defining RDN.
DS_DISPLAY_NAME = 3,
// obsolete - see #define later
// DS_DOMAIN_SIMPLE_NAME = 4,
// obsolete - see #define later
// DS_ENTERPRISE_SIMPLE_NAME = 5,
// String-ized GUID as returned by IIDFromString().
// eg: 4fa050f0-f561-11cf-bdd9-00aa003a77b6
DS_UNIQUE_ID_NAME = 6,
// eg: example.microsoft.com/software/user name
// Domain-only version includes trailing '/'.
DS_CANONICAL_NAME = 7,
// eg: [email protected]
DS_USER_PRINCIPAL_NAME = 8,
// Same as DS_CANONICAL_NAME except that rightmost '/' is
// replaced with 'n' - even in domain-only case.
// eg: example.microsoft.com/softwarenuser name
DS_CANONICAL_NAME_EX = 9,
// eg: www/[email protected] - generalized service principal
// names.
DS_SERVICE_PRINCIPAL_NAME = 10,
// This is the string representation of a SID. Invalid for formatDesired.
// See sddl.h for SID binary <--> text conversion routines.
// eg: S-1-5-21-397955417-626881126-188441444-501
DS_SID_OR_SID_HISTORY_NAME = 11,
// Pseudo-name format so GetUserNameEx can return the DNS domain name to
// a caller. This level is not supported by the DS APIs.
DS_DNS_DOMAIN_NAME = 12
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DS_NAME_RESULT_ITEM
public DS_NAME_ERROR status;
public string pDomain;
public string pName;
[DllImport("ntdsapi.dll", CharSet = CharSet.Auto)]
static public extern uint DsBind(
string DomainControllerName, // in, optional
string DnsDomainName, // in, optional
out IntPtr phDS);
[DllImport("ntdsapi.dll", CharSet = CharSet.Auto)]
static public extern uint DsUnBind(ref IntPtr phDS);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DS_NAME_RESULT
public uint cItems;
public IntPtr rItems; // PDS_NAME_RESULT_ITEM
[STAThread]
static void Main(string[] args)
// Bind to default global catalog
IntPtr hDS;
uint err = DsBind(null, null, out hDS);
if (err != NO_ERROR)
Console.WriteLine("Error on DsBind : 0", err);
return;
// Crack the currently logged on name
try
string[] names = new string[] System.Security.Principal.WindowsIdentity.GetCurrent().Name ;
DS_NAME_RESULT_ITEM[] results =
HandleDsCrackNames(hDS, DS_NAME_FLAGS.DS_NAME_NO_FLAGS, DS_NAME_FORMAT.DS_NT4_ACCOUNT_NAME, DS_NAME_FORMAT.DS_USER_PRINCIPAL_NAME, names);
foreach (DS_NAME_RESULT_ITEM result in results)
Console.WriteLine("Result : 0rnDomain : 1rnName : 2", result.status, result.pDomain, result.pName);
finally
DsUnBind(ref hDS);
///
/// A wrapper function for the DsCrackNames OS call
///
/// DsBind handle
/// Flags controlling the process
/// Format of the names
/// Desired format for the names
/// The names to crack
/// The crack result
public static DS_NAME_RESULT_ITEM[] HandleDsCrackNames(IntPtr hDS, DS_NAME_FLAGS flags, DS_NAME_FORMAT formatOffered, DS_NAME_FORMAT formatDesired, string[] names)
IntPtr pResult;
DS_NAME_RESULT_ITEM[] ResultArray;
uint err = DsCrackNames(
hDS,
flags,
formatOffered,
formatDesired,
(uint)((names == null) ? 0 : names.Length),
names,
out pResult);
if (err != NO_ERROR)
throw new System.ComponentModel.Win32Exception((int)err);
try
// Next convert the returned structure to managed environment
DS_NAME_RESULT Result = new DS_NAME_RESULT();
Result.cItems = (uint)Marshal.ReadInt32(pResult);
Result.rItems = Marshal.ReadIntPtr(pResult, Marshal.OffsetOf(typeof(DS_NAME_RESULT), "rItems").ToInt32());
IntPtr curptr = Result.rItems;
ResultArray = new DS_NAME_RESULT_ITEM[Result.cItems];
for (int index = 0; index < (int)Result.cItems; index++)
ResultArray[index] = (DS_NAME_RESULT_ITEM)Marshal.PtrToStructure(curptr, typeof(DS_NAME_RESULT_ITEM));
curptr = (IntPtr)((int)curptr + Marshal.SizeOf(ResultArray[index]));
finally
DsFreeNameResult(pResult);
return ResultArray;
Si posees alguna desconfianza y disposición de aumentar nuestro tutorial puedes realizar una aclaración y con mucho placer lo ojearemos.