diff --git a/Frends.LDAP.AddUserToGroups/CHANGELOG.md b/Frends.LDAP.AddUserToGroups/CHANGELOG.md
index 9b9fc68..32d1db5 100644
--- a/Frends.LDAP.AddUserToGroups/CHANGELOG.md
+++ b/Frends.LDAP.AddUserToGroups/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.0.1] - 2024-03-01
+### Fixed
+- Fixed issue with UserExistsAction parameter when AD returned another error message by adding method to actually check if the user is in the group.
+
## [1.0.0] - 2022-10-07
### Added
- Initial implementation
\ No newline at end of file
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/Frends.LDAP.AddUserToGroups.Tests.csproj b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/Frends.LDAP.AddUserToGroups.Tests.csproj
index 0f0ca79..3334efb 100644
--- a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/Frends.LDAP.AddUserToGroups.Tests.csproj
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/Frends.LDAP.AddUserToGroups.Tests.csproj
@@ -8,12 +8,22 @@
false
-
-
-
-
-
-
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/GlobalSuppressions.cs b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/GlobalSuppressions.cs
new file mode 100644
index 0000000..3de20a3
--- /dev/null
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/GlobalSuppressions.cs
@@ -0,0 +1,9 @@
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Following Frends documentation guidelines")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:Field names should not begin with underscore", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Tests")]
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/UnitTests.cs b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/UnitTests.cs
index e1fd56f..3a5fd8f 100644
--- a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/UnitTests.cs
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.Tests/UnitTests.cs
@@ -1,9 +1,10 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+namespace Frends.LDAP.AddUserToGroups.Tests;
+
+using NUnit.Framework;
using Frends.LDAP.AddUserToGroups.Definitions;
using Novell.Directory.Ldap;
-namespace Frends.LDAP.AddUserToGroups.Tests;
-[TestClass]
+[TestFixture]
public class UnitTests
{
/*
@@ -14,21 +15,15 @@ LDAP server to docker.
private readonly int _port = 10389;
private readonly string? _user = "uid=admin,ou=system";
private readonly string? _pw = "secret";
- private readonly string _path = "ou=users,dc=wimpi,dc=net";
private readonly string? _groupDn = "cn=admin,ou=roles,dc=wimpi,dc=net";
+ private string _testUserDn = "CN=Test User,ou=users,dc=wimpi,dc=net";
- Input? input;
- Connection? connection;
+ private Input? input;
+ private Connection? connection;
- [TestMethod]
- public void Update_HandleLDAPError_Test()
+ [SetUp]
+ public void SetUp()
{
- input = new()
- {
- UserDistinguishedName = "CN=Common Name,CN=Users,DC=Example,DC=Com",
- GroupDistinguishedName = "CN=Admins,DC=Example,DC=Com",
- UserExistsAction = UserExistsAction.Throw
- };
connection = new()
{
Host = _host,
@@ -39,57 +34,136 @@ public void Update_HandleLDAPError_Test()
TLS = false,
};
- var ex = Assert.ThrowsException(() => LDAP.AddUserToGroups(input, connection));
+ CreateTestUser(_testUserDn);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ DeleteTestUsers(_testUserDn, "CN=admin,ou=roles,dc=wimpi,dc=net");
+ }
+
+ [Test]
+ public void Update_HandleLDAPError_Test()
+ {
+ input = new()
+ {
+ UserDistinguishedName = "CN=Common Name,CN=Users,DC=Example,DC=Com",
+ GroupDistinguishedName = "CN=Admins,DC=Example,DC=Com",
+ UserExistsAction = UserExistsAction.Throw,
+ };
+
+ var ex = Assert.Throws(() => LDAP.AddUserToGroups(input, connection, default));
Assert.IsTrue(ex.Message.Contains("No Such Object"));
}
- [TestMethod]
+ [Test]
public void AddUserToGroups_Test()
{
- var tuser = "Tes Tuser" + Guid.NewGuid().ToString();
- var dn = $"CN={tuser},ou=users,dc=wimpi,dc=net";
- CreateTestUsers(tuser);
+ input = new()
+ {
+ UserDistinguishedName = _testUserDn,
+ GroupDistinguishedName = _groupDn,
+ UserExistsAction = UserExistsAction.Throw,
+ };
+ var result = LDAP.AddUserToGroups(input, connection, default);
+ Assert.IsTrue(result.Success);
+ }
+
+ [Test]
+ public void AddUserToGroups_TestWithUserExisting()
+ {
input = new()
{
- UserDistinguishedName = dn,
- GroupDistinguishedName = _groupDn
+ UserDistinguishedName = _testUserDn,
+ GroupDistinguishedName = _groupDn,
+ UserExistsAction = UserExistsAction.Throw,
};
- connection = new()
+
+ var result = LDAP.AddUserToGroups(input, connection, default);
+ Assert.IsTrue(result.Success);
+
+ var ex = Assert.Throws(() => LDAP.AddUserToGroups(input, connection, default));
+ Assert.AreEqual("AddUserToGroups LDAP error: Attribute Or Value Exists", ex.Message);
+ }
+
+ [Test]
+ public void AddUserToGroups_TestWithUserExistingWithSkip()
+ {
+ input = new()
+ {
+ UserDistinguishedName = _testUserDn,
+ GroupDistinguishedName = _groupDn,
+ UserExistsAction = UserExistsAction.Skip,
+ };
+
+ var result = LDAP.AddUserToGroups(input, connection, default);
+ Assert.IsTrue(result.Success);
+
+ input.UserExistsAction = UserExistsAction.Skip;
+
+ result = LDAP.AddUserToGroups(input, connection, default);
+ Assert.IsFalse(result.Success);
+ }
+
+ private void CreateTestUser(string userDn)
+ {
+ LdapConnection conn = new()
{
- Host = _host,
- User = _user,
- Password = _pw,
SecureSocketLayer = false,
- Port = _port,
- TLS = false,
+ };
+ conn.Connect(_host, _port);
+ conn.Bind(_user, _pw);
+
+ var attributeSet = new LdapAttributeSet
+ {
+ new LdapAttribute("objectclass", "inetOrgPerson"),
+ new LdapAttribute("cn", "Test User"),
+ new LdapAttribute("givenname", "Test"),
+ new LdapAttribute("sn", "User"),
};
- var result = LDAP.AddUserToGroups(input, connection);
- Assert.IsTrue(result.Success.Equals(true));
+ LdapEntry newEntry = new(userDn, attributeSet);
+ conn.Add(newEntry);
+ conn.Disconnect();
}
- public void CreateTestUsers(string tuser)
+ private void DeleteTestUsers(string userDn, string groupDn)
{
- try
+ LdapConnection conn = new();
+ conn.Connect(_host, _port);
+ conn.Bind(_user, _pw);
+
+ var searchResults = (LdapSearchResults)conn.Search(
+ groupDn,
+ LdapConnection.ScopeSub,
+ "(objectClass=*)",
+ null,
+ false);
+
+ LdapEntry groupEntry = searchResults.Next();
+
+ var remove = false;
+
+ LdapAttribute memberAttr = groupEntry.GetAttribute("member");
+ var currentMembers = memberAttr.StringValueArray;
+ foreach (var member in currentMembers)
{
- LdapConnection conn = new();
- conn.Connect(_host, _port);
- conn.Bind(_user, _pw);
-
- var attributeSet = new LdapAttributeSet();
- attributeSet.Add(new LdapAttribute("objectclass", "user"));
- attributeSet.Add(new LdapAttribute("cn", tuser));
- attributeSet.Add(new LdapAttribute("givenname", "Tes"));
- attributeSet.Add(new LdapAttribute("sn", tuser.Split(' ', 1)));
-
- var entry = $"CN={tuser},{_path}";
- LdapEntry newEntry = new(entry, attributeSet);
- conn.Add(newEntry);
- conn.Disconnect();
+ if (member == userDn)
+ remove = true;
}
- catch (Exception)
+
+ if (remove)
{
+ // Remove the user from the group
+ var mod = new LdapModification(LdapModification.Delete, new LdapAttribute("member", userDn));
+ conn.Modify(groupDn, mod);
}
+
+ conn.Delete(userDn);
+
+ // Disconnect from the LDAP server
+ conn.Disconnect();
}
}
\ No newline at end of file
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/AddUserToGroups.cs b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/AddUserToGroups.cs
index 79d6b54..eb2a25e 100644
--- a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/AddUserToGroups.cs
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/AddUserToGroups.cs
@@ -2,6 +2,7 @@
using System.ComponentModel;
using Novell.Directory.Ldap;
using System;
+using System.Threading;
namespace Frends.LDAP.AddUserToGroups;
@@ -16,8 +17,9 @@ public class LDAP
///
/// Input parameters.
/// Connection parameters.
+ /// Cancellation token given by Frends.
/// Object { bool Success, string Error, string CommonName, string Path }
- public static Result AddUserToGroups([PropertyTab] Input input, [PropertyTab] Connection connection)
+ public static Result AddUserToGroups([PropertyTab] Input input, [PropertyTab] Connection connection, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(connection.Host) || string.IsNullOrWhiteSpace(connection.User) || string.IsNullOrWhiteSpace(connection.Password))
throw new Exception("AddUserToGroups error: Connection parameters missing.");
@@ -30,22 +32,24 @@ public static Result AddUserToGroups([PropertyTab] Input input, [PropertyTab] Co
conn.SecureSocketLayer = connection.SecureSocketLayer;
conn.Connect(connection.Host, connection.Port == 0 ? defaultPort : connection.Port);
- if (connection.TLS) conn.StartTls();
+ if (connection.TLS)
+ conn.StartTls();
conn.Bind(connection.User, connection.Password);
- LdapModification[] mods = new LdapModification[1];
- var member = new LdapAttribute("member", input.UserDistinguishedName);
- mods[0] = new LdapModification(LdapModification.Add, member);
- conn.Modify(input.GroupDistinguishedName, mods);
+ LdapModification[] mods = new LdapModification[1];
+ var member = new LdapAttribute("member", input.UserDistinguishedName);
+ mods[0] = new LdapModification(LdapModification.Add, member);
- return new Result(true, null, input.UserDistinguishedName, input.GroupDistinguishedName);
+ if (UserExistsInGroup(conn, input.UserDistinguishedName, input.GroupDistinguishedName, cancellationToken) && input.UserExistsAction.Equals(UserExistsAction.Skip))
+ return new Result(false, "AddUserToGroups LDAP error: User already exists in the group.", input.UserDistinguishedName, input.GroupDistinguishedName);
+
+ conn.Modify(input.GroupDistinguishedName, mods);
+
+ return new Result(true, null, input.UserDistinguishedName, input.GroupDistinguishedName);
}
catch (LdapException ex)
{
- if (ex.Message.Equals("Attribute Or Value Exists") && input.UserExistsAction.Equals(UserExistsAction.Skip))
- return new Result(false, ex.Message, input.UserDistinguishedName, input.GroupDistinguishedName);
- else
- throw new Exception($"AddUserToGroups LDAP error: {ex.Message}");
+ throw new Exception($"AddUserToGroups LDAP error: {ex.Message}");
}
catch (Exception ex)
{
@@ -57,4 +61,43 @@ public static Result AddUserToGroups([PropertyTab] Input input, [PropertyTab] Co
conn.Disconnect();
}
}
+
+ private static bool UserExistsInGroup(LdapConnection connection, string userDn, string groupDn, CancellationToken cancellationToken)
+ {
+ // Search for the user's groups
+ var searchResults = (LdapSearchResults)connection.Search(
+ groupDn,
+ LdapConnection.ScopeSub,
+ "(objectClass=*)",
+ null,
+ false);
+
+ // Check if the user is a member of the specified group
+ while (searchResults.HasMore())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ LdapEntry entry;
+ try
+ {
+ entry = searchResults.Next();
+ }
+ catch (LdapException)
+ {
+ continue;
+ }
+
+ if (entry != null)
+ {
+ LdapAttribute memberAttr = entry.GetAttribute("member");
+ var currentMembers = memberAttr.StringValueArray;
+ foreach (var member in currentMembers)
+ {
+ if (member == userDn)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
}
\ No newline at end of file
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.csproj b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.csproj
index f823688..64c51e3 100644
--- a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.csproj
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups.csproj
@@ -2,7 +2,7 @@
net6.0
- 1.0.0
+ 1.0.1
Frends
Frends
Frends
@@ -20,8 +20,16 @@
PreserveNewest
+
+
+
+ <_Parameter1>$(MSBuildProjectName).Tests
+
+
+
+
\ No newline at end of file
diff --git a/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/GlobalSuppressions.cs b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/GlobalSuppressions.cs
new file mode 100644
index 0000000..33b4365
--- /dev/null
+++ b/Frends.LDAP.AddUserToGroups/Frends.LDAP.AddUserToGroups/GlobalSuppressions.cs
@@ -0,0 +1,11 @@
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Definitions")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation should match accessors", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups.Definitions")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Following Frends documentation guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Following Frends Tasks guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]
+[assembly: SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Following Frends guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Following Frends guidelines")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Following Frends guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "Following Frends guidelines", Scope = "namespaceanddescendants", Target = "~N:Frends.LDAP.AddUserToGroups")]