Saltar al contenido

Cómo crear WindowsIdentity / WindowsPrincipal desde el nombre de usuario en formato DOMINIO usuario

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *