Setting Custom Permission Levels in Sharepoint Programmatically


Before going into How part, lets first understand What is a Permission Level (known as Site Groups prior to WSS 3.0) in Sharepoint? Why do we need it?

What is a Permission Level?
It is a group or set of permissions\actions. By action, i mean that what a particular user is allowed to do in an application.
These permissions can then be assigned to a user or a group of users to allow\restrict certain actions based upon his role in the application.

Why?
Security. Making application secure, defining roles and responsibilities of it’s users.
Obviously, every application has users [otherwise why would it exist?]. Each user has it’s own set of roles and responsibilities. As per those responsibilities, he can perform tasks or take actions which are laid down for him.

Have you ever seen a Software Developer doing a job of CA, Finance Head? Surely, it is not meant for a poor developer.
This is where Permission Levels are needed. These permission levels segregate permissions, clearly demarcating or creating a boundary for users what they are supposed to do and what they are not.


Default Permission Levels
Sharepoint does provide a set of Permission levels when it is installed on a system. These default permission levels define what a user can do on assignment.

Default permission levels in Windows SharePoint Services 3.0 (Same description as in Microsoft site)

Permission Level Description
Full Control This permission level contains all permissions. Assigned to the Site name Owners SharePoint group, by default. This permission level cannot be customized or deleted.
Design Can create lists and document libraries, edit pages and apply themes, borders, and style sheets in the Web site. Not assigned to any SharePoint group, by default.
Contribute Can add, edit, and delete items in existing lists and document libraries. Assigned to the Site name Members SharePoint group, by default.
Read Read-only access to the Web site. Users and SharePoint groups with this permission level can view items and pages, open items, and documents. Assigned to the Site name Visitors SharePoint group, by default.
Limited Access

The Limited Access permission level is designed to be combined with fine-grained permissions to give users access to a specific list, document library, item, or document, without giving them access to the entire site. However, to access a list or library, for example, a user must have permission to open the parent Web site and read shared data such as the theme and navigation bars of the Web site. The Limited Access permission level cannot be customized or deleted.

Note You cannot assign this permission level to users or SharePoint groups. Instead, Windows SharePoint Services 3.0 automatically assigns this permission level to users and SharePoint groups when you grant them access to an object on your site that requires that they have access to a higher level object on which they do not have permissions. For example, if you grant users access to an item in a list and they do not have access to the list itself, Windows SharePoint Services 3.0 automatically grants them Limited Access on the list, and also the site, if needed.

There are scenarios where a requirement can’t be fulfilled by these default permission levels. Take, for e.g., a Contribute permission level. User who has contribute permissions can add, edit, open, view or delete an item besides other permissions. What if you want to permit a user to add, edit, view but not delete an item.

    For cases like this, Sharepoint allows to define custom Permission Levels in two ways:

  • Using Central Admin or Sharepoint Site itself
  • Programmatically


Manage Permissions using Central Admin or Sharepoint site
This is an easy way to configure permission levels.
You can refer to this link Manage Permissions @ Microsoft to know the steps in detail.

Programmatic way of Managing

    Yes. Sharepoint has exposed APIs for the same purpose. You can manage\add permissions at the following levels:

  • Web Application: SPWebApplication object has a property named PolicyRoles. Use an Add() to add a permission to the existing set.
    Note: I was able to achieve this and i was able to see the changes in Permission Policy of a web app in the Central Admin but Site Collections underneath my web app were not able to see the added permission levels.
    [Could not get time to resolve this issue. If you get, please add it here]
  • Site Collection:
  • Site\Web level:


Adding Permission level to a Site Collection\Site
SPWeb object has a named property RoleDefinitions which is actually a collection\set of Permission Levels available in the web.
We’ll be using an Add() to create a new permission level.


public void ProvisionPermissions(SPWeb newWeb)
{
SPRoleDefinition roleDefinition = null;
try
{
roleDefinition = this.PrepareSPRoleDefinition();

// Role inheritance should be broken on a we where permission levels need to be added
// Call BreakRoleInheritance() on a web to do so
if (newWeb.HasUniqueRoleDefinitions)
{
newWeb.RoleDefinitions.Add(roleDefinition);
newWeb.Update();
}
}
catch (SPException ex)
{
LogErrors(ex);
}
}


private SPRoleDefinition PrepareSPRoleDefinition(string name, string description, List permissions)

{
SPRoleDefinition spRoleDefinition = new SPRoleDefinition();

spRoleDefinition.Name = name;
spRoleDefinition.Description = description;

foreach (string permission in permissions)
{
SPBasePermissions permissionType = (SPBasePermissions)Enum.Parse(typeof(SPBasePermissions), permission, true);

// add this permission to the current permission set
spRoleDefinition.BasePermissions = spRoleDefinition.BasePermissions | permissionType;
}

return spRoleDefinition;
}

    The above code looks fine and may add a new role definition but this code is not perfect.
    It has many problems:

  • This code can’t be run multiple times. Reason being that after first time, a role definition with the same name will already exist and thus the call to SPWeb.RoleDefinitions.Add() will always result into an exception
  • This code will not add a role definition for the webs which inherit their permissions from the parent. So in an existing code, a call to SPWeb.BreakRoleInheritance() must be made before SPWeb.RoleDefinitions.Add()
  • This is not extensible. What if you want to supply an xml file containing desired Role definitions to be created\edited.
  • This code doesn’t permit the customization of an existing permission level: say Contribute or a permission level which was added earlier.
  • How about a case where you need all permissions of a Contribute except Delete?


All these issues can be solved by


SPWeb _newWeb;

/// <summary>
/// Loads the Site role definition configuration specified in the configuration file.
/// The Site role definitions for this parser is cleared and populated with role definitions from the configuration file.
/// </summary>
/// <param name="configFilePath">The path of the configuration file where the role definitions are stored.</param>
/// <returns>The list of site role definition.</returns>
public static List LoadConfig(string configFilePath)
{
List roles = new List();

if (!File.Exists(configFilePath))
{
return roles;
}

string content = File.ReadAllText(configFilePath);

roles.AddRange((List)new XmlSerializer(typeof(List)).Deserialize(content));

return roles;
}


/// <summary>
/// Provisions the custom role definitions
/// </summary>
/// <param name="siteType">Site type</param>
/// <param name="rootDirectory">Path to root directory</param>
public void ProvisionPermissions(string siteType, string filePath)
{
List roleDefinitions = LoadConfig(filePath);

SPRoleDefinition roleDefinition = null;
bool addNewRoleDefinition = false;

foreach (RoleDefinition definition in roleDefinitions)
{
if (ValidateRoleDefinition(definition))
{
bool createRoleDefinition = true;

try
{
roleDefinition = this.PrepareSPRoleDefinition(definition, out createRoleDefinition);

if (addNewRoleDefinition)
{
if (this._newWeb.HasUniqueRoleDefinitions || this._newWeb.IsRootWeb)
{
this._newWeb.RoleDefinitions.Add(roleDefinition);
this._newWeb.Update();
}
}
else
{
roleDefinition.Update();
}
}
catch (SPException ex)
{
this.LogErrors(ex);
}
}
}
}


/// <summary>
/// Validates the role definition read from config file
/// </summary>
/// <param name="roleDefinition">Role definition to be validated</param>
/// <returns>True if validated</returns>
private bool ValidateRoleDefinition(RoleDefinition roleDefinition)
{
bool isValid = false;

if (roleDefinition != null && !string.IsNullOrEmpty(roleDefinition.BaseRoleDefinition) && roleDefinition.Name.Trim() != string.Empty)
{
foreach (VoicePermission permission in roleDefinition.Permissions)
{
try
{
SPBasePermissions basePermission = (SPBasePermissions)Enum.Parse(typeof(SPBasePermissions), permission.Name, true);

if (!string.IsNullOrEmpty(roleDefinition.BaseRoleDefinition))
{
// verify if specified base role definition is valid n existing role definition
SPRoleDefinition spRoleDefinition = this._newWeb.RoleDefinitions[roleDefinition.BaseRoleDefinition];
}

isValid = true;
}
catch (SPException ex)
{
LogErrors(ex);

isValid = false;
break;
}
}
}

return isValid;
}


/// <summary>
/// Prepares a Role defnition to be added\updated
/// </summary>
/// <param name="roleDefinition">SDK Role definition object specifying metadata</param>
/// <param name="createRoleDefinition">True if new SPRoleCollection should be created</param>
/// <returns>Sharepoint Role Definition object</returns>
private SPRoleDefinition PrepareSPRoleDefinition(RoleDefinition roleDefinition, out bool createRoleDefinition)
{
SPRoleDefinition spRoleDefinition = null;

try
{
spRoleDefinition = this._newWeb.RoleDefinitions[roleDefinition.Name];
spRoleDefinition.BasePermissions = SPBasePermissions.EmptyMask;
createRoleDefinition = false;
}
catch (SPException)
{
// do nothing, new role definition has to be created
spRoleDefinition = new SPRoleDefinition();
createRoleDefinition = true;
}

spRoleDefinition.Name = roleDefinition.Name;
spRoleDefinition.Description = roleDefinition.Description;

if (!string.IsNullOrEmpty(roleDefinition.BaseRoleDefinition))
{
spRoleDefinition.BasePermissions = this._newWeb.RoleDefinitions[roleDefinition.BaseRoleDefinition].BasePermissions;
}

foreach (Permission permission in roleDefinition.Permissions)
{
SPBasePermissions permissionType = (SPBasePermissions)Enum.Parse(typeof(SPBasePermissions), permission.Name, true);

// remove only if permission is already in the set
if (permission.Exclude)
{
if ((spRoleDefinition.BasePermissions & permissionType) == permissionType)
{
// remove the permission from the set
spRoleDefinition.BasePermissions = spRoleDefinition.BasePermissions ^ permissionType;
}
}
else
{
// add this permission to the current permission set
spRoleDefinition.BasePermissions = spRoleDefinition.BasePermissions | permissionType;
}
}

return spRoleDefinition;
}


[Serializable]
public class RoleDefinition
{
#region Public Members

/// Gets or sets the Name of role definition.
[XmlAttribute("name")]
public string Name { get; set; }

/// Gets or sets the Description of role definition.
[XmlAttribute("description")]
public string Description { get; set; }

/// Gets or sets the base role definition.
[XmlAttribute("baseRoleDefinition")]
public string BaseRoleDefinition { get; set; }

/// Gets or sets the names of all permissions with in a role definition.
[XmlArray("Permissions")]
[XmlArrayItem("Permission")]
public List Permissions { get; set; }

#endregion
}

[XmlRoot("Permission")]
[Serializable]
public class Permission
{
#region Auto Properties

/// Gets or sets the Name of role definition.
[XmlAttribute("name")]
public string Name { get; set; }

/// Gets or sets a value indicating whether permission to be excluded or not
[XmlAttribute("exclude")]
public bool Exclude { get; set; }

#endregion
}

  • I made use of XmlSerializer to construct my info objects from a config file. My objects are of type “RoleDefinition” and “Permission”.

  • For each Role defintion, i’m checking if the role definition with the same already exists or not. If it exists, i get it’s reference or i create a new SPRoleDefinition object and messaging a caller that a new SPRoleDefinition has to be created.
    While defining a set of permissions, it is checked whether it has to be excluded from the set or should be added.
  • Code is understandable, i believe. šŸ™‚

    This code can be placed in Feature Activation event receiver code to create\edit permissions or wherever you want to.

Sample config file can look like this:

<?xml version="1.0" encoding="utf-8" ?>
<RoleDefinitions>
<RoleDefinition name="LimitedContribute" description="Contribute Permissions except Delete" baseRoleDefinition="Contribute">
<Permissions>
<Permission name="DeleteListItems" exclude="true" />
</Permissions>
</RoleDefinition>
<RoleDefinition name="Contribute" description="Contribute permissions for Vendors" baseRoleDefinition="Contribute">
<Permissions>
<Permission name="ViewFormPages" exclude="true" />
</Permissions>
</RoleDefinition>
</RoleDefinitions>

IMPORTANT: There is a dependency among the various Permissions. That means, if you select Permission 1, Permission 2 will be selected as well due to a dependency. This is visible if you add a Custom permission level through Sharepoint Central Admin or Site. But the same is not the case when you do it programmatically. So, it is very important that you select dependencies as well.
Knowing this, i would suggest that you select a base permission and the remove the permissions which you don’t want.

Though you can extend this to Delete. I hardly think that this may be required but you can achieve this by calling SPRoleDefinition.Delete().

One thought on “Setting Custom Permission Levels in Sharepoint Programmatically”

Leave a comment