Modifying My Site Owner Security

This blog posting applies to Microsoft Office SharePoint Server 2007.

When someone creates a MySite, they are automatically given full control of that site.  More accurately: a MySite is a site collection; the creator becomes the first & only Site Collection Administrator; the creator is made a member of the Owners group. 

Some people feel that this is too much control, and they'd like to limit it in some way.  My first piece of advice in this situation is: don't.  MySites are built with dependencies on the permissions and identity of the owner, the person who created the site.  Changing this can introduce undesirable or confusing behavior.  Then, if requirements later change, after hundreds or thousands of people have created their MySite, more code must be written to effect those changes.  But if you insist on going down this path, I found a way to meet this seemingly simple request.  The answer is most certainly not simple.

Basically, you have to follow Steve Peschka’s approach described in the blog entry customizing My Sites as the framework for doing this.  Then, add code to that scheme that knows how to change the security.  There are a few added challenges:

  • You must do two things: 1) change the creator’s group membership and 2) remove them as a Site Collection Administrator.
  • To give the creator of the MySite specific privileges, you typically want to create a custom privilege level and add the user into that group.  The benefit is that you can tweak this to allow/prevent actions at a fairly granular level, such as being able to block the ability to add subsites.
  • The creator of the MySite is the only Site Collection Administrator, so you can’t remove this person from this role until you first put someone else in there.  For best governance, this should be a user account that’s not really associated with an individual, but shared among trusted admins.  Unfortunately it must be a user account, not a group.
  • There seems to be a timer job that fixes up the title of the MySite to match the name of the site administrator… so you need some code that resets this back every time the user opens the site.

Why can’t we just change the MySite site definition (template)?  Two reasons:

  • Modifying the MySite site definition is “unsupported” by Microsoft.  If you did it, then wanted help with problems related to it, the support engineer would make a best effort, but could not guarantee a fix.  Steve’s article talks more about this.
  • Moot point!  User security is not stored in the site definition, so even if it were supported to modify it, there would be nothing to do.

Below are code samples that address the challenges listed above.  Your job would be to integrate them with Steve Peschka’s code.  Sorry I can’t give a more complete solution.  This is one task that is very difficult to accomplish in SharePoint, and the methods described are the only way we know of doing it in a supported way.

(BTW, I use lots of try...catch blocks here to get the most granular logging.  You can implement Utility.LogMessage any way you like; I use MSDN's Trace Log Example to write to the ULS logs, and CodePlex's Log Viewer Feature to read them.)

 public void RestrictMySiteOwner()
{
    SPUserInfo originalOwnerInfo = new SPUserInfo();
    SPUserInfo newOwnerInfo = new SPUserInfo();

    // Store the original owner in site properties so we can restore it later if necessary
    try
    {
        StoreValueInSiteProperty(_curWeb, "OriginalOwner", MakeUserInfoString(originalOwnerInfo));
        Utility.LogMessage("Stored original administrator in site property.");
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't store original administrator in site property.  " + ex.Message);
    }

    // Switch out the primary site administrator with a pre-defined one.
    try
    {
        newOwnerInfo.LoginName = "DOMAIN\MySitesAdmin";
        newOwnerInfo.Email = "mysitesadmin@customer.com";
        newOwnerInfo.Name = "MySites domain manager";
        newOwnerInfo.Notes = "Owner of all MySites";
        originalOwnerInfo = ReplacePrimarySiteAdministrator(_curWeb, newOwnerInfo);
        Utility.LogMessage("Switched primary administrator.");
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't replace primary administrator.  " + ex.Message);
    }

    // Add the My Site creator to a group with limited permissions to control what is permissible on the site
    try
    {
        ChangeCurrentUserPermission(_curWeb, "Restricted Owner");
        Utility.LogMessage("Changed current user permissions.");
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't change current user permissions.  " + ex.Message);
    }

    // Reset the name of the site back to original owner (was changed by switching primary site admin)
    try
    {
        SetSiteTitle(_curWeb, originalOwnerInfo.Name);
        Utility.LogMessage("Reset site title.");
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't reset site title.  " + ex.Message);
    }
}


protected SPUserInfo ReplacePrimarySiteAdministrator(SPWeb site, SPUserInfo newAdminInfo)
{
    SPUser originalOwner = null;
    SPUserInfo originalOwnerInfo = new SPUserInfo();
 
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        // Get the parent site collection
        SPSite sitecollection = site.Site;

        originalOwner = sitecollection.Owner;
        originalOwnerInfo = Utility.GetUserInfo(originalOwner);

        if (newAdminInfo.LoginName != originalOwnerInfo.LoginName)
        {
            try
            {
                // Add new admin to Full Control group
                SPRoleDefinition admins = site.RoleDefinitions["Full Control"];
                if (admins != null)
                {
                    SPRoleAssignment roleAssignment = 
                        new SPRoleAssignment(newAdminInfo.LoginName, newAdminInfo.Email, 
                        newAdminInfo.Name, newAdminInfo.Notes);
                   SPRoleDefinitionBindingCollection roleDefBindings = 
                        roleAssignment.RoleDefinitionBindings;
                    roleDefBindings.Add(admins);
                    site.RoleAssignments.Add(roleAssignment);
                    //site.Update(); // Don't need to do this
                }

                // Reset owner of site collection to new admin
                sitecollection.Owner = site.Users[newAdminInfo.LoginName];
                sitecollection.Owner.Update();

                //change the userprofile guid of the site
                sitecollection.Properties["urn:schemas-microsoft-com:sharepoint:portal:profile:userprofile_guid"] = toProfile.ID.ToString().Replace("{", string.Empty).Replace("}", string.Empty); 
                sitecollection.Properties.Update(); 

            }
            catch (SPException ex)
            {
                // We may get here if the user running this thread is the same as the 
                // aministrator we're trying to remove.
                Utility.LogMessage("Error: can't replace primary administrator.  " + ex.Message);
            }
        }
    });

    return originalOwnerInfo;
}


protected void ChangeCurrentUserPermission(SPWeb site, string roleDefinitionName)
{
    SPUser curUser = site.CurrentUser;
    string curUserName = curUser.LoginName;

    SPRoleDefinition roleDefFull = null;         // Full control privilege level
    SPRoleDefinition roleDefContributor = null;  // Contributor privilege level
    SPRoleDefinition roleDefLimitedOwner = null; // Our new privilege level
            
    // Create new role definition
    try
    {
        // Get existing role definitions
        roleDefFull = site.RoleDefinitions["Full Control"];
        roleDefContributor = site.RoleDefinitions["Contribute"];

        // Create custom role definition
        roleDefLimitedOwner = new SPRoleDefinition();
        roleDefLimitedOwner.Name = roleDefinitionName;
        roleDefLimitedOwner.Description = "Permission settings for the owner of this MySite.";
        site.FirstUniqueRoleDefinitionWeb.RoleDefinitions.Add(roleDefLimitedOwner);
        site.FirstUniqueRoleDefinitionWeb.Update();

        // Get the new role definition again... makes sure the Update took all the way or something.
        roleDefLimitedOwner = site.FirstUniqueRoleDefinitionWeb.RoleDefinitions[roleDefinitionName];

        // Set permissions of new role... start from Contributor as base.
        roleDefLimitedOwner.BasePermissions = roleDefContributor.BasePermissions;
        // To ADD a permission, use Bitwise-Or assignment:       permissions |= permission
        // To REMOVE, use Bitwise-And assignment to Complement:  permissions &= ~permission
        roleDefLimitedOwner.BasePermissions |= SPBasePermissions.CancelCheckout;
        roleDefLimitedOwner.BasePermissions &= ~SPBasePermissions.AddDelPrivateWebParts;
        roleDefLimitedOwner.BasePermissions &= ~SPBasePermissions.UpdatePersonalWebParts;
        roleDefLimitedOwner.Update();
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't create new permission group \"" 
            + roleDefinitionName + "\". " + ex.Message);
    }
            
    // Add owner to new role definition
    try
    {
        SPRoleAssignment assignment = new SPRoleAssignment(curUser);
        if (roleDefLimitedOwner != null)
        {
            assignment.RoleDefinitionBindings.Add(roleDefLimitedOwner);
        }
        else
        {
            //Workaround: for now we just make owner a contributor
            assignment.RoleDefinitionBindings.Add(roleDefContributor);
        }
        site.RoleAssignments.Add(assignment);
        //site.Update(); // Don't need to do this
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't add user \"" + curUserName 
            + "\" to new permission group \"" + roleDefinitionName + "\".  " + ex.Message);
    }

    // Remove owner from Full Control
    try
    {
        SPRoleAssignment userRoleAssignment = UserRoleAssignment(site, curUser, roleDefFull);
        if (userRoleAssignment != null)
        {
            userRoleAssignment.RoleDefinitionBindings.Remove(roleDefFull);
            userRoleAssignment.Update();
        }
        //site.Update(); // Don't need to do this
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't remove user \"" + curUserName 
            + "\" from default permission groups.  " + ex.Message);
    }
}
 
 
private SPRoleAssignment UserRoleAssignment(SPWeb site, SPUser curUser, SPRoleDefinition curRoleDef)
{
    foreach (SPRoleAssignment roleAssignment in site.RoleAssignments)
    {
        if (roleAssignment.Member.ID == curUser.ID)
        {
            foreach (SPRoleDefinition roleDefinition in roleAssignment.RoleDefinitionBindings)
            {
                if (roleDefinition.Id == curRoleDef.Id)
                {
                    return roleAssignment;
                }
            }
        }
    }
    return null;
}


protected void StoreValueInSiteProperty(SPWeb site, string propertyName, string propertyValue)
{
    if (site.Properties.ContainsKey(propertyName))
        site.Properties[propertyName] = propertyValue;
    else
        site.Properties.Add(propertyName, propertyValue);
    site.Properties.Update();
}


protected void SetSiteTitle(SPWeb site, string title)
{
    try
    {
        site.Title = title;
        //site.Update(); // Don't need to do this
    }
    catch (Exception ex)
    {
        Utility.LogMessage("Error: can't set site title to \"" + title + "\".  " + ex.Message);
    }
}
 

Technorati Tags: MySite,SharePoint Security,SharePoint Development