I’ve been doing a lot with variations recently and have encountered numerous challenges with keeping data and lists in sync between the various variations. One of the things that we ended up implementing on my current project was a simple event receiver which copied items from one variation to another when that item was approved. From this you can obviously conclude that we had matching lists in each variation. One of the challenges with this was keeping the security on the lists the same between each of the variations and being able to set that security without being able to read the menus of the site (the other variation was in Korean).

So what I decided to create was a simple command that that would enable me to export the security of a list to an XML file. Once I had the XML I could then create an import command which would allow me to set the security on the lists in the other variations or even in other farms so I could get it set in our test farm and then import to production. The first command I created is called, simply enough, gl-exportlistsecurity. I’ll talk about the import command in a follow up post.

Creating this command was actually really easy because I already had a command which would copy the security settings of a source list to a destination list. I could have obviously used this command in a single farm only environment but I wanted the ability to set the security in a test farm and import to the production farm so my gl-copylistsecurity command wouldn’t really work for me. The code also exports folder level and optionally item level security. The code can be seen below:

  1/// <summary>
  2/// Exports the security.
  3/// </summary>
  4/// <param name="outputFile">The output file.</param>
  5/// <param name="scope">The scope.</param>
  6/// <param name="url">The URL.</param>
  7/// <param name="includeItemSecurity">if set to <c>true</c> [include item security].</param>
  8public static void ExportSecurity(string outputFile, string scope, string url, bool includeItemSecurity)
  9{
 10    using (SPSite site = new SPSite(url))
 11    using (SPWeb web = site.OpenWeb())
 12    {
 13        StringBuilder sb = new StringBuilder();
 14 
 15        XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
 16        xmlWriter.Formatting = Formatting.Indented;
 17 
 18        Log("Start Time: {0}.", DateTime.Now.ToString());
 19 
 20 
 21        if (scope == "list")
 22        {
 23            SPList list = Utilities.GetListFromViewUrl(web, url);
 24 
 25            if (list == null)
 26                throw new SPException("List was not found.");
 27            ExportSecurity(list, web, xmlWriter, includeItemSecurity);
 28        }
 29        else
 30        {
 31            xmlWriter.WriteStartElement("Lists");
 32            foreach (SPList list in web.Lists)
 33            {
 34                ExportSecurity(list, web, xmlWriter, includeItemSecurity);
 35            }
 36            xmlWriter.WriteEndElement(); //Lists
 37        }
 38        xmlWriter.Flush();
 39 
 40        File.WriteAllText(outputFile, sb.ToString());
 41 
 42        Log("Finish Time: {0}.\r\n", DateTime.Now.ToString());
 43 
 44    }
 45}
 46 
 47/// <summary>
 48/// Exports the security.
 49/// </summary>
 50/// <param name="list">The list.</param>
 51/// <param name="web">The web.</param>
 52/// <param name="xmlWriter">The XML writer.</param>
 53/// <param name="includeItemSecurity">if set to <c>true</c> [include item security].</param>
 54public static void ExportSecurity(SPList list, SPWeb web, XmlTextWriter xmlWriter, bool includeItemSecurity)
 55{
 56    try
 57    {
 58        Log("Progress: Processing list \"{0}\".", list.RootFolder.ServerRelativeUrl);
 59 
 60        xmlWriter.WriteStartElement("List");
 61        xmlWriter.WriteAttributeString("WriteSecurity", list.WriteSecurity.ToString());
 62        xmlWriter.WriteAttributeString("ReadSecurity", list.ReadSecurity.ToString());
 63        xmlWriter.WriteAttributeString("Url", list.RootFolder.Url);
 64 
 65        // Write the security for the list itself.
 66        WriteObjectSecurity(list, xmlWriter);
 67 
 68        // Write the security for any folders in the list.
 69        WriteFolderSecurity(list, xmlWriter);
 70 
 71        // Write the security for any items in the list.
 72        if (includeItemSecurity)
 73            WriteItemSecurity(list, xmlWriter);
 74 
 75        xmlWriter.WriteEndElement(); // List
 76    }
 77    finally
 78    {
 79        Log("Progress: Finished processing list \"{0}\".", list.RootFolder.ServerRelativeUrl);
 80    }
 81}
 82 
 83/// <summary>
 84/// Writes the folder security.
 85/// </summary>
 86/// <param name="list">The list.</param>
 87/// <param name="xmlWriter">The XML writer.</param>
 88public static void WriteFolderSecurity(SPList list, XmlTextWriter xmlWriter)
 89{
 90    foreach (SPListItem folder in list.Folders)
 91    {
 92        Log("Progress: Processing folder \"{0}\".", folder.Url);
 93 
 94        xmlWriter.WriteStartElement("Folder");
 95        xmlWriter.WriteAttributeString("Url", folder.Url);
 96        WriteObjectSecurity(folder, xmlWriter);
 97        xmlWriter.WriteEndElement(); // Folder
 98    }
 99}
100 
101/// <summary>
102/// Writes the item security.
103/// </summary>
104/// <param name="list">The list.</param>
105/// <param name="xmlWriter">The XML writer.</param>
106public static void WriteItemSecurity(SPList list, XmlTextWriter xmlWriter)
107{
108    foreach (SPListItem item in list.Items)
109    {
110        Log("Progress: Processing item \"{0}\".", item.ID.ToString());
111 
112        xmlWriter.WriteStartElement("Item");
113        xmlWriter.WriteAttributeString("Id", item.ID.ToString());
114        WriteObjectSecurity(item, xmlWriter);
115        xmlWriter.WriteEndElement(); // Item
116    }
117}
118 
119/// <summary>
120/// Writes the object security.
121/// </summary>
122/// <param name="sourceObject">The source object.</param>
123/// <param name="xmlWriter">The XML writer.</param>
124public static void WriteObjectSecurity(ISecurableObject sourceObject, XmlTextWriter xmlWriter)
125{
126    xmlWriter.WriteAttributeString("HasUniqueRoleAssignments", sourceObject.HasUniqueRoleAssignments.ToString());
127 
128    if (!sourceObject.HasUniqueRoleAssignments)
129        return;
130 
131    //xmlWriter.WriteRaw(sourceObject.RoleAssignments.Xml);
132 
133    xmlWriter.WriteStartElement("RoleAssignments");
134    foreach (SPRoleAssignment ra in sourceObject.RoleAssignments)
135    {
136        xmlWriter.WriteStartElement("RoleAssignment");
137        xmlWriter.WriteAttributeString("Member", ra.Member.Name);
138        SPPrincipalType pType = SPPrincipalType.None;
139        if (ra.Member is SPUser)
140             pType = SPPrincipalType.User;
141        else if (ra.Member is SPGroup)
142            pType = SPPrincipalType.SharePointGroup;
143 
144        xmlWriter.WriteAttributeString("PrincipalType", pType.ToString());
145 
146        xmlWriter.WriteStartElement("RoleDefinitionBindings");
147        foreach (SPRoleDefinition rd in ra.RoleDefinitionBindings)
148        {
149            if (rd.Name == "Limited Access")
150                continue;
151 
152            xmlWriter.WriteStartElement("RoleDefinition");
153            xmlWriter.WriteAttributeString("Name", rd.Name);
154            xmlWriter.WriteAttributeString("Description", rd.Description);
155            xmlWriter.WriteAttributeString("Order", rd.Order.ToString());
156            xmlWriter.WriteAttributeString("BasePermissions", rd.BasePermissions.ToString());
157            xmlWriter.WriteEndElement(); //RoleDefinition
158        }
159        xmlWriter.WriteEndElement(); //RoleDefinitionBindings
160        xmlWriter.WriteEndElement(); //RoleAssignment
161    }
162    xmlWriter.WriteEndElement(); //RoleAssignments
163}

The syntax of the command can be seen below:

C:\>stsadm -help gl-exportlistsecurity

stsadm -o gl-exportlistsecurity

Exports a list's security settings to an XML file.

Parameters:
        -url <list view url to export security from>
        -outputfile <file to output settings to>
        -scope <Web | List>
        [-quiet]
        [-includeitemsecurity]

The following table summarizes the command and its various parameters:

Command NameAvailabilityBuild Date
gl-exportlistsecurityWSS v3, MOSS 2007Released: 4/12/2008, Updated: 8/28/2008
Parameter NameShort FormRequiredDescriptionExample Usage
scopesYesEither web or list. If web then the security for each list within the web will be exported. Note that this does not work recursively – only the lists in the specified web will be exported.-scope web, -s web
urlYesThe URL to either a specific list (if scope is list) or to a web if scope is web.-url http://portal
outputfilefileYesThe output path to save the results to. Must be a valid filename.-outputfile "c:\security.xml", -file "c:\security.xml"
quietNoIf specified then progress information will not be output to the console.-quiet
-includeitemsecurityitemsNoIf specified then item level security will be exported.-includeitemsecurity, -items

Here’s an example of using the command to export the security settings on the Documents library which is set to inherit security:

stsadm -o gl-exportlistsecurity -url http://portal/documents/forms/allitems.aspx -outputfile c:\security.xml -scope list

The output from running the above command would be something like (note that the list contained two folders, each inheriting as well):

1<List WriteSecurity="1" ReadSecurity="1" Url="Documents" HasUniqueRoleAssignments="False">
2  <Folder Url="Documents/Test Folder 1" HasUniqueRoleAssignments="False" />
3  <Folder Url="Documents/Test Folder 1/Test Folder 1" HasUniqueRoleAssignments="False" />
4</List>

If we change the list so that it no longer inherits it’s security and re-run the command we’d get results similar to the following:

 1<List WriteSecurity="1" ReadSecurity="1" Url="Documents" HasUniqueRoleAssignments="True">
 2  <RoleAssignments>
 3    <RoleAssignment Member="SharePoint Administrator" PrincipalType="User">
 4      <RoleDefinitionBindings />
 5    </RoleAssignment>
 6    <RoleAssignment Member="Home Owners" PrincipalType="SharePointGroup">
 7      <RoleDefinitionBindings>
 8        <RoleDefinition Name="Full Control" Description="Has full control." Order="1" BasePermissions="FullMask" />
 9      </RoleDefinitionBindings>
10    </RoleAssignment>
11    <RoleAssignment Member="Home Visitors" PrincipalType="SharePointGroup">
12      <RoleDefinitionBindings>
13        <RoleDefinition Name="Read" Description="Can view only." Order="6" BasePermissions="ViewListItems, OpenItems, ViewVersions, ViewFormPages, Open, ViewPages, CreateSSCSite, BrowseUserInfo, UseClientIntegration, UseRemoteAPIs, CreateAlerts" />
14      </RoleDefinitionBindings>
15    </RoleAssignment>
16    <RoleAssignment Member="Home Members" PrincipalType="SharePointGroup">
17      <RoleDefinitionBindings>
18        <RoleDefinition Name="Contribute" Description="Can view, add, update, and delete." Order="5" BasePermissions="ViewListItems, AddListItems, EditListItems, DeleteListItems, OpenItems, ViewVersions, DeleteVersions, ManagePersonalViews, ViewFormPages, Open, ViewPages, CreateSSCSite, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, UseClientIntegration, UseRemoteAPIs, CreateAlerts, EditMyUserInfo" />
19      </RoleDefinitionBindings>
20    </RoleAssignment>
21    <RoleAssignment Member="Style Resource Readers" PrincipalType="SharePointGroup">
22      <RoleDefinitionBindings />
23    </RoleAssignment>
24    <RoleAssignment Member="Designers" PrincipalType="SharePointGroup">
25      <RoleDefinitionBindings>
26        <RoleDefinition Name="Design" Description="Can view, add, update, delete, approve, and customize." Order="2" BasePermissions="ViewListItems, AddListItems, EditListItems, DeleteListItems, ApproveItems, OpenItems, ViewVersions, DeleteVersions, CancelCheckout, ManagePersonalViews, ManageLists, ViewFormPages, Open, ViewPages, AddAndCustomizePages, ApplyThemeAndBorder, ApplyStyleSheets, CreateSSCSite, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, UseClientIntegration, UseRemoteAPIs, CreateAlerts, EditMyUserInfo" />
27      </RoleDefinitionBindings>
28    </RoleAssignment>
29    <RoleAssignment Member="Hierarchy Managers" PrincipalType="SharePointGroup">
30      <RoleDefinitionBindings>
31        <RoleDefinition Name="Manage Hierarchy" Description="Can create sites and edit pages, list items, and documents." Order="3" BasePermissions="ViewListItems, AddListItems, EditListItems, DeleteListItems, OpenItems, ViewVersions, DeleteVersions, CancelCheckout, ManagePersonalViews, ManageLists, ViewFormPages, Open, ViewPages, AddAndCustomizePages, ViewUsageData, CreateSSCSite, ManageSubwebs, ManagePermissions, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, ManageWeb, UseClientIntegration, UseRemoteAPIs, ManageAlerts, CreateAlerts, EditMyUserInfo, EnumeratePermissions" />
32      </RoleDefinitionBindings>
33    </RoleAssignment>
34    <RoleAssignment Member="Approvers" PrincipalType="SharePointGroup">
35      <RoleDefinitionBindings>
36        <RoleDefinition Name="Approve" Description="Can edit and approve pages, list items, and documents." Order="4" BasePermissions="ViewListItems, AddListItems, EditListItems, DeleteListItems, ApproveItems, OpenItems, ViewVersions, DeleteVersions, CancelCheckout, ManagePersonalViews, ViewFormPages, Open, ViewPages, CreateSSCSite, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, UseClientIntegration, UseRemoteAPIs, CreateAlerts, EditMyUserInfo" />
37      </RoleDefinitionBindings>
38    </RoleAssignment>
39    <RoleAssignment Member="Restricted Readers" PrincipalType="SharePointGroup">
40      <RoleDefinitionBindings>
41        <RoleDefinition Name="Restricted Read" Description="Can view pages and documents, but cannot view historical versions or review user rights information." Order="7" BasePermissions="ViewListItems, OpenItems, Open, ViewPages" />
42      </RoleDefinitionBindings>
43    </RoleAssignment>
44    <RoleAssignment Member="Quick Deploy Users" PrincipalType="SharePointGroup">
45      <RoleDefinitionBindings />
46    </RoleAssignment>
47    <RoleAssignment Member="Viewers" PrincipalType="SharePointGroup">
48      <RoleDefinitionBindings>
49        <RoleDefinition Name="View Only" Description="Members of this group can view pages, list items, and documents. If the document has a server-side file handler available, they can only view the document using the server-side file handler." Order="2147483647" BasePermissions="ViewListItems, ViewVersions, ViewFormPages, Open, ViewPages, CreateSSCSite, BrowseUserInfo, UseClientIntegration, UseRemoteAPIs, CreateAlerts" />
50      </RoleDefinitionBindings>
51    </RoleAssignment>
52  </RoleAssignments>
53  <Folder Url="Documents/Test Folder 1" HasUniqueRoleAssignments="False" />
54  <Folder Url="Documents/Test Folder 1/Test Folder 1" HasUniqueRoleAssignments="False" />
55</List>

You may have noticed that I provide lots of details for the role definition bindings – this is so that I can recreate the bindings in a new environment if they are missing.

Update 8/28/2008: I’ve updated the code so that it now optionally exports item level security.