Wednesday, 30 October 2013

.NET 4.5, Obtaining Role Membership

Over the last few months I have been doing a significant amount of work with claims authentication and authorisation.  This has led me into some very tight spots with management of principals and identities inside the .NET framework.  More recently I have been required to produce a console application which takes the current windows credential and verifies role membership.

This is a very quick post which provides the solution I have adopted to obtain verification of role membership on a windows machine inside a console application.  I am surprised at the effort required to achieve this outcome and am sure there is an easier way.


using System.DirectoryServices;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Threading;

    using GSTT.ENoting.Domain.Security;
    using GSTT.ENoting.Infrastructure.Data.Concrete;
    using GSTT.ENoting.Infrastructure.Data.Interfaces;
    using GSTT.ENoting.Services;
    using GSTT.ENoting.Services.Factories;

    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                var item = WindowsIdentity.GetCurrent().Token;
                var identity = System.Security.Principal.WindowsIdentity.Impersonate(item);

                var principal = WindowsIdentity.GetCurrent();

                Console.WriteLine("Logged on as: " + principal.Name);
                Console.WriteLine("Credential Type: " + WindowsIdentity.GetCurrent().ToString());

                var claimRoles = principal.Claims.Where(p => p.Type == principal.RoleClaimType).ToList();
                var groups = Groups();

                using (var uow = new UnitOfWork(new IObjectContextFactory[] { new EnotingContextFactory() }).BeginTransaction(CsCatalog.ENoting))
                {
                    var roles = uow.Entity().Read.ToList();

                    foreach (var role in roles)
                    {
                        var group = groups.FirstOrDefault(p => p.Name == role.RoleName);
                        var exists = group != null && claimRoles.FirstOrDefault(p => p.Value == group.Sid) != null;

                        Console.WriteLine("IsInRole: {0}, {1}", role.RoleName, exists);
                    }
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.ToString());
            }

            Console.ReadLine();
        }

        private static AdObject[] Groups()
        {
            var path = string.Format("WinNT://{0},computer", Environment.MachineName);

            using (var computerEntry = new DirectoryEntry(path))
            {
                var userNames = from DirectoryEntry childEntry in computerEntry.Children
                                where childEntry.SchemaClassName == "Group"
                                select new AdObject { Name = childEntry.Name, Class = childEntry.SchemaClassName, Id = childEntry.Guid.ToString(), Sid = GetSidString((byte[])childEntry.Properties["objectSid"][0]) };

                return userNames.ToArray();
            }           
        }

        private class AdObject
        {

            public string Name { get; set; }

            public string Id { get; set; }

            public string Class { get; set; }

            public string Sid { get; set; }
        }

        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool ConvertSidToStringSid([MarshalAs(UnmanagedType.LPArray)] byte[] pSID, out IntPtr ptrSid);

        public static string GetSidString(byte[] sid)
        {
            IntPtr ptrSid;
            string sidString;
            if (!ConvertSidToStringSid(sid, out ptrSid))
            {
                throw new System.ComponentModel.Win32Exception();
            }

            try
            {
                sidString = Marshal.PtrToStringAuto(ptrSid);
            }
            finally
            {
                Marshal.FreeHGlobal(ptrSid);
            }
            return sidString;
        }
    }

Please let me know your thoughts.