Po małym przeszukaniu internetu (głównieCodeProject i ExpertExchange) udało mi się w końcu zrobić logowanie do AD z wykorzystaniem grup (ale tylko tych jawnie wpisanych użytkowników, bez rozwijania grup zagnieżdżonych).Główny kod programu:
//try autchenitcationstring[] groupsToCheck ={ "cwiczeniowcy", "Asystenci", "operatorzy", "wykladowcy" };try{ //bind to ad DirectoryEntry de = new DirectoryEntry("LDAP://dc=pjwstk,dc=edu,dc=pl", TBLogin.Text, TBHaslo.Text); DirectorySearcher mySearcher = new DirectorySearcher(de); mySearcher.Filter = ("(&(ObjectCategory=Person)(ObjectClass=user)(SAMAccountName=" + TBLogin.Text + "))"); mySearcher.PropertiesToLoad.Add("givenName"); mySearcher.PropertiesToLoad.Add("sn"); mySearcher.PropertiesToLoad.Add("Path"); mySearcher.PropertiesToLoad.Add("primaryGroupId"); mySearcher.PropertiesToLoad.Add("objectSid"); SearchResult results = mySearcher.FindOne(); if (null != results) { uImie = results.Properties["givenName"][0].ToString(); uNazwisko = results.Properties["sn"][0].ToString(); //get user groups ArrayList groups = new ArrayList(); DirectoryEntry obUser = new DirectoryEntry(results.Path); object obGroups = obUser.Invoke("Groups"); foreach (object ob in (IEnumerable)obGroups) { DirectoryEntry obGpEntry = new DirectoryEntry(ob); groups.Add(obGpEntry.Name); } //get primary group int primaryGroupId = (int)results.Properties["PrimaryGroupID"][0]; byte[] userSid = results.Properties["objectSid"][0] as byte[]; byte[] primaryGroupSid = BuildPrimaryGroupSID(userSid, primaryGroupId); string adsPath = String.Format("LDAP://<SID={0}>", BuildOctetString(primaryGroupSid)); DirectoryEntry objUser = new DirectoryEntry(adsPath, TBLogin.Text, TBHaslo.Text); groups.Add((string)objUser.Properties["name"].Value); //check if member is in group bool isInGroup =false; foreach (string stc in groupsToCheck) if (groups.Contains(stc)) isInGroup = true; if (isInGroup) { //Authenticated } else { LblError.Text = "Nie masz uprawnien do wypelnienienia ankiety"; } } else LblError.Text += "Blad Autoryzacji"; }catch (Exception dsce) { LblError.Text = "Blad Autoryzacji";}// insertXML(uImie,uNazwisko);
Do tego potrzebujemy jeszcze funkcję do konwersji SIDów
private byte[] BuildPrimaryGroupSID(byte[] userSid, int primaryGroupId){ byte[] rid = BitConverter.GetBytes(primaryGroupId); for (int i = 0; i < rid.Length; i++) { userSid.SetValue(rid[i], new long[] { userSid.Length - (rid.Length - i) }); } return userSid;}private string BuildOctetString(byte[] bytes){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { sb.AppendFormat("{0}", bytes[i].ToString("X2")); } return sb.ToString();}
I klasę, która produkuje SID
using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Runtime.InteropServices;using PSID = System.IntPtr;using HLOCAL = System.IntPtr;using BOOL = System.Int32;using HANDLE = System.IntPtr;using DWORD = System.UInt32;/// <summary>/// Summary description for Class1/// </summary>public class sid{ [DllImport("Advapi32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)] public static extern BOOL ConvertSidToStringSid(PSID Sid, out IntPtr StringSid); public unsafe static string UnsafeGetSidString(object sid) { string sidString = null; IntPtr strPtr; fixed (byte* psid = (byte[])sid) { IntPtr psidPtr = (IntPtr)psid; BOOL rc = ConvertSidToStringSid(psidPtr, out strPtr); Win32.CheckCall(rc); try { sidString = Marshal.PtrToStringAuto(strPtr); } finally { Win32.LocalFree(strPtr); } } return sidString; }}public class Win32{ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern HLOCAL LocalFree(HLOCAL hMem); public static void CheckCall(bool funcResult) { if (!funcResult) { ThrowLastError(); } } public static void CheckCall(BOOL funcResult) { CheckCall(funcResult != 0); } public static void CheckCall(HANDLE funcResult) { CheckCall(!IsNullHandle(funcResult)); } public static DWORD GetLastError() { return (DWORD)Marshal.GetLastWin32Error(); } public static bool IsNullHandle(HANDLE ptr) { return (ptr == IntPtr.Zero); } public static void ThrowLastError() { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); }}
Oczywiście w web.config w <system.web> musimy dodać obsługę funkcji typu unsafe:
<compilation> <assemblies> <add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/> </assemblies> <compilers> <compiler language="c#;cs;csharp" extension=".cs" compilerOptions="/unsafe" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </compilers> </compilation>
Remember Me