Friday, July 25, 2008

How to get data from your SharePoint lists in XML format?

A technique we used extensively in STS still applies to WSS: How to get data from your SharePoint lists in XML format. It's actually quite simple, and you don't need to use web services to get it.

WSS supports a number of protocols to interact with the data. SOAP and WEBDAV immediately come to mind for most of you, but you may be forgetting the elusive URL Protocol (GET). Luckily for you, it's simple to use, and returns data in the MS-standard rowset stream format.

Follow along with these steps, and you'll be pleased with the results, I'm certain.

Determine the GUID of a list whose data you need in XML.
Navigate to the allitems.aspx view of the list in question
Click the "Modify Settings and Columns" link on the left side
Copy the List's GUID (including curly braces) from the address bar
Construct the URL to retrieve the list's data in XML format (it's case sensitive, be careful!)
The requested object is http://servername/sitename/_vti_bin/owssvr.dll
The object will expect three parameters: Cmd, List and XMLDATA (case sensitive)
Since we're displaying items (in XML format), the value of the Cmd parameter should be Display
We want to grab the list whose GUID we determined in step one; the value of the List parameter is this GUID (including curly braces)
Of course, we want XML data to be returned; the value of the XMLDATA parameter will be TRUE
Putting this together, we get a URL that looks like this:
http://server/site/_vti_bin/owssvr.dll?Cmd=Display&List={E1D9FED5-2531-413F-8C0F-CAA5C6280E51}&XMLDATA=TRUE

Sit back and marvel at how easy it is to get a rowset out of SharePoint without using the Object Model or the Web Services. You can point a Data View web part to this URL and grab data from another SharePoint site without adding the whole site to your DV catalog listing.

You may find that it doesn't return EVERY field. That's right: It returns fields that are defined in the default "allitems" view of that particular list. If you want more fields, you have two choices:

Modify the allitems.aspx view to show more fields (easy)
Pass an additional URL parameter, View, with the GUID value of the view whose fields you want to return (a little more involved/tedious)

Feature Stapling

One of the big problems people faced in WSS (V2) was that user was not able to customize out of the box site definitions. One of the big reasons for this was that we might need to update that site definition in a Service Pack and overwrite user changes.
So what happens if you want to associate a Feature with a site definition?
Well, as you should know by now, it is not advisable to modify the out of the box site definitions. So how do you do this?
This is where Feature Stapling comes in...

Feature Stapling allows you to “staple” a Feature to a site definition without modifying it in any way. This means you can add your feature to all sites created using that site definition.
The idea is that you can staple a Feature to a configuration defined within a site definition so that the Feature is automatically activated whenever a new site is provisioned from that specific configuration.
To create a staple you actually create another Feature that does the staple. The second Feature must provide a FeatureSiteTemplateAssociation element which specifies the identifying GUID of the target feature along with a formatted string used to identify a site definition and a target configuration.
for example:

<FeatureSiteTemplateAssociation Id="29D85C25-170C-4df9-A641-12DB0B9D4130" TemplateName="STS#0" />

This is very powerful as it allows you to add functionality to site definitions without having to modify the site definitions themselves.

Now ... one last final note on this, if you want to staple your Feature to ALL site definitions then you can staple it to the GLOBAL site definition and it will be added to all sites that are created.

Thursday, July 24, 2008

SharePoint Designer - how to edit a page?

In SharePoint 2003 (WSSv2) with FrontPage, it was so easy to edit a page. Just open the page you want to edit in Internet Explorer and hit the "Edit in FrontPage" icon at the toolbar.
Well, the good news are that in SharePoint 2007 (WSSv3) the same applies, but only for some of the pages, while other pages will tell you "This page cannot be edited in SharePoint Designer".
This article will explain what is going on, and how to get each type of page to be editable in SharePoint Designer.
Lets start easy- when you create a WSSv3 web site from one of the default template such as team site, blank site etc...you will have no problems using the easy button -edit in SharePoint Designer.You can also open the SharePoint site or page for editing from within SharePoint Designer using the "Open Site" or "Open.." dialogs within the application.
When you open such a page from such a site directly from Internet Explorer, or from SharePoint Designer, you will see the page in SharePoint Designer, and be able to modify it using the designer mode or the code mode.
So far so good right?
So lets figure out why some of the pages give us the following message when we try to edit them in SharePoint Designer:
"This page cannot be edited in SharePoint Designer. You can edit the content in the browser, or edit the corresponding page layout in SharePoint Designer"
Why is that? well, simply because the page you are trying to edit is under the publishing feature. It is a publishing page, and as such, by default, gets it's layout from the layout page and the master page.
The only change you should be doing on such a page is edit it in the browser and add\remove\change web parts in it.
However, there is a way to work around this - detach it from it's layout page. This is similar to the ghosting\unghosting process that we had back in SharePoint 2003 (WSSv2), but with the added benefit that we can allways roll back the change.
It also means that in the first time you will edit that page, you will have to start from SharePoint Designer to do it:
Open the site that contains the page in SharePoint Designer (use "File>Open Site") and browse to the page you want to edit in the folder list.
Right click the file, and choose "Detach from page layout". This will unghost the file - copying it's layout from the layout page into the database, and so allowing you to edit it, just like you used to do in SharePoint 2003
Unlike sharepoint 2003, you can take it back, and reattach the file.

Wednesday, July 23, 2008

Leverage SPUtility.ValidateFormDigest() for CSRF mitigation

To protect against cross-site request forgery, be sure your custom built aspx pages call SPUtility.ValidateFormDigest(). Sharepoint pages includes a hidden token in the postback which is unique to the user and the request. ValidateFormRequest() can be called in your custom aspx pages directly, or if you inherit from the WSS master page, to ensure you’re leveraging this same protection.

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.

Don’t enable AllowUnsafeUpdates property to true. When set to true the AllowUnsafeUpdates value tells Sharepoint to allow actions and data from GET requests to modify the database. By not allowing GET requests to modify data (this is the default), you’ve effectively protected against CSRF attacks which exploit GET requests. This is supplementary to ValidateFormRequest() above which only works with POST requests.

Tuesday, July 22, 2008

SharePoint Elevated Privilege without RunWithElevatedPrivelege

You may need to elevate privilege if the current user doesn't have permission to read that object from the SPSite. In this case, you'll need to elevate privilege just to get the user token, but we don't want to perform any operations inside RunWithElevatedPrivilege, we only want to get a token out (which is basically a simple byte array). You could also cache the system token in the application if you needed to.
Best Practices for Elevated Privilege in SharePoint:
Elevated Privilege can be used to bypass or work with security, and can be performed either through SPSecurity or through impersonation techniques involving the SPUserToken and the SPSite class. It's one of the most misunderstood aspects of the SharePoint API, but in general you should always prefer impersonation using the SPSite class and SPUserToken objects. Here's my list of best practices for elevated privilege code in SharePoint that will help you create more reliable applications for the enterprise.
• Avoid using SPSecurity.RunwithElevatedPrivilege to access the SharePoint object model. Instead, use the SPUserToken to impersonate with SPSite.
• If you do use SPSecurity.RunwithElevatedPrivilege, dispose of all objects in the delegate. Do not pass SharePoint objects out of the RunwithElevatedPrivilege method.
• Only use SPSecurity.RunwithElevatedPrivilege to make network calls under the application pool identity. Don't use it for elevation of privilege of SharePoint objects.
• Always use the SPSite constructor with an SPUserToken to create an elevated privilege security context in SharePoint. To impersonate the system, use the SystemAccount.UserToken property of the current SPSite context, such as:
var site = new SPSite(SPContext.Current.Site.ID, SPContext.Current.Site.SystemAccount.UserToken);
• Avoid passing SharePoint objects between different security contexts (SPSite instances), with the exception of the SPUserToken used in the SPSite ctor. An SPUser object created from SPSite A cannot (reliably) be passed to SPSite B. This can be the source of obscure bugs in production that are difficult to reproduce in development. For example, an SPUser reference created from SPContext.Current.Site cannot reliably be used in an elevated site context, as the user reference may take on a different meaning in the alternate context.
• Never use elevated privilege to bypass security-- always use it to work with security.
• Restrict what assemblies can use elevated privilege by running in minimal trust, avoiding the GAC, and auditing any CAS policies deployed with vendor solutions.
A better way to do perform system actions is to impersonate the SHAREPOINT\system account. Impersonation is a concept that is built into the object model, but is underutilized by developers.
The SPSite object takes an SPUserToken object in its constructor in order to support impersonation. (This does require Impersonate="True" for the Microsoft.SharePoint.Security.SharePointPermission permission class). You can impersonate any user when creating the SPSite context-- so to get the system account, just use the magic system account "SHAREPOINT\system".
Here's a code sample of SYSTEM ACCOUNT impersonation. The SYSTEM ACCOUNT uses the login name "SHAREPOINT\system" internally while it will use the process identity (usually that means the application pool identity, but it could be the service identity if a task was run as a timer job) when making external network calls. Note that the account is abstracted, and when a request comes into the system AS this account it will take on the identity of SYSTEM ACCOUNT.
Here's the code sample. Grab a user object, and then grab the SPUserToken for impersonation:

var user = SPContext.Current.Web.AllUsers[@"SHAREPOINT\SYSTEM"];
var superToken = user.UserToken;
using (var site = new SPSite(SPContext.Current.Web.Url, superToken))
{
// This code runs under the security context of the SHAREPOINT\system
// for all objects accessed through the "site" reference. Note that it's a
// different reference than SPContext.Current.Site.
using(var elevatedWeb = site.OpenWeb())
{
// Perform actions as SYSTEM here
}
}


Because the thread identity hasn't changed, this will produce more stable code in most circumstances, although you should be aware that ONLY objects that are referenced from the elevated site context will run as system.