Udostępnij za pośrednictwem


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

Comments

  • Anonymous
    April 10, 2008
    PingBack from http://stevepietrek.com/2008/04/10/links-4102008/

  • Anonymous
    April 10, 2008
    Suche FTP-Sites mit SharePoint 2007 / Search Server 2008 indizieren Tools Smart Search for SharePoint

  • Anonymous
    April 11, 2008
    Suche FTP-Sites mit SharePoint 2007 / Search Server 2008 indizieren Tools Smart Search for SharePoint

  • Anonymous
    April 16, 2008
    Hi Mark, Thanks for the good article. We have the same requirement where we are supposed to disable create rights on the MySite for all users for now. In future it should be easily CONFIGURABLE to allow certain users the ability to create lists, discussions, blogs etc. We have taken the same code from your article, everything seems to be working except removing the FullControl role definition binding for the owner. When we debug, we see the user has only the custom role definition, but it actually still exists once the site is created. When we go to site permissions, we see Full Control along with custom role definition. Because of this, though the user is not a site collection administrator, he still gets all create rights. What could be the problem? Is it because the the site is already provisioned when the feature is activated? Also with the above approach, there is a limitation that in future if some users have to be given access to creating lists, discussion forums etc. we'll not be able to do it, unless we run a batch file to again add the user to site collection administrator or give appropriate rights. Is there a way to do this, say When the mysite is getting loaded, check an xml or external list which has the list of people who can be given create rights and based on that enable site actions, create permissions etc? Regards Rajesh

  • Anonymous
    April 16, 2008
    Hmm… someone else was having this problem, I haven’t heard back from them on any progress.  How did you implement the code from the blog, did you invoke it from a master page control as Steve Peschka described in his article?  Are any of the catch blocks being executed?  Can you log what’s going on from ReplacePrimarySiteAdministrator?   I might try to make another test of this but have been very busy and probably can’t get to it for awhile.  The code worked successfully for me when I wrote it, but I had to pull it out of context for the blog.  Maybe I missed something from where these methods were called from.  I remember it was a little hard getting everything to work.  Logging is your friend here to see what the code is doing.

  • Anonymous
    April 16, 2008
    Thanks for the reply. Currently we have all the code in the FeatureActivated event of the MySiteStaplee feature. We don't get any exception, but the Full Control role definition is not being removed from the roleassignment of the user. What we'll try is to put the code in the master page webcontrol as you suggested. There is a function "ReplacePrimarySiteAdministrator" which is not being called any where now. I guess it has to be called before we remove Full control for the current owner. Do you have any suggestion on how to enable create rights for certain users in future? Is it possible to enable/disable create rights for mysite through some configuration entries?

  • Anonymous
    April 16, 2008
    Hi Mark, i had sent u a mail regarding the same problem as told by Rajesh in the above posting and u had asked me what approach i am using - i have added this permission related code to the feature activation event (as said in Steve Peschka's blog). and no exception is caught in ReplacePrimarySiteAdministrator function. The ReplacePrimaryAdministrator is working fine as the site collection admin is replaced.The problem is, the original owner is still retaining the Full Control permission.

  • Anonymous
    April 17, 2008
    Thanks Mark for your help. The original code worked fine when we moved it as a part of webcontrol in the master page from the FeatureActivated event..

  • Anonymous
    April 18, 2008
    Rajesh, you're welcome... I integrated the original code that worked for you into an update of the blog and removed the comment that included that code on this commments page to avoid further confusion.

  • Anonymous
    April 22, 2008
    The comment has been removed

  • Anonymous
    April 23, 2008
    M. Bharg, If I am interpreting what you mean by "public site collection," this is the one at the root of the MySite host web application.  See http://blogs.msdn.com/markarend/archive/2008/02/22/mysite-pages-and-architecture.aspx for an explanation of how MySite site collections and the "Profile" or "Person" site collection are set up.  Your web control is probably executing with the identity of the MySite user, so you need to give all those users rights to the list.  Also, look at your code to see how you are getting to the list... do you open the site collection or even the web application?  Whatever your code is accessing, your MySite users must have the rights to.  Or, as an alternative, you could use RunWithElevatedPrivilege in your code so that it runs under a SharePoint system identity.  An example of doing this is in the code sample for this blog entry.

  • Anonymous
    April 28, 2008
    Hi Mark, using RunWithElevatedPrivileges, i was able to access the list, and the list items, but when i try to change the site collection admin i get access denied exception. I have followed ur approach and so when my MySite is created i have only read permissions. Using RunWithElevatedPrivileges can i add myself back to the site collection administrators ? Is this possible ?

  • Anonymous
    April 30, 2008
    Sorry this is not working for you yet.  Logging exceptions is the key to debugging here. Yes, using RunWithElevatedPrivileges you should be able to add yourself back.  Note with this call, there are some restrictions on how you must do things.  For instance, if you create an SPSite object, then use RunWithElevatedPrivileges to access it, it will NOT be accessed with elevated privileges... you must create the SPSite object in the anonymous delegate.  Be sure to dispose the object if you created it anew.  Reference blogs that discuss it.  http://geekswithblogs.net/DanielC/archive/2008/01/28/sharepoint-security-runwithelevatedprivileges.aspx is good. Here's the brief MSDN overview  http://msdn.microsoft.com/en-us/library/aa543467.aspx

  • Anonymous
    May 06, 2008
    Thanks Mark, thanks a lot.. the mistake i had done was - i was creating the SPSite object outside RunWithElevatedPrivileges delegate. Now it works.