MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Saturday, April 12, 2008

Exporting List Security Settings

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>
   8: public 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>
  54: public 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>
  88: public 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>
 106: public 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>
 124: public 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 Name Availability Build Date
gl-exportlistsecurity WSS v3, MOSS 2007 Released: 4/12/2008
Updated: 8/28/2008 

Parameter Name Short Form Required Description Example Usage
scope s Yes Either 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
url   Yes The URL to either a specific list (if scope is list) or to a web if scope is web. -url http://portal
outputfile file Yes The output path to save the results to.  Must be a valid filename. -outputfile "c:\security.xml"

-file "c:\security.xml"
quiet   No If specified then progress information will not be output to the console. -quiet
-includeitemsecurity items No If 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.

5 comments:

Sergey said...

Dear Gary,
Many thanks.
When you do the export you use the Name of User

121: xmlWriter.WriteAttributeString("Member", ra.Member.Name);

instead of UserLogin.

One can create many users with the same name and different UserLogins.

In our system we have many Active Directory groups for students. So, in each course web there is the user "Students" with UserLogin DmainName\courseXXXX_stdUG.
So, when trying to import security settings I get non correct user.

I am humbly waiting for gl-listuserrole. ))

george.mougos said...

Hello Gary, do you have anything that would export/import sub site permissions along with any list/document library permissions and not the information captured under the "All people" section. The reason I'mm asking is that I need to avoid at all costs using STSADM with the -includeusersecurity parameter which captures everything as mentioned above. I need to make roughly 3000 sub sites into top level sites. But by using what STSADM comes with out of the box it takes me roughly 20 minutes to export and 2 hours to import a 2GB subsite. That is because it captures all permissions which include the "All people" info. The problem is that this section has roughly 40000 people. That is why it takes me so long. Any suggestions would be greatly appreciated. Thank you George

Gary Lapointe said...

George - I don't have anything that will export the web level security settings. Using my command to export list level settings will result in any missing permission levels being created but that's about it. One thing you could try (on a test version) would be to use my export command to export the list and item security settings, then use my deletesiteusers command to clear out all the users, then move your sites around, and then use the importlistsecurity command to reapply security.

Matt K said...

Gary,
I am using you ExportListSecurity command and run into this error. The command completes the export of the Announcements and Calendar items then encounters this error. I am running the command from an account that has no problem running the native STSADM commands (Farm Admin, Site Collection Admin, Site Content DB Admin). Any help would be appreciated.
Matt,
04/01/2009 14:31:03.48 STSADM.EXE (0x0DEC) 0x1304 Windows SharePoint Services General 8e2s Medium Unknown SPRequest error occurred. More information: 0x80070005
04/01/2009 14:31:03.49 STSADM.EXE (0x0DEC) 0x1304 Windows SharePoint Services General 72ju High stsadm: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) Callstack: at Microsoft.SharePoint.SPGlobal.HandleUnauthorizedAccessException(UnauthorizedAccessException ex) at Microsoft.SharePoint.Library.SPRequest.GetRoleDefsDataAsSafeArray(String bstrUrl, UInt32 dwRolesScope, Guid& pguidScopeId, Int32 principalId, UInt32& pdwRowCount, Object& pvarDataSet) at Microsoft.SharePoint.SPRoleAssignmentCollection.Init() at Microsoft.SharePoint.SPRoleAssignmentCollection.Undirty() at Microsoft.SharePoint.SPBaseCollection.System.Collections.IEnumerable.GetEnumerator() at Lapointe.SharePoint.STSADM.Commands.Lists.ExportListSecurity.WriteObjectSecurity(ISecurableObject sourceObject, XmlTextWriter xmlWriter) at Lapointe.SharePoint.STSADM.Commands.Lists.Expo...
04/01/2009 14:31:03.49* STSADM.EXE (0x0DEC) 0x1304 Windows SharePoint Services General 72ju High ...rtListSecurity.ExportSecurity(SPList list, SPWeb web, XmlTextWriter xmlWriter, Boolean includeItemSecurity) at Lapointe.SharePoint.STSADM.Commands.Lists.ExportListSecurity.ExportSecurity(String outputFile, String scope, String url, Boolean includeItemSecurity) at Lapointe.SharePoint.STSADM.Commands.Lists.ExportListSecurity.Execute(String command, StringDictionary keyValues, String& output) at Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPOperation.Run(String command, StringDictionary keyValues, String& output) at Microsoft.SharePoint.StsAdmin.SPStsAdmin.RunOperation(SPGlobalAdmin globalAdmin, String strOperation, StringDictionary keyValues, SPParamCollection pars)

Gary Lapointe said...

Do you have full control on the site collection?