June 2005 - Posts

This is good news! There is a new version of the BizTalk Adapter for SharePoint released on the GotDotNet Workspace. It seems that the adapter was launched at TechEd USA, you can view the web cast online over here (including a demo of the BizTalk 2006 Adapter). Here is my little summary:

Features of V2:

  • Side by side install with V1 (no upgrade scenario)
  • Exposes properties in Context (like originating folder etc.)
  • Security Credentials per Endpoint (also accessible through context properties!)
  • Binary file support (!)

BizTalk 2006 WSS Adapter:

  • Includes all features of V2
  • Integrated setup, configuration, and deployment experience
  • Support of Form library, View, and List
  • Integrated Office InfoPath experience

I've wanted to write this post for a long time, the trigger to actually write it was my interview by Chris Kunicki for his Office Zealot podcast last week. During this podcast (which was really fun to do, thanks for having me Chris!) we discussed briefly some security problems web part developers could face. Podcasts are great but they are not suited to communicate code, so here's the accompanying post! When you're developing web parts, security could be one of the biggest problems to deal with. First of all you may face some problems to get your web part deployed and secondly you may have some Code Access Security problems. Let's take a look at both of them!

When you develop web parts using the traditional way (using the Visual Studio.NET Templates), you have to create your web part class and a DWP file before you can deploy your web part. Additionally you must alter the Web.Config of your SharePoint site, and add your web part in the SafeControls section. Once everything is in place, and you want to drag and drop your brand new web part on a page, you may get the message "A Web Part or Web Form Control on this Web Part Page cannot be displayed or imported because it is not registered on this site as safe". If you get this message, either something is wrong in your DWP or something is wrong in your Web.Config. Common mistakes are:

  • Wrong AssemblyName in DWP: if you have a strong named assembly (using a SNK file) your assembly element should contain the complete name (including the version, culture and public key token. An easy way to retrieve that name is the using the Reflector tool.
  • Wrong Assembly in Web.Config: same remark, the Assembly attribute should contain the complete name of your assembly.
  • Wrong TypeName DWP: the type name of your class is prefixed with the namespace. By default your namespace is the same as your Visual Studio project name.

Example of a DWP file:

<?xml version="1.0" encoding="utf-8"?> <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2" > <Title>SmartPart 1.0.0.0</Title> <Description>The SmartPart User Control Webpart</Description> <Assembly>SmartPart, Version=1.0.0.0, Culture=neutral, 
PublicKeyToken=dd064a5b12b5277a</Assembly> <TypeName>SmartPart.UserControlWebpart</TypeName> </WebPart>

Example of SafeControl node:

<SafeControl 
Assembly="SmartPart, Version=1.0.0.0, Culture=neutral, PublicKeyToken=dd064a5b12b5277a"
Namespace="SmartPart" TypeName="*" Safe="True" />

Using the SmartPart to create web parts solves these issues. You don't need to create a DWP file and you don't need to change your Web.Config! Once you have deployed your web part, you may have some problems regarding Code Access Security. Out-of-the-box SharePoint is running with a trust level that contains very limited privileges. For example: you can not access the object model or a web service; this will give you a security exception. If you take a look at the Web.Config file, you will see that SharePoint is running using the WSS_Minimal trust level (<trust level="WSS_Minimal" originUrl="" />). Where is this WSS_Minimal defined? Well if you look in the securityPolicy section of the Web.Config you will find:

<securityPolicy> <trustLevel name="WSS_Medium" 
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_mediumtrust.config" /> <trustLevel name="WSS_Minimal"
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_minimaltrust.config" /> </securityPolicy>

So WSS_Minimal is pointing to a trust level which is defined in the wss_minimaltrust.config on your hard disk. As you can see, another default trust level is WSS_Medium which will add permissions to access the object model. If you write a web part that uses the SharePoint object model, make sure you set the trust level to WSS_Medium. What about web parts that contain really dangerous stuff like accessing a web service or, even worse, writing a file on the hard disk? Well you need to make sure that you web part is running with elevated privileges. So far the theory, how can we do this? There are several possibilities:

  • Set the trust level in the Web.Config to full. This will solve all your CAS problems because everything will be running with full trust. Of course this is not a good solution, so use it only on your dev machine for testing reasons!
  • Deploy your web part assembly to the GAC instead of to the BIN folder. Assemblies deployed to the GAC will run with full trust, so it solves all CAS issues for the assembly in the GAC. Is this a good solution? In my opinion this is abusing the GAC to gain full trust permissions. I know some people would argue differently, but my advice: do not abuse the GAC for this. Especially during your development cycle, assemblies deployed to the GAC are cached so you will need an IISReset when you deploy a new version.
  • Create a custom policy file that will grant full trust to your own assembly. This is my favorite solution; it's the most difficult to implement, but it's very secure and you only have to do the work once.

In the custom policy file, you can grant for example full trust permissions only to your code. But what is your code, how do you identify your code? Well (once again in my opinion) the nicest way is to grant full trust to code that is signed with a specific public key. It's very secure and reusable, as long as you keep using the same public/private key pair (SNK) to sign your code, all your code will be running with full trust. If you want to use this technique, follow these steps:

  • Create your web part, and give your code a strong name by using a public/private key pair (SNK).
  • Create a customized policy file. The easiest thing to do is to start from the default policy files, for example the wss_mediumtrust.config file.
  • Copy that file and name the new file wss_customtrust.config for example. In this policy file you will need to add the following section, right after the FirstMatchCodeGroup:
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust"> <IMembershipCondition version="1" class="StrongNameMembershipCondition" 
PublicKeyBlob="your public key blob"> </IMembershipCondition> </CodeGroup>
  • By adding this section to the policy file you will assign full trust permissions to all the code that is signed with that specific public key blob.
  • How do you retrieve the public key blob? The easiest way to do this is to use following little trick:
    • Start the Microsoft.NET Framework 1.1 Configuration (from the Administrative Tools)
    • Open Runtime Security Policy/Enterprise/Code Groups/All Code (actually what you open doesn't matter because we're not going to make any changes in this tool).
    • Right click on the All Code node and choose "New..."
    • Choose "Dummy" as the name (don't worry we are not going to save this!)
    • As the condition type choose Strong Name.
    • Click the Import button and point to your web part dll.
    • The public key blob will appear in the text box so you can easily copy and paste it in the IMembershipCondition node.
    • Cancel everything in the Microsoft.NET Framework 1.1 Configuration tool. (Do not save any changes!)
  • Once you have saved the wss_customtrust.config in the same folder as the default policy files, you need to alter the Web.Config file so your new policy file will be used. So change the trust node to
<trust level="WSS_Custom" originUrl="" />

In the securityPolicy node, add following node:

<trustLevel name="WSS_Custom" 
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_customtrust.config" />

You're done. When you test your hard work, you may end up with the "Assembly <assemblyName> security permission grant set is incompatible" exception. Don't worry, you'll need an IISReset. To give you all a head start I've uploaded my own prepped wss_customtrust.config. It's a modified version of the wss_mediumtrust.config that already contains the UnionCodeGroup to give code singed with a specific public key blob full trust. The only thing you need to replace is CHANGE_THIS, put in your own public key blob over here. This may look like an awful lot of work, my advice do it right the first time and you won't have any CAS issues anymore.

Although using the SmartPart to create web parts has some advantages, it doesn’t solve your CAS problems. Make sure you’ve assigned a strong name to your Web Application project that contains your user control, and use that strong name in a custom policy file as described above.

If you want to learn more about CAS, I would recommend visiting Maxim Karpov’s blog. He has written a couple of must-read articles and he has helped me with my own CAS problems some times. :-)

When I’m delivering a SharePoint developer course I usually challenge my students to think of something that they would like to do in SharePoint with the object model. So far I always have been able to write the code, and today I got another interesting challenge: how can you add a web part to a web part page in code.

 

The code to accomplish this is quite easy, basically there are two scenarios: you add a web part that is displaying data for a list, or you add any other web part (e.g. a custom web part or a 3rd party web part like the SmartPart). The code for the first scenario is:

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;

// Get a reference to a web and a list
SPSite site = new SPSite("http://localhost:8000");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["Contacts"];

// Instantiate the web part
ListViewWebPart wp = new ListViewWebPart();
wp.ZoneID = "Left";
wp.ListName = list.ID.ToString("B").ToUpper();
wp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();

// Get the web part collection
SPWebPartCollection coll = 
	web.GetWebPartCollection("default.aspx", 
	Storage.Shared);

// Add the web part
coll.Add(wp); 

  

First you get a reference to the SPWeb in which you want to add the web part, and to the list you want to use (in this example the Contacts list). Next you create an instance of the ListViewWebPart class, in which you can set the ZoneID, the ListName and the ViewGuid. This is the tricky part, the ListName property should contain the ID of your list (a GUID), not the name of your list!! But the ListName property is of the type string, so you need to convert the List GUID to a string using .ToString(“B”).ToUpper(). The same goes for the ViewGuid. Finally you need to get a reference to the WebPartCollection for the page in which you want to add the web part (in this example the home page, being default.aspx). Now you can add the web part using the Add method.

 

For the second scenario, the code is as follows:

// Get a reference to a web and a list
SPSite site = new SPSite("http://localhost:8000");
SPWeb web = site.OpenWeb();

// Get the web part collection
SPWebPartCollection coll = 
	web.GetWebPartCollection("default.aspx", 
	Storage.Shared);

string dwp = @"<?xml version=""1.0"" encoding=""utf-8""?>
<WebPart xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns=""http://schemas.microsoft.com/WebPart/v2"">
<Title>SmartPart List 1.0.0.0</Title> <ZoneID>Left</ZoneID>
<Assembly>SmartPart, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=dd064a5b12b5277a</Assembly>
<TypeName>SmartPart.UserControlWebpart</TypeName>
<UserControlPath xmlns=""SmartPart"">
~\UserControls</UserControlPath></WebPart>"
; coll.Add(dwp);

The first section is the same, get a reference to your site and the WebPartCollection (you don’t need a reference to a list). Next you need to build a XML string containing the information about the web part you want to add. The contents of that string are the same as the DWP file that you need to create to use your custom web parts. A trick to figure out what needs to be in there (especially if you want to specify values for some properties) is to put the web part on a page (using the web interface), and choose “Export” from the dropdown menu of the web part. This will save the contents of the DWP in a file. Finally you can add the web part by calling the Add method of the WebPartCollection and using the dwp string as a parameter.

Here's a small tip for Patrick, since he's going to deliver a TechEd Session on Web Parts in ASP.NET 2.0. :-) An important thing in your web part pages is the functionality to switch to design mode (at run-time), so your users can change properties, location, connections etc. for the web parts. In the pre-Beta2 age, we had the WebPartPageMenu control for that, but in Beta 2 this control has been removed. The QuickStarts seem to be using a custom control that is simulating this behavior, but they don't provide the source for it. Luckily it's quite easy to re-create, you can switch by using WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode; and WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode;. Check out Darren's post for more info!
More Posts