/// <summary> /// Delete a user /// </summary> /// <param name="username"></param> /// <param name="acp"></param> public static void DeleteUser( string username, AccountManagementPrincipalContext acp) { using (PrincipalContext context = BuildPrincipal(acp)) { var user = FindUser(username, context); user?.Delete(); } }
/// <summary> /// Search a user hierarchicaly in the AD structure /// </summary> /// <param name="userName">The user name or identifier</param> /// <param name="ac">The principal context global configuration</param> /// <param name="pc">The principal context this user was found in</param> /// <returns></returns> private static UserPrincipal SearchUser(string userName, AccountManagementPrincipalContext ac, out PrincipalContext pc) { UserPrincipal user = null; var name = new FqdnNameParser(userName); if (name.ContextType == ContextType.Machine) { pc = BuildPrincipal(null); user = FindUser(userName, pc); if (user != null) { return(user); } pc.Dispose(); return(null); } // Probar sin OU pc = BuildPrincipal(ac); user = FindUser(userName, pc); if (user != null) { return(user); } pc.Dispose(); // Probar con OU pc = BuildPrincipalWithoutOu(ac); user = FindUser(userName, pc); if (user != null) { return(user); } pc.Dispose(); return(null); }
/// <summary> /// Search for a group in diferent principal context in the following order: local, domain without OU, domain with OU /// </summary> /// <param name="groupName"></param> /// <param name="ac"></param> /// <param name="pc"></param> /// <returns></returns> private static GroupPrincipal SearchGroup(string groupName, AccountManagementPrincipalContext ac, out PrincipalContext pc) { var name = new FqdnNameParser(groupName); GroupPrincipal group = null; if (name.ContextType == ContextType.Machine) { pc = BuildPrincipal(null); group = FindGroup(groupName, pc); if (group != null) { return(group); } pc.Dispose(); return(null); } // Look in provided OU pc = BuildPrincipal(ac); group = FindGroup(groupName, pc); if (group != null) { return(group); } pc.Dispose(); // Look in domain pc = BuildPrincipalWithoutOu(ac); group = FindGroup(groupName, pc); if (group != null) { return(group); } pc.Dispose(); return(null); }
/// <summary> /// Ensure user is in group /// </summary> /// <param name="userPrincipalName"></param> /// <param name="groupname"></param> /// <param name="logger"></param> /// <param name="acp"></param> public static void EnsureUserInGroup( string userPrincipalName, string groupname, ILoggerInterface logger, AccountManagementPrincipalContext acp) { logger.LogInfo(true, $"Ensure user '{userPrincipalName}' in group '{groupname}'"); UserPrincipal user = SearchUser(userPrincipalName, acp, out var userContext); if (user == null) { userContext?.Dispose(); throw new Exception($"User '{userPrincipalName}' not found."); } GroupPrincipal group = SearchGroup(groupname, acp, out var groupContext); if (group == null) { userContext?.Dispose(); groupContext?.Dispose(); throw new Exception($"Group '{groupname}' not found."); } logger.LogWarning(false, $"Found group '{group.Name}' '{group.Sid}' in context '{groupContext.ConnectedServer}' and '{groupContext.ContextType}'"); foreach (Principal member in group.GetMembers(true)) { if (member.SamAccountName == user.SamAccountName) { logger.LogInfo(true, $"User already in group '{groupname}'"); return; } } group.Members.Add(user); group.Save(); userContext.Dispose(); groupContext.Dispose(); logger.LogInfo(true, $"Added user '{userPrincipalName}' to group '{groupname}'"); }
/// <summary> /// Chef if user is in a group. /// </summary> /// <param name="userName"></param> /// <param name="groupName"></param> /// <param name="acp"></param> /// <returns></returns> public static bool IsUserInGroup( string userName, string groupName, AccountManagementPrincipalContext acp) { using (var context = BuildPrincipal(acp)) { using (var searcher = new PrincipalSearcher(new UserPrincipal(context) { SamAccountName = userName })) { using (var user = searcher.FindOne() as UserPrincipal) { return(user != null && user.IsMemberOf(context, IdentityType.SamAccountName, groupName)); } } } }
/// <summary> /// Add this permission to a directory and user /// </summary> /// <param name="identity"></param> /// <param name="directory"></param> /// <param name="fileSystemRights"></param> /// <param name="acp"></param> public static void AddPermissionToDirectoryIfMissing( string identity, string directory, FileSystemRights fileSystemRights, AccountManagementPrincipalContext acp) { // Search for the target principal in all the domain using (PrincipalContext context = BuildPrincipalWithoutOu(acp)) { var principal = FindIdentity(identity, context); if (principal == null) { throw new Exception($"Unable to find principal with identifier: {identity}"); } AddPermissionToDirectoryIfMissing(principal.Sid, directory, fileSystemRights); } }
/// <summary> /// Ensure user not in group /// </summary> /// <param name="userPrincipalName"></param> /// <param name="groupname"></param> /// <param name="logger"></param> /// <param name="acp"></param> public static void EnsureUserNotInGroup( string userPrincipalName, string groupname, ILoggerInterface logger, AccountManagementPrincipalContext acp) { logger.LogInfo(true, $"Ensure user '{userPrincipalName}' NOT in group {groupname}"); UserPrincipal up = SearchUser(userPrincipalName, acp, out var userContext); if (up == null) { userContext?.Dispose(); throw new Exception($"User '{userPrincipalName}' not found."); } GroupPrincipal group = SearchGroup(groupname, acp, out var groupContext); if (group == null) { userContext?.Dispose(); groupContext?.Dispose(); throw new Exception($"Group '{groupname}' not found."); } foreach (Principal member in group.GetMembers(true)) { if (member.SamAccountName == up.SamAccountName) { group.Members.Remove(member); group.Save(); logger.LogInfo(true, $"Removed user '{userPrincipalName}' from group '{groupname}'"); return; } } logger.LogInfo(true, $"User '{userPrincipalName}' not found in group '{groupname}'"); }
/// <summary> /// Add a user to a group, /// </summary> /// <param name="groupName"></param> /// <param name="acp"></param> /// <returns></returns> public static void EnsureGroupExists( string groupName, AccountManagementPrincipalContext acp) { using (PrincipalContext context = BuildPrincipal(acp)) { GroupPrincipal g = FindGroup(groupName, context); if (g == null) { g = new GroupPrincipal(context); g.Name = groupName; g.SamAccountName = groupName; g.Description = groupName; if (context.ContextType == ContextType.Domain) { g.UserPrincipalName = groupName; } } g.Save(); } }
/// <summary> /// Generate a valid directory principal, removing any OU specification /// </summary> /// <param name="context"></param> /// <returns></returns> public static PrincipalContext BuildPrincipalWithoutOu(AccountManagementPrincipalContext context) { if (context == null) { return(BuildPrincipal(null)); } var ctx = context.JsonClone(); var container = new ContainerParser(ctx.Container); for (int x = 0; x < container.Parts.Count; x++) { if (container.Parts[x].Item1.Equals("OU", StringComparison.CurrentCultureIgnoreCase)) { container.Parts.RemoveAt(x); x--; } } ctx.Container = container.GetContainer(); return(BuildPrincipal(ctx)); }
/// <summary> /// Create a user or return one if it does not exist. /// </summary> /// <param name="identity"></param> /// <param name="password"></param> /// <param name="displayName"></param> /// <param name="logger"></param> /// <param name="acp"></param> /// <returns></returns> public static UserPrincipal EnsureUserExists( string identity, string password, string displayName, ILoggerInterface logger, AccountManagementPrincipalContext acp) { var parsedUserName = new FqdnNameParser(identity); if (parsedUserName.UserPrincipalName.Length > 64) { throw new Exception($"Windows account userPrincipalName '{parsedUserName.UserPrincipalName}' cannot be longer than 64 characters."); } using (PrincipalContext pc = BuildPrincipal(acp)) { UserPrincipal up = FindUser(identity, pc); string samAccountName = parsedUserName.SamAccountName; logger.LogInfo(false, $"Ensure windows account exists '{samAccountName}@{password}' with userPrincipal '{identity}'"); if (up == null) { up = new UserPrincipal(pc, samAccountName, password, true); } else { logger.LogInfo(true, $"Found account IsAccountLockedOut={up.IsAccountLockedOut()}, SamAccountName={up.SamAccountName}"); // Make sure we have the latest password, just in case // the pwd algorithm generation changes... try { up.SetPassword(password); } catch (Exception e) { logger.LogWarning(true, "Cannot update password for account: " + e.Message + e.InnerException?.Message); } } up.UserCannotChangePassword = true; up.PasswordNeverExpires = true; up.Enabled = true; up.DisplayName = displayName; up.Description = parsedUserName.UserPrincipalName; // If we are in a domain, assign the user principal name if (pc.ContextType == ContextType.Domain) { logger.LogInfo(true, "Setting UserPrincipalName to '{0}'", parsedUserName.UserPrincipalName); up.UserPrincipalName = parsedUserName.UserPrincipalName + "@" + parsedUserName.DomainName.ToLower(); } if (up.IsAccountLockedOut()) { try { up.UnlockAccount(); } catch (Exception e) { logger.LogWarning(true, "Cannot unlock account: " + e.Message + e.InnerException?.Message); } } try { up.Save(); } catch (Exception e) { logger.LogException(new Exception("Error while saving user", e), EventLogEntryType.Warning); // Sometimes it crashes, but everything was OK (weird?) // so we check again if the user has been created Thread.Sleep(500); up = FindUser(identity, pc); if (up == null) { // Rethrow the original, whatever it was. ExceptionDispatchInfo.Capture(e).Throw(); } } return(up); } }
/// <summary> /// Generate a valid directory principal based on the application settings. /// </summary> /// <param name="context"></param> /// <returns></returns> public static PrincipalContext BuildPrincipal(AccountManagementPrincipalContext context) { if (context == null) { // Default to old configuration... return(new PrincipalContext(ContextType.Machine, Environment.MachineName)); } ContextType contextType; switch (context.ContextType) { case nameof(ContextType.Machine): contextType = ContextType.Machine; break; case nameof(ContextType.ApplicationDirectory): contextType = ContextType.ApplicationDirectory; break; case nameof(ContextType.Domain): contextType = ContextType.Domain; break; default: throw new Exception($"Context type not supported: " + context.ContextType); } ContextOptions?options = null; switch (context.ContextOptions) { case nameof(ContextOptions.Negotiate): options = ContextOptions.Negotiate; break; case nameof(ContextOptions.Sealing): options = ContextOptions.Sealing; break; case nameof(ContextOptions.SecureSocketLayer): options = ContextOptions.SecureSocketLayer; break; case nameof(ContextOptions.Signing): options = ContextOptions.Signing; break; case nameof(ContextOptions.ServerBind): options = ContextOptions.ServerBind; break; case nameof(ContextOptions.SimpleBind): options = ContextOptions.SimpleBind; break; case null: break; default: throw new Exception($"Context option not supported: " + context.ContextOptions); } if (options != null) { return(new PrincipalContext(contextType, context.ContextName, context.Container, options.Value, context.ContextUsername, context.ContextPassword)); } else { return(new PrincipalContext(contextType, context.ContextName, context.Container, context.ContextUsername, context.ContextPassword)); } }