If you saw my last post, Exporting List Security Settings, then you know what this post is all about. I needed a way to be able to copy the security settings from one variation to another or from one farm to another. I already had my gl-copylistsecurity command which could handle the first piece of this but I needed another set of commands to handle going from one farm to another. The first was gl-exportlistsecurity which handles the exporting – the second command is, obviously enough, gl-importlistsecurity.

Like the export code this was fairly easy for me to do because I already had the code for the copy command – it was just a matter of refactoring it so that the source could be an XML file rather than another list:

  1/// <summary>
  2/// Imports the security.
  3/// </summary>
  4/// <param name="inputFile">The input file.</param>
  5/// <param name="url">The URL.</param>
  6/// <param name="includeItemSecurity">if set to <c>true</c> [include item security].</param>
  7public static void ImportSecurity(string inputFile, string url, bool includeItemSecurity)
  8{
  9    using (SPSite site = new SPSite(url))
 10    using (SPWeb web = site.OpenWeb())
 11    {
 12        XmlDocument xmlDoc = new XmlDocument();
 13        xmlDoc.Load(inputFile);
 14 
 15        Log("Start Time: {0}.", DateTime.Now.ToString());
 16 
 17        foreach (XmlElement listElement in xmlDoc.SelectNodes("//List"))
 18        {
 19            SPList list = null;
 20 
 21            try
 22            {
 23                list = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/" + listElement.GetAttribute("Url"));
 24            }
 25            catch (ArgumentException) { }
 26            catch (FileNotFoundException) { }
 27 
 28            if (list == null)
 29            {
 30                Console.WriteLine("WARNING: List was not found - skipping.");
 31                continue;
 32            }
 33 
 34            ImportSecurity(list, web, includeItemSecurity, listElement);
 35 
 36        }
 37        Log("Finish Time: {0}.\r\n", DateTime.Now.ToString());
 38    }
 39}
 40 
 41/// <summary>
 42/// Imports the security.
 43/// </summary>
 44/// <param name="targetList">The target list.</param>
 45/// <param name="web">The web.</param>
 46/// <param name="includeItemSecurity">if set to <c>true</c> [include item security].</param>
 47/// <param name="listElement">The list element.</param>
 48internal static void ImportSecurity(SPList targetList, SPWeb web, bool includeItemSecurity, XmlElement listElement)
 49{
 50    Log("Progress: Processing list \"{0}\".", targetList.RootFolder.ServerRelativeUrl);
 51 
 52    try
 53    {
 54        int writeSecurity = int.Parse(listElement.GetAttribute("WriteSecurity"));
 55        int readSecurity = int.Parse(listElement.GetAttribute("ReadSecurity"));
 56 
 57        if (writeSecurity != targetList.WriteSecurity)
 58            targetList.WriteSecurity = writeSecurity;
 59 
 60        if (readSecurity != targetList.ReadSecurity)
 61            targetList.ReadSecurity = readSecurity;
 62 
 63        // Set the security on the list itself.
 64        SetObjectSecurity(web, targetList, targetList.RootFolder.ServerRelativeUrl, listElement);
 65 
 66        // Set the security on any folders in the list.
 67        SetFolderSecurity(web, targetList, listElement);
 68 
 69        // Set the security on any items in the list.
 70        if (includeItemSecurity)
 71            SetItemSecurity(web, targetList, listElement);
 72 
 73        targetList.Update();
 74 
 75    }
 76    finally
 77    {
 78        Log("Progress: Finished processing list \"{0}\".", targetList.RootFolder.ServerRelativeUrl);
 79    }
 80}
 81 
 82/// <summary>
 83/// Sets the folder security.
 84/// </summary>
 85/// <param name="web">The web.</param>
 86/// <param name="list">The list.</param>
 87/// <param name="listElement">The list element.</param>
 88private static void SetFolderSecurity(SPWeb web, SPList list, XmlElement listElement)
 89{
 90    foreach (XmlElement folderElement in listElement.SelectNodes("Folder"))
 91    {
 92        string folderUrl = folderElement.GetAttribute("Url");
 93        SPListItem folder = null;
 94        foreach (SPListItem tempFolder in list.Folders)
 95        {
 96            if (tempFolder.Folder.Url.ToLowerInvariant() == folderUrl.ToLowerInvariant())
 97            {
 98                folder = tempFolder;
 99                break;
100            }
101        }
102        if (folder == null)
103        {
104            Log("Progress: Unable to locate folder '{0}'.", EventLogEntryType.Warning, folderUrl);
105            continue;
106        }
107        SetObjectSecurity(web, folder, folderUrl, folderElement);
108    }
109}
110 
111/// <summary>
112/// Sets the item security.
113/// </summary>
114/// <param name="web">The web.</param>
115/// <param name="list">The list.</param>
116/// <param name="listElement">The list element.</param>
117private static void SetItemSecurity(SPWeb web, SPList list, XmlElement listElement)
118{
119    foreach (XmlElement itemElement in listElement.SelectNodes("Item"))
120    {
121        int itemId = int.Parse(itemElement.GetAttribute("Id"));
122        SPListItem item = null;
123        try
124        {
125            item = list.GetItemById(itemId);
126        }
127        catch (ArgumentException)
128        {
129            // no-op
130        }
131        if (item == null)
132        {
133            Log("Progress: Unable to locate item '{0}'.", EventLogEntryType.Warning, itemId.ToString());
134            continue;
135        }
136        SetObjectSecurity(web, item, "Item " + itemId, itemElement);
137    }
138}
139 
140/// <summary>
141/// Sets the object security.
142/// </summary>
143/// <param name="web">The web.</param>
144/// <param name="targetObject">The target object.</param>
145/// <param name="itemName">Name of the item.</param>
146/// <param name="sourceElement">The source element.</param>
147private static void SetObjectSecurity(SPWeb web, ISecurableObject targetObject, string itemName, XmlElement sourceElement)
148{
149 
150    bool hasUniqueRoleAssignments = bool.Parse(sourceElement.GetAttribute("HasUniqueRoleAssignments"));
151 
152    if (!hasUniqueRoleAssignments && targetObject.HasUniqueRoleAssignments)
153    {
154        Log("Progress: Setting target object to inherit permissions from parent for \"{0}\".", itemName);
155        targetObject.ResetRoleInheritance();
156        return;
157    }
158    else if (hasUniqueRoleAssignments && !targetObject.HasUniqueRoleAssignments)
159    {
160        Log("Progress: Breaking target object inheritance from parent for \"{0}\".", itemName);
161        targetObject.BreakRoleInheritance(false);
162    }
163    else if (!hasUniqueRoleAssignments && !targetObject.HasUniqueRoleAssignments)
164    {
165        Log("Progress: Ignoring \"{0}\".  Target object and source object both inherit from parent.", itemName);
166        return; // Both are inheriting so don't change.
167    }
168    if (hasUniqueRoleAssignments && targetObject.HasUniqueRoleAssignments)
169    {
170        while (targetObject.RoleAssignments.Count > 0)
171            targetObject.RoleAssignments.Remove(0); // Clear out any existing permissions
172    }
173 
174    foreach (XmlElement roleAssignmentElement in sourceElement.SelectNodes("RoleAssignments/RoleAssignment"))
175    {
176        string memberName = roleAssignmentElement.GetAttribute("Member");
177        SPRoleAssignment existingRoleAssignment = GetRoleAssignement(web, targetObject, memberName);
178 
179        if (existingRoleAssignment != null)
180        {
181            if (AddRoleDefinitions(web, existingRoleAssignment, roleAssignmentElement))
182            {
183                existingRoleAssignment.Update();
184 
185                Log("Progress: Updated \"{0}\" at target object \"{1}\".", memberName, itemName);
186            }
187        }
188        else
189        {
190            SPPrincipal principal = GetPrincipal(web, memberName);
191            if (principal == null)
192            {
193                Log("Progress: Unable to add Role Assignment for \"{0}\" - Member \"{1}\" not found.", EventLogEntryType.Warning, itemName, memberName);
194                continue;
195            }
196 
197            SPRoleAssignment newRA = new SPRoleAssignment(principal);
198            AddRoleDefinitions(web, newRA, roleAssignmentElement);
199 
200            if (newRA.RoleDefinitionBindings.Count == 0)
201            {
202                Log("Progress: Unable to add \"{0}\" to target object \"{1}\" (principals with only \"Limited Access\" cannot be added).", EventLogEntryType.Warning, memberName, itemName);
203                continue;
204            }
205 
206            Log("Progress: Adding new Role Assignment \"{0}\".", newRA.Member.Name);
207            
208            targetObject.RoleAssignments.Add(newRA);
209 
210            existingRoleAssignment = GetRoleAssignement(targetObject, principal);
211            if (existingRoleAssignment == null)
212            {
213                Log("Progress: Unable to add \"{0}\" to target object \"{1}\".", EventLogEntryType.Warning, memberName, itemName);
214            }
215            else
216            {
217                Log("Progress: Added \"{0}\" to target object \"{1}\".", memberName, itemName);
218            }
219        }
220    }
221}
222 
223/// <summary>
224/// Adds the role definitions.
225/// </summary>
226/// <param name="web">The web.</param>
227/// <param name="roleAssignment">The role assignment.</param>
228/// <param name="roleAssignmentElement">The role assignment element.</param>
229/// <returns></returns>
230private static bool AddRoleDefinitions(SPWeb web, SPRoleAssignment roleAssignment, XmlElement roleAssignmentElement)
231{
232    bool modified = false;
233    foreach (XmlElement roleDefinitionElement in roleAssignmentElement.SelectNodes("RoleDefinitionBindings/RoleDefinition"))
234    {
235        string name = roleDefinitionElement.GetAttribute("Name");
236        if (name == "Limited Access")
237            continue;
238 
239        SPRoleDefinition existingRoleDef = web.RoleDefinitions[name];
240        if (existingRoleDef == null)
241        {
242            Log("Progress: Adding new Role Definition \"{0}\".", name);
243 
244            SPBasePermissions perms = SPBasePermissions.EmptyMask;
245            foreach (string perm in roleDefinitionElement.GetAttribute("BasePermissions").Split(','))
246            {
247                perms = perms | (SPBasePermissions)Enum.Parse(typeof(SPBasePermissions), perm, true);
248            }
249            existingRoleDef = new SPRoleDefinition();
250            existingRoleDef.Name = name;
251            existingRoleDef.BasePermissions = perms;
252            existingRoleDef.Description = roleDefinitionElement.GetAttribute("Description");
253            existingRoleDef.Order = int.Parse(roleDefinitionElement.GetAttribute("Description"));
254            existingRoleDef.Update();
255 
256            SPWeb tempWeb = web;
257            while (!tempWeb.HasUniqueRoleDefinitions)
258                tempWeb = tempWeb.ParentWeb;
259 
260            tempWeb.RoleDefinitions.Add(existingRoleDef);
261        }
262        if (!roleAssignment.RoleDefinitionBindings.Contains(existingRoleDef))
263        {
264            roleAssignment.RoleDefinitionBindings.Add(existingRoleDef);
265            modified = true;
266        }
267    }
268    List<SPRoleDefinition> roleDefsToRemove = new List<SPRoleDefinition>();
269    foreach (SPRoleDefinition roleDef in roleAssignment.RoleDefinitionBindings)
270    {
271        if (roleDef.Name == "Limited Access")
272            continue;
273 
274        bool found = false;
275        foreach (XmlElement roleDefinitionElement in roleAssignmentElement.SelectNodes("RoleDefinitionBindings/RoleDefinition"))
276        {
277            if (roleDef.Name == roleDefinitionElement.GetAttribute("Name"))
278            {
279                found = true;
280                break;
281            }
282        }
283        if (!found)
284        {
285            roleDefsToRemove.Add(roleDef);
286            modified = true;
287        }
288    }
289    foreach (SPRoleDefinition roleDef in roleDefsToRemove)
290    {
291        Log("Progress: Removing '{0}' from '{1}'", roleDef.Name, roleAssignment.Member.Name);
292        roleAssignment.RoleDefinitionBindings.Remove(roleDef);
293    }
294    return modified;
295}
296 
297/// <summary>
298/// Gets the role assignement.
299/// </summary>
300/// <param name="web">The web.</param>
301/// <param name="securableObject">The securable object.</param>
302/// <param name="memberName">Name of the member.</param>
303/// <returns></returns>
304private static SPRoleAssignment GetRoleAssignement(SPWeb web, ISecurableObject securableObject, string memberName)
305{
306    SPPrincipal principal = GetPrincipal(web, memberName);
307    return GetRoleAssignement(securableObject, principal);
308}
309 
310/// <summary>
311/// Gets the role assignement.
312/// </summary>
313/// <param name="securableObject">The securable object.</param>
314/// <param name="principal">The principal.</param>
315/// <returns></returns>
316private static SPRoleAssignment GetRoleAssignement(ISecurableObject securableObject, SPPrincipal principal)
317{
318    SPRoleAssignment ra = null;
319    try
320    {
321        ra = securableObject.RoleAssignments.GetAssignmentByPrincipal(principal);
322    }
323    catch (ArgumentException)
324    { }
325    return ra;
326}
327 
328/// <summary>
329/// Gets the principal.
330/// </summary>
331/// <param name="web">The web.</param>
332/// <param name="memberName">Name of the member.</param>
333/// <returns></returns>
334private static SPPrincipal GetPrincipal(SPWeb web, string memberName)
335{
336    foreach (SPPrincipal p in web.SiteUsers)
337    {
338        if (p.Name == memberName)
339            return p;
340    }
341    foreach (SPPrincipal p in web.SiteGroups)
342    {
343        if (p.Name == memberName)
344            return p;
345    }
346    return null;
347}

This command simply takes in the output from the gl-exportlistsecurity command so naturally anything that command doesn’t support this one won’t either. The syntax can be seen below:

C:\>stsadm -help gl-importlistsecurity

stsadm -o gl-importlistsecurity


Imports security settings using data output from gl-exportlistsecurity.

Parameters:
        -url <url to import security to>
        -inputfile <file to import settings from>
        [-quiet]
        [-includeitemsecurity]

The following table summarizes the command and its various parameters:

Command NameAvailabilityBuild Date
gl-importlistsecurityWSS v3, MOSS 2007Released: 4/12/2008, Updated: 8/28/2008
Parameter NameShort FormRequiredDescriptionExample Usage
urlYesThe web URL to import the security settings to.-url http://portal
inputfilefileYesThe file with the security information to import.-inputfile "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 imported.-includeitemsecurity , -items

Here’s an example of how to import the security settings:

stsadm -o gl-importlistsecurity -url http://portal/documents/forms/allitems.aspx -inputfile c:\security.xml

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