Michael's profileMike's RavingsBlogLists Tools Help

Blog


    November 20

    Programmatically Adding LDAP groups to MOSS groups

     

    I have seen a few posting for this on importing users or AD groups into a MOSS security group. For the most part it is very similar to the AD groups , however there are a couple little special things that can make it interesting and cost you a little time. Rather than annoy you with a lot of chatter, I have included functional code below to help you out. One critical thing, the groups MUST be prefixed with the LDAP provider name. This is the equivalent of the AD domain so when you get over the annoyance and stop to think about it, it makes sense.
    So let’s say your LDAP provider name is: “ACMELDAP” and your group name in the LDAP system is ACME_Managers. You would enter “acmeldap:ACME_Managers” as the LDAPFullRoleName. LDAPRoleName is the field for the friendly name. Anyway, I hope this helps someone out there out. Shoot me a comment/question if you come across a better way to do any of this.

     

    public bool AddLDAPRoleToMossGroup(string sharePointSiteURL, string LDAPFullRoleName, string LDAPRoleName, string MOSSGroupName)

            {

                try

                {

                    SPSite StationWorkspace;

                    SPWeb StationWorkspaceWeb;

                    SPUser LDAPGroupUser;

     

                   

                    StationWorkspace = new SPSite(sharePointSiteURL);

                    StationWorkspaceWeb = StationWorkspace.OpenWeb();

     

                    SPUser spUser = GetSPUser(LDAPFullRoleName,LDAPRoleName, sharePointSiteURL);

     

                    if (spUser == null)

                    {

                        spUser = CreateUser(LDAPFullRoleName, "", LDAPRoleName, "Added by SharePoint utilities", sharePointSiteURL);

                    }

     

                    foreach(SPGroup curGroup in StationWorkspaceWeb.SiteGroups)

                    {

                        if (curGroup.Name.ToUpper() == MOSSGroupName.ToUpper())

                        {

                            //Add and update group with new user

                            curGroup.AddUser(spUser);

                            curGroup.Update();   

                        }

                    }

     

                    return true;

                   

                }

                catch (Exception ex)

                {

                    CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:AddLDAPRoleToMossGroup - sharePointSiteURL: " + sharePointSiteURL + " LDAPRoleName: " + LDAPRoleName + " MOSSGroupName: " + MOSSGroupName + " Error:" + ex.ToString());

                    return false;

    }

                }

            }

    private SPUser GetSPUser(string LDAPFullRoleName, string strLoginName, string strSiteURL)

            {

                SPUser spReturn = null;

                SPSite spSite = null;

                SPWeb spWeb = null;

                try

                {

                    //Open the ShrePoint site

                    spSite = new SPSite(strSiteURL);

                    spWeb = spSite.OpenWeb();

     

                    //Check to see if user exists

                    spReturn = spWeb.AllUsers[LDAPFullRoleName];

                  }

                catch (Exception ex)

                {

     CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:GetSPUser - Error: " + ex.ToString());

                    return null;

                }

                finally

                {

                    spWeb.Close();

                    spSite.Close();

                }

     

                return spReturn;

            }

     

            //Creates a new user

            private SPUser CreateUser(string strLoginName, string strEMail, string strName, string strNotes, string strSiteURL)

            {

                SPUser spReturn = null;

                SPSite spSite = null;

                SPWeb spWeb = null;

     

                try

                {

                    //Open the SharePoint site

                    spSite = new SPSite(strSiteURL);

                    spWeb = spSite.OpenWeb();

     

                    //Assign role and add user to site

     SPRoleAssignment spRoleAssignment = new SPRoleAssignment(strLoginName, strEMail, strName, strNotes);

     

                    //Using Contribute, might need high access

                    SPRoleDefinition spSPRoleDefinition = spWeb.RoleDefinitions["View Only"];

     

                    spRoleAssignment.RoleDefinitionBindings.Add(spSPRoleDefinition);

                    spWeb.RoleAssignments.Add(spRoleAssignment);

     

                    //Update site

                    spWeb.Update();

                    spReturn = spWeb.AllUsers[strLoginName];

     

                }

                catch (Exception ex)

                {

     CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:CreateUser - Error: " + ex.ToString());

                    spReturn = null;

                }

                finally

                {

                    spWeb.Close();

                    spSite.Close();

                }

     

                return spReturn;

    November 15

    Fun MOSS Quirks: Custom Site templates PageLayoutURL

           This particular one will nearly drive you to suicide. So you develop a sweet site and decide, hey, I am going to save this out to a site template (stp) file and deploy it out to my other MOSS farms. You got a nice welcome page that is created with a custom page layout you developed. You include your stp file(s), custom master pages, custom CSS, images, custom page layout(s) and other branding features in a wsp and deploy them to your other MOSS farm and activate the features. Everything is nice, then you create your first site using one of the templates and all hell breaks loose. Your custom welcome page will not load, in your researching you take a look at your page layout and associate content type. It is then, and only then you find...your page layout on your other server farm is piinting back to the original site you developed on. So what the heck is going on?
          What's going on is a field in MOSS called "pageLayoutURL". If is found via the page.ListItem[FieldiD.PageLayout] call. It contains a freaking fully qualified URL to the original site "http://" and all. Apparently, this field passed MS' testing process. It can create some serious issues for your site.
          As part of any feature rollout, I have included some new code to help me out in this. I pulled it off another blog (as soon as I find the URL I will post that fellows URL here so he can get proper credit).
          First you need to create a site using the sp file you pulled over in your feature. Next, you need to be sure publishing is turned on. Next you run the code I will include in this post against that site (it is fugly POC code so if you need to clean it up for your purposes, feel free). Basically, it cycles throught every page in a site and reset this value to the current site URL.  For my feature, I actually created some script that will provision a new site usin gthe template, turn on publishing, execute the fix code, turn off publishing, and save new template from the updated site and blow away the old template.
     
    sample code:
    PublishingPageCollection pages = publishingWeb.GetPublishingPages();
                foreach (PublishingPage page in pages)
                {
                    string pageLayoutUrl = (string)page.ListItem[FieldId.PageLayout];
                    utilities.WriteToLogFile("MOSSFunctions", "FixPages: Now Checking: " + page.Name + " Layout url is at: " + pageLayoutUrl);
                    //if pageLayoutURL is null or empty or page layout url index of "/catalogs" < 0)
                    if (string.IsNullOrEmpty(pageLayoutUrl) || pageLayoutUrl.IndexOf("/_catalogs/") < 0)
                    {
                        utilities.WriteToLogFile("MOSSFunctions", "FixPages: Condition one tripped");
                        continue;
                    }
                    SPFieldUrlValue url = new SPFieldUrlValue(pageLayoutUrl);

                    string temp = publishingWeb.Web.Site.ServerRelativeUrl.TrimEnd('/') +
                                  url.Url.Substring(url.Url.IndexOf("/_catalogs/"));
                    temp = publishingWeb.Web.Site.MakeFullUrl(temp);

                    if (url.Url.ToLowerInvariant() == temp.ToLowerInvariant())
                    {
                        utilities.WriteToLogFile("MOSSFunctions", "FixPages: Condition two tripped");
                        continue;
                    }

    Custom Membership Provider "Server not operational"

    You have coded your own Membership and Role providers to manage the authentication, role validation, user information, etc to a non-AD LDAP data store. You have utilized the standard DirectorySearcher, Directory,Entry, etc classes from the System.DirectoryServices namespace. The installation on central admin, the windows auth site, goes smoothly and you easily verify communication between these sites and LDAP as you add a couple LDAP users to the extended FBA site to make it possible for you to log in to that site and add other users/LDAP groups.

    You attempt to log into the FBA site and despite being a high level user your LDAP authentication fails. You check the logs from your membership provider and find that all attempts to access the LDAP server return a “server not operational” error. As an additional hint, you can look in your SYSTEM event log and find an exception kicked out by “Schannel”, just once. It generally will not repeat until you reboot the server. The text of that exception will be something like:

    “System.Runtime.InteropServices.COMException (0x8007203A): The server is not operational. Also, schannel event 36882 is logged to the event log: "The certificate received from the remote server was issued by an untrusted certificate authority. Because of this, none of the data contained in the certificate can be validated. The SSL connection request has failed. The attached data contains the server certificate.".  

    The code failing is the exact same code that runs fine on the other 2 sites (CA and non-FBA site) and you have installed the certificate for the SSL communication on your server.

    There are a couple circumstances that lead to this exception. The first thing to understand is this, the actual thread that is accessing the membership provider, on the central administration and Windows integration security site, is running under the logged in user, and NOT one of the MOSS service accounts. However, when you set up FBA on the 3rd site, in IIS is turned off integrated security, and enabled anonymous access. As a result, when you attempted to log into the FBA site, there was no windows login to run under. SharePoint will call and run the membership provider validation code under a “GenericIdentity” object user.  

    Now since the LDAP directory requires SSL, there are some certificates that need to be checked. The classes in System.DirectoryServices ( as opposed to System.DirectoryServices.Protocols ) automatically manage the whole certificate validation process for you.  When you are logged in as a known user, it will check the user HIVE for the certificate LDAP is passing to validate, when you are a GenericIdentity, it will check the machine root  for the certificate. This can present a problem since under certain circumstances it appears , that even though the certificate is in the trusted store,  ASP.Net 2.0 would not examine the machine-level certificate store. The result was, the LDAP server’s certificate could not be validated and the machine would NOT make the connection. The first time sChannel logged it in the SYSTEM log (and never again till I rebooted), though in the exception object returned by the catch statement you will only get “server is not operational”

    This is one area where the fix gets ugly. At the current time the only way I found around this issue was to code around it. You need to implement the LDAP connection and queries in the affected methods using the System.DirectoryServices.Protocols namespace level and you need to manually code a function to validate the certificate passed by the LDAP server to the membership/role provider. Keep in mind, the majority of the role provider calls even once you are logged in will be made with a genericidentity or as a user unknown to the server.

    November 07

    Fun SharePoint Quirks: Deleting Master Pages

         This was a little annoying thing I came across recently. I had a custom master page that I pushed in with a feature. I needed to update it and of course rolling the feature back, retracting and deleting the solution did not delete the master page. So I grudgingly went in to delete it manually. Imagine my surprise when I got an error telling me it was being referenced by something and could not be deleted. I went in and checked the file associations through Site Settings --> Manage Content and Structure.
        Navigate to the master pages folder, then check click the "Show related resources" button. You will find the master page is referencing itself. Thus you can NOT delete it...ever. Well not directly. turns out there is a way to remove it.
        First make sure that this is the ONLY thing referencing it, then create a new folder in the master pages folder, move the master page into that folder. You can delete the folder which kills the master page.