1

I am trying to add a Contact to a distribution list.

Here is how I am going about it:

public void AddContactsToGroup(string groupName, string[] userNames)
{
         using (PrincipalContext context = GetPrincipalContext())
         {
            var group = GroupPrincipal.FindByIdentity(context, IdentityType.Name, groupName);

            if (group.IsSecurityGroup.HasValue && group.IsSecurityGroup.Value == true)
               throw new ArgumentException("Groups cannot be security groups.");

            foreach (var userName in userNames)
            {
               var query = new ContactPrincipal(context)
                              {
                                 Name = userName
                              };

               var user = Search(query).First();

               group.Members.Add(user);
            }

            group.Save(context);
         }
      }

  private List<T> Search<T>(T query) where T : Principal
  {
     var searcher = new PrincipalSearcher();
     searcher.QueryFilter = query;
     PrincipalSearchResult<Principal> results = searcher.FindAll();

     return results.Cast<T>().ToList();
  }

Here is the ContactPrincipal class (I borrowed this from somewhere):

   [DirectoryObjectClass("contact")]
   [DirectoryRdnPrefix("CN")]
   public class ContactPrincipal : AuthenticablePrincipal
   {
      public ContactPrincipal(PrincipalContext context)
         : base(context)
      {
      }

      public static ContactPrincipal FindByIdentity(PrincipalContext context, string identityValue)
      {
         return (ContactPrincipal) Principal.FindByIdentityWithType(context, typeof (ContactPrincipal), identityValue);
      }

      public static ContactPrincipal FindByIdentity(PrincipalContext context, IdentityType identityType,
                                                    string identityValue)
      {
         return
            (ContactPrincipal)
            Principal.FindByIdentityWithType(context, typeof (ContactPrincipal), identityType, identityValue);
      }

      [DirectoryProperty("mail")]
      public string EmailAddress
      {
         get
         {
            if (ExtensionGet("mail").Length == 1)
            {
               return ExtensionGet("mail")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("mail", value); }
      }

      [DirectoryProperty("givenName")]
      public string GivenName
      {
         get
         {
            if (ExtensionGet("givenName").Length == 1)
            {
               return ExtensionGet("givenName")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("givenName", value); }
      }

      [DirectoryProperty("middleName")]
      public string MiddleName
      {
         get
         {
            if (ExtensionGet("middleName").Length == 1)
            {
               return ExtensionGet("middleName")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("middleName", value); }
      }

      [DirectoryProperty("sn")]
      public string Surname
      {
         get
         {
            if (ExtensionGet("sn").Length == 1)
            {
               return ExtensionGet("sn")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("sn", value); }
      }

      [DirectoryProperty("mobile")]
      public string MobileTelephoneNumber
      {
         get
         {
            if (ExtensionGet("mobile").Length == 1)
            {
               return ExtensionGet("mobile")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("mobile", value); }
      }

      [DirectoryProperty("telephoneNumber")]
      public string VoiceTelephoneNumber
      {
         get
         {
            if (ExtensionGet("telephoneNumber").Length == 1)
            {
               return ExtensionGet("telephoneNumber")[0].ToString();
            }
            else
            {
               return null;
            }
         }
         set { ExtensionSet("telephoneNumber", value); }
      }
   }

My problem is, when I hit the

group.Members.Add(user) 

line in the AddContactsToGroup method, an error is thrown that states

The Principal object must have a valid SID IdentityType in order to perform this operation.

When I interrogate the properties of the ContactPrincipal, the Sid is, in fact null. This isn't surprising since a Contact is an object with no security.

How can I add a Contact to a non-security group?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Ryan Bennett
  • 3,404
  • 19
  • 32

3 Answers3

1

Why not just use SIDs? Otherwise, what are you using to uniquely identify your users and groups? GUID?


  1. Are you sure the ContactPrinciple search query is returning the correct results? The section Finding Ourselves in Managing Directory Security Principals in the .NET Framework 3.5 might be helpful. Why don't you search for each user by using the ContactPrincipal FindByIdentity rather than the search query?

  2. Along the same lines, maybe you should add a line that will check if the user principle is equal to null before trying to add it to the group.

    if(user != null) { group.Members.Add(user); }

  3. Also, this thread towards the bottom seems to describe the same problem you are having. Here's the solution provided in VB.

    How to add a contact to a group :)? For some reason adding a contact to a group through the normal methods you would use to add a user or group to a group causes an error. To work around this I used the following code:

    Public Sub AddPrincipalToGroup(ByVal Group As GroupPrincipal, ByVal ChildPrincipal As Principal)
        ' use the underlying DirectoryEntry objects to avoid 
        ' issues surrounding contacts and other custom types
        Dim deGroup As DirectoryEntry = Group.GetUnderlyingObject()
    
        deGroup.Properties("member").Add(ChildPrincipal.DistinguishedName)
        deGroup.CommitChanges()
    End Sub
    

Other possibly helpful links:

Community
  • 1
  • 1
JSuar
  • 21,056
  • 4
  • 39
  • 83
  • For point #1 - using FindByIdentity while searching for the Name gives me a null ContactPrincipal, where as using the query-by-example gives me a legit contact albeit with no SID. I am not adverse to using SIDs, I'm saying that they are not on the contact principal objects that I am getting back and this is preventing me from saving the contact. Should I be adding one to the contact? – Ryan Bennett Jan 08 '13 at 14:31
  • 1
    Not sure why the SIDs are not automatically generated. However, have you tried implementing a `UserPrincipal` rather than a `AuthenticablePrincipal`. Seems more appropriate considering the attributes you are storing. [UserPrincipal Class](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal.aspx) "With UserPrincipal, you have many properties to get and set information about the user, for example, EmployeeId, EmailAddress, GivenName, and VoiceTelephoneNumber." [See more here.](http://p2p.wrox.com/content/articles/directory-services?page=0,5) – JSuar Jan 08 '13 at 14:52
  • Your code example worked for me. Thank you very much. I am going to switch to UserPrincipal and see how that works for me as well - might clear up some problems I have with group.Members searches in general. Thanks again. – Ryan Bennett Jan 08 '13 at 15:16
  • Hope I still earn the bounty. :] – JSuar Jan 10 '13 at 13:03
  • Sorry - I thought accepting automatically gave you the bounty. Fixed – Ryan Bennett Jan 10 '13 at 15:15
1

Possibly your value for the group or the contact is not the expected distinguishedName?

There is however no reason that a contact can not be a member of a security group, or that a security enabled user or group object may not be a member of a distribution group.

Maybe go back to the basics first?

This (minimalistic) code works perfectly assuming the user has permissions. If not, use the proper user name and password with the directoryEntries.

public static void AddContactToGroup(string ContactDN, string GroupDN)
{
  try
  {
    using (DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://" + GroupDN))
    {
      directoryEntry.RefreshCache();
      DirectoryEntry Contactgroup = new DirectoryEntry("LDAP://" + ContactDN);
      directoryEntry.Properties["member"].Add(Contactgroup.Properties["distinguishedName"].Value);
      directoryEntry.CommitChanges();
    }
  }
  catch (Exception e)
  {
    string msg = e.Message.ToString();
    throw e;
  }
}

ContactDN is the contact's distinguishedName. GroupDN is the group's distinguishedName.

If this works for you, add on what you need. If not. figure out why.

Daro
  • 1,990
  • 2
  • 16
  • 22
0

It sounds like your user either has no SID or is trying to use one that is already taken. Have you looked and what the SID is? Also JSuar is right, with AD it is ALWAYS (I can't stress that enough) a good idea to check for null, the AD objects can return null and not throw any errors, a null check never hurt anybody.

Joshua G
  • 2,086
  • 3
  • 19
  • 22
  • There is no SID on the contact. I don't know if this is ok or not. If it's not, I don't know what to do about it. – Ryan Bennett Jan 08 '13 at 14:20
  • http://technet.microsoft.com/en-us/library/cc961625.aspx might offer some help, I would look at Microsoft's site and see if they have any info on implementing DirectoryObjectClass, you may need to define a group and add the user to the group, like DomainUsers. – Joshua G Jan 08 '13 at 21:13