Monday, March 15, 2010

SharePoint SPSite.AllowUnsafeUpdates

The Microsoft idea behind introducing the AllowUnsafeUpdates property is to protect you from cross-site scripting attacks.

MSDN Definition: Gets or sets a Boolean value that specifies whether to allow updates to the database as a result of a GET request or without requiring a security validation. Setting this property to true opens security risks, potentially introducing cross-site scripting vulnerabilities.

If you try to do any updates to lists, webs or any SharePoint objects that require an SPSite to be created first, you need to set AllowUnsafeUpdates to TRUE, otherwise it will through an error saying "System.Exception: Microsoft.SharePoint.SPException: The security validation for this page is invalid."

In order for the AllowUnsafeUpdates to work, that the Update method of the SPSite object needs to be called as well:

SPSite.AllowUnsafeUpdates = true;
SPSite.Update();

Also, you may want to try using a POST, rather than a GET in order to avoid having to set this property. If your code is processing a POST request then make sure you call SPUtility.ValidateFormDigest() before you do anything else. This will ensure that the post request is validated (that it is not a cross-site scripting attack) and after that you will not have to worry about AllowUnsafeUpdates, because its default value will be “true” after the form digest is validated.

If the HTTPContext.Current is null then AllowSafeUpdates will be always true. This is the case in rich clients where no cross-scripting is possible as there are simply no web requests.

When any object that implements ISecurable (those are SPWeb, SPList and SPListItem) breaks or reverts their role definition inheritance. This means every time you call SPRoleDefinitionCollection.BreakInheritance(), BreakRoleInheritance(), ResetRoleInheritance() or set the value of HasUniquePerm the AllowUnsafeUpdates property of the parent web will reset to its default value and you may need to set it back to true in order to do further updates to the same objects. So always set AllowUnsafeUpdates back to true after you break inheritance in an environment with HTTPContext.

Additional References:

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsite.allowunsafeupdates.aspx
http://hristopavlov.wordpress.com/2008/05/16/what-you-need-to-know-about-allowunsafeupdates/
http://hristopavlov.wordpress.com/2008/05/21/what-you-need-to-know-about-allowunsafeupdates-part-2/

SharePoint SPWeb.EnsureUser

MSDN Definition: Checks whether the specified login name belongs to a valid user of the Web site, and if the login name does not already exist, adds it to the Web site.

This method can not be called by everyone, as it requires some high level permissions. Your solution is to wrap the EnsureUser within RunWithElevatedPrivileges call.

According to the SharePoint SDK it should be enough to call in your code:

using (SPSite site = new SPSite("http://mywebsite.com"))
{
using (SPWeb web = site.OpenWeb())
{
string login = "MyUserName";
string groupName = "MyGroup";
SPUser user = web.EnsureUser(login);
SPGroup group = web.Groups[groupName];
group.AddUser(user);
}
}

The above code works correctly: the EnsureUser method returns a valid SPUser object which can be added to the MyGroup group.

If you run the above piece of code with Forms Based Authentication (FBA), all you see is an SPException: SharePoint cannot find the user.

Surprisingly the same piece of code executed from a control or an Application Page runs correctly.

At some point the Roles class tries to retrieve the system.web/roleManager configuration section. Because that section is available in the web.config of a Web Application with FBA configured, the code works properly. But for Console Applications it doesn't. What you can do is to create a config file like ConsoleApplication1.exe.config (assuming that ConsoleApplication1.exe is the name of your executable after building) and copy there that particular piece of configuration from your web.config. If you run your Console Application now, it should work perfectly.

Note:

To add an User to SharePoint Group, we need to add the User to the Site Collection. For instance if we write the following lines of code to add user to a group, if will throw error is user does not exist.

group.Users.Add(item,"","","");

To resolve this, we can easily use the following line of code:

SPUser userTemp = web.EnsureUser(item);

If the user is known on the site collection there are three ways to get it:

1. Gets the collection of user objects that represents all users who are either members of the site or who have browsed to the site as authenticated members of a domain group in the site.

SPUserCollection users = portalSite.RootWeb.AllUsers;

2. Gets the collection of all users that belong to the site collection.

SPUserCollection users = portalSite.RootWeb.SiteUsers;

3. Gets the collection of user objects that are explicitly assigned permissions on the Web site.

SPUserCollection users = portalSite.RootWeb.Users;

Additional References:

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.ensureuser.aspx
http://blog.mastykarz.nl/inconvenient-programmatically-sharepoint-users-spweb-ensureuser/
http://vspug.com/mirjam/2007/12/20/using-spweb-ensureuser-loginname-to-add-a-new-spuser-to-a-web/