// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; namespace Spludlow { public class DirectoryService { // should use newer System.DirectoryServices.AccountManagement public enum UserAccountControlFlags { SCRIPT = 0x0000001, ACCOUNTDISABLE = 0x0000002, HOMEDIR_REQUIRED = 0x0000008, LOCKOUT = 0x0000010, PASSWD_NOTREQD = 0x0000020, PASSWD_CANT_CHANGE = 0x0000040, ENCRYPTED_TEXT_PWD_ALLOWED = 0x0000080, TEMP_DUPLICATE_ACCOUNT = 0x0000100, NORMAL_ACCOUNT = 0x0000200, INTERDOMAIN_TRUST_ACCOUNT = 0x0000800, WORKSTATION_TRUST_ACCOUNT = 0x0001000, SERVER_TRUST_ACCOUNT = 0x0002000, DONT_EXPIRE_PASSWORD = 0x0010000, MNS_LOGON_ACCOUNT = 0x0020000, SMARTCARD_REQUIRED = 0x0040000, TRUSTED_FOR_DELEGATION = 0x0080000, NOT_DELEGATED = 0x0100000, USE_DES_KEY_ONLY = 0x0200000, DONT_REQ_PREAUTH = 0x0400000, PASSWORD_EXPIRED = 0x0800000, TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000, } public static bool Exists(string userOrGroup) { ContextType contextType = ContextType.Machine; if (IsDomain(userOrGroup) == true) contextType = ContextType.Domain; using (PrincipalContext context = new PrincipalContext(contextType)) { using (UserPrincipal user = UserPrincipal.FindByIdentity(context, userOrGroup)) { if (user != null) return true; } using (GroupPrincipal group = GroupPrincipal.FindByIdentity(context, userOrGroup)) { if (group != null) return true; } } return false; } public static string[] UsersInGroup(string userOrGroup) { ContextType contextType = ContextType.Machine; if (IsDomain(userOrGroup) == true) contextType = ContextType.Domain; using (PrincipalContext context = new PrincipalContext(contextType)) { using (GroupPrincipal group = GroupPrincipal.FindByIdentity(context, userOrGroup)) { if (group == null) throw new ApplicationException("UsersInGroupExists; Group not found: " + userOrGroup); List users = new List(); foreach (Principal principal in group.Members) { if (principal is UserPrincipal) { string name = principal.Sid.Translate(typeof(System.Security.Principal.NTAccoun‌​t)).ToString(); users.Add(name); } } return users.ToArray(); } } } public static bool IsUserInGroup(string user, string group) { user = user.ToLower(); foreach (string checkUser in UsersInGroup(group)) { if (user == checkUser.ToLower()) return true; } return false; } public static bool IsDomain(string name) // And validate domain or host is valid { string domain = Spludlow.ManagementObjects.SystemDomain(); int index = name.IndexOf(@"\"); if (index == -1) throw new ApplicationException(@"IsDomain; Expected in format 'HOST\UserOrGroup' or 'DOMAIN\UserOrGroup': " + name); string hostOrDomain = name.Substring(0, index).ToUpper(); string host = Environment.MachineName.ToUpper(); if (domain == null) { if (hostOrDomain != host) throw new ApplicationException("IsDomain; Computer not in domain and user or group is not local: " + name); return false; } if (hostOrDomain == domain.ToUpper()) return true; if (hostOrDomain == host) return false; throw new ApplicationException("IsDomain; Computer in domain and user or group not in domain and not on local computer: " + name); } public static void AddGroup(string group) { int index = group.IndexOf(@"\"); if (index == -1) throw new ApplicationException(@"AddGroup; Expected in format 'HOST\UserOrGroup' or 'DOMAIN\UserOrGroup': " + group); string groupName = group.Substring(index + 1); if (IsDomain(group) == true) AddDomainGroup(groupName); else AddLocalGroup(groupName); } public static void AddLocalGroup(string groupName) { string description = "Spludlow Group: " + groupName; AddLocalGroup(groupName, description); } public static void AddLocalGroup(string groupName, string description) { using (DirectoryEntry computer = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer")) { using (DirectoryEntry group = computer.Children.Add(groupName, "group")) { if (description != null) group.Invoke("Put", new object[] { "Description", description }); group.CommitChanges(); Console.WriteLine("Added Local Group: " + groupName); } } } public static void AddDomainGroup(string groupName) { string description = "Spludlow Group: " + groupName; AddDomainGroup(groupName, "Users", description); } public static void AddDomainGroup(string groupName, string container, string description) { string dnsDomain = Spludlow.ManagementObjects.SystemDnsDomain(); using (DirectoryEntry domain = new DirectoryEntry(DirectoryEntryPath(container, dnsDomain))) { using (DirectoryEntry group = domain.Children.Add("CN=" + groupName, "group")) { group.Properties["description"].Add(description); group.Properties["sAMAccountName"].Add(groupName); group.CommitChanges(); Console.WriteLine("Added Domain Group: " + groupName); } } } public static void AddUser(string user, string password, string group) { AddUser(user, password, new string[] { group }); } public static void AddUser(string user, string password, string[] groups) { int index = user.IndexOf(@"\"); if (index == -1) throw new ApplicationException(@"AddUser; Expected in format 'HOST\UserOrGroup' or 'DOMAIN\UserOrGroup': " + user); string userName = user.Substring(index + 1); if (IsDomain(user) == true) { AddDomainUser(userName, password); if (groups == null) groups = new string[0]; // Automattically added { "Domain Users" }; } else { AddLocalUser(userName, password); if (groups == null) groups = new string[] { Environment.MachineName + @"\Users" }; // pass empty array if you dont want this !!! can because of overload } if (groups.Length > 0) AddUserToGroups(user, groups); } public static void AddLocalUser(string userName, string password) { string description = "Spludlow User: " + userName; AddLocalUser(userName, password, UserAccountControlFlags.DONT_EXPIRE_PASSWORD, description); } public static void AddLocalUser(string userName, string password, UserAccountControlFlags flags, string description) { using (DirectoryEntry computer = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer")) { using (DirectoryEntry user = computer.Children.Add(userName, "user")) { user.Invoke("SetPassword", new object[] { password }); user.Invoke("Put", new object[] { "UserFlags", (int)flags }); if (description != null) user.Invoke("Put", new object[] { "Description", description }); user.CommitChanges(); Console.WriteLine("Added Local User: " + userName); } } } public static void AddDomainUser(string userName, string password) { string description = "Spludlow User: " + userName; AddDomainUser(userName, password, UserAccountControlFlags.DONT_EXPIRE_PASSWORD, "Users", description); } public static void AddDomainUser(string userName, string password, UserAccountControlFlags flags, string container, string description) { string dnsDomain = Spludlow.ManagementObjects.SystemDnsDomain(); using (DirectoryEntry domain = new DirectoryEntry(DirectoryEntryPath(container, dnsDomain))) { using (DirectoryEntry user = domain.Children.Add("CN=" + userName, "user")) { user.Properties["userPrincipalName"].Value = userName + "@" + dnsDomain; user.CommitChanges(); user.Properties["sAMAccountName"].Value = userName; user.CommitChanges(); user.Invoke("SetPassword", new object[] { password }); if (description != null) user.Properties["description"].Value = description; user.Properties["userAccountControl"].Value = (int)flags; user.CommitChanges(); Console.WriteLine("Added Domain User: " + userName); } } } public static void AddUserToGroups(string user, string[] groups) { foreach (string group in groups) AddUserToGroup(user, group); } public static void AddUserToGroup(string user, string group) { if (user.Contains(@"\") == false || group.Contains(@"\") == false) throw new ApplicationException(@"Please use the format DOMAIN\User or HOST\User for the users and groups"); user = WinNTPath(user, "user"); group = WinNTPath(group, "group"); using (DirectoryEntry groupEntry = new DirectoryEntry(group)) { groupEntry.Invoke("Add", user); groupEntry.CommitChanges(); Console.WriteLine("Added User to Group: " + user + ", " + group); } } public static string WinNTPath(string username, string type) { return "WinNT://" + username.Replace(@"\", "/") + "," + type; } public static string DirectoryEntryPath(string container, string dnsDomain) { string[] domainParts = dnsDomain.Split(new char[] { '.' }); StringBuilder result = new StringBuilder(); result.Append("LDAP://CN="); result.Append(container); foreach (string domainPart in domainParts) { result.Append(",DC="); result.Append(domainPart); } return result.ToString(); } } }