Skip to content

Permission System

HomeDocumentationFeatures → Permissions


Overview

Cesivi Server implements a comprehensive permission system that mirrors real SharePoint behavior, including role-based access control (RBAC), Active Directory integration, and permission inheritance.

Features: - ✅ Role Definitions - Full Control, Contribute, Read, etc. - ✅ Role Assignments - Assign roles to users and groups - ✅ Permission Inheritance - Web → List → Item hierarchy - ✅ Active Directory Integration - Users, groups, nested membership - ✅ SharePoint Groups - Native group support - ✅ Effective Permissions - Calculate combined permissions from all roles


SharePoint Permission Model

Permission Hierarchy

Web Application (root)
  └─ Site Collection
      └─ Web (Site)
          └─ List/Library
              └─ List Item/File

Inheritance Flow: - By default, objects inherit permissions from their parent - Inheritance can be broken at any level - Objects with unique permissions maintain their own role assignments

Role Definitions (Permission Levels)

SharePoint uses predefined role definitions (permission levels):

Role Definition Permission Mask Description
Full Control 0xFFFFFFFFFFFFFFFF All permissions
Design 0x8F3FFF3F Create lists/libraries, edit pages
Contribute 0x10000BF Add, edit, delete items
Read 0x1030021 View items and pages
Limited Access 0x20000 Access specific resources
View Only 0x1030020 View items (no download)

Base Permissions (Individual Rights)

Common SPBasePermissions flags:

Permission Value Description
ViewListItems 0x01 View items in lists, documents in libraries
AddListItems 0x02 Add items to lists, documents to libraries
EditListItems 0x04 Edit items in lists, documents in libraries
DeleteListItems 0x08 Delete items from lists, documents from libraries
OpenItems 0x10 View the source of items with file handlers
ViewPages 0x20000 View pages in a site
Open 0x10000 Allow users to open a site
ManageLists 0x800 Create and delete lists, add or remove columns
FullMask 0xFFFFFFFFFFFFFFFF Full control

Active Directory Integration

AD Users and Groups

The server can load AD identities from a JSON file to support realistic permission scenarios.

File Location: MockData/ActiveDirectory/identities.json

Example Structure:

{
  "Users": [
    {
      "DistinguishedName": "CN=John Doe,OU=Users,DC=contoso,DC=com",
      "SamAccountName": "jdoe",
      "UserPrincipalName": "jdoe@contoso.com",
      "DisplayName": "John Doe",
      "GivenName": "John",
      "Surname": "Doe",
      "Email": "jdoe@contoso.com",
      "Department": "IT",
      "Title": "System Administrator",
      "MemberOf": [
        "CN=IT Admins,OU=Groups,DC=contoso,DC=com",
        "CN=Domain Users,OU=Groups,DC=contoso,DC=com"
      ]
    }
  ],
  "Groups": [
    {
      "DistinguishedName": "CN=IT Admins,OU=Groups,DC=contoso,DC=com",
      "SamAccountName": "IT Admins",
      "DisplayName": "IT Administrators",
      "Description": "IT department administrators",
      "GroupType": 0,
      "Members": [
        "CN=John Doe,OU=Users,DC=contoso,DC=com"
      ],
      "MemberOf": [
        "CN=All Admins,OU=Groups,DC=contoso,DC=com"
      ]
    }
  ]
}

Nested Group Membership

The server supports nested groups (groups containing other groups):

All Admins (has Full Control permission)
  └─ IT Admins (nested group)
      └─ John Doe (user)

When calculating permissions, the server resolves all transitive group memberships using breadth-first search (BFS).


Permission Operations

Breaking Permission Inheritance

REST API:

// Break inheritance on a list
fetch('http://localhost:5000/_api/web/lists/getbytitle(\'Documents\')/breakroleinheritance(copyRoleAssignments=true,clearSubscopes=true)', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }
})
.then(r => r.json())
.then(data => console.log('Inheritance broken'));

C# CSOM:

using Microsoft.SharePoint.Client;

var ctx = new ClientContext("http://localhost:5000");
ctx.Credentials = new System.Net.NetworkCredential("user", "password");

var list = ctx.Web.Lists.GetByTitle("Documents");
list.BreakRoleInheritance(copyRoleAssignments: true, clearSubscopes: true);
ctx.ExecuteQuery();

Console.WriteLine("Inheritance broken on list");

PowerShell PnP:

Connect-PnPOnline -Url "http://localhost:5000" -Credentials (Get-Credential)

# Break inheritance on a list
Set-PnPList -Identity "Documents" -BreakRoleInheritance -CopyRoleAssignments

# OR use REST API for better reliability
Invoke-PnPSPRestMethod -Method POST `
  -Url "/_api/web/lists/getbytitle('Documents')/breakroleinheritance(copyRoleAssignments=true,clearSubscopes=true)"

Resetting Permission Inheritance

REST API:

// Reset inheritance on a list
fetch('http://localhost:5000/_api/web/lists/getbytitle(\'Documents\')/resetroleinheritance()', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json'
  }
})
.then(r => r.json())
.then(data => console.log('Inheritance reset'));

C# CSOM:

var list = ctx.Web.Lists.GetByTitle("Documents");
list.ResetRoleInheritance();
ctx.ExecuteQuery();

Console.WriteLine("Inheritance reset on list");

Granting Permissions

REST API:

// Grant Read permission to a user on a list
const data = {
  __metadata: { type: 'SP.RoleAssignment' },
  PrincipalId: 5,  // User ID
  RoleDefId: 1073741826  // Read role definition ID
};

fetch('http://localhost:5000/_api/web/lists/getbytitle(\'Documents\')/roleassignments/addroleassignment(principalid=5,roledefid=1073741826)', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
})
.then(r => r.json())
.then(data => console.log('Permission granted'));

C# CSOM:

var list = ctx.Web.Lists.GetByTitle("Documents");
var user = ctx.Web.EnsureUser("jdoe@contoso.com");
var roleDefinition = ctx.Web.RoleDefinitions.GetByName("Read");

ctx.Load(user);
ctx.Load(roleDefinition);
ctx.ExecuteQuery();

var roleAssignment = new RoleDefinitionBindingCollection(ctx);
roleAssignment.Add(roleDefinition);
list.RoleAssignments.Add(user, roleAssignment);
ctx.ExecuteQuery();

Console.WriteLine($"Granted Read permission to {user.Title}");

PowerShell PnP:

# Grant Read permission to a user on a list
Set-PnPListPermission -Identity "Documents" -User "jdoe@contoso.com" -AddRole "Read"

# Grant Contribute permission to a group
Set-PnPListPermission -Identity "Documents" -Group "IT Admins" -AddRole "Contribute"

Checking Permissions

C# CSOM:

// Check if user has specific permission
var list = ctx.Web.Lists.GetByTitle("Documents");
var user = ctx.Web.CurrentUser;

ctx.Load(list, l => l.EffectiveBasePermissions);
ctx.Load(user);
ctx.ExecuteQuery();

bool canEdit = list.EffectiveBasePermissions.Has(PermissionKind.EditListItems);
bool canDelete = list.EffectiveBasePermissions.Has(PermissionKind.DeleteListItems);

Console.WriteLine($"Can Edit: {canEdit}");
Console.WriteLine($"Can Delete: {canDelete}");

PowerShell PnP:

# Get user's effective permissions on a list
$ctx = Get-PnPContext
$list = Get-PnPList "Documents"
$user = $ctx.Web.CurrentUser

$ctx.Load($list, "EffectiveBasePermissions")
$ctx.Load($user)
$ctx.ExecuteQuery()

$canEdit = $list.EffectiveBasePermissions.Has([Microsoft.SharePoint.Client.PermissionKind]::EditListItems)
Write-Host "Can Edit: $canEdit"

REST API:

// Get user's effective permissions
fetch('http://localhost:5000/_api/web/lists/getbytitle(\'Documents\')/getusereffectivepermissions(@user)?@user=\'jdoe@contoso.com\'', {
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json'
  }
})
.then(r => r.json())
.then(data => {
  const permissions = parseInt(data.d.GetUserEffectivePermissions);
  const canEdit = (permissions & 0x04) === 0x04;  // EditListItems
  console.log('Can Edit:', canEdit);
});

User and Group Management

Creating SharePoint Users

REST API:

// Add a user to the site
const userData = {
  __metadata: { type: 'SP.User' },
  LoginName: 'jdoe@contoso.com'
};

fetch('http://localhost:5000/_api/web/siteusers', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(userData)
})
.then(r => r.json())
.then(data => console.log('User added:', data.d.Title));

C# CSOM:

var user = ctx.Web.EnsureUser("jdoe@contoso.com");
ctx.Load(user);
ctx.ExecuteQuery();

Console.WriteLine($"User added: {user.Title} (ID: {user.Id})");

PowerShell PnP:

# Add a user to the site
New-PnPUser -LoginName "jdoe@contoso.com"

Creating SharePoint Groups

REST API:

// Create a SharePoint group
const groupData = {
  __metadata: { type: 'SP.Group' },
  Title: 'Project Team',
  Description: 'Project team members'
};

fetch('http://localhost:5000/_api/web/sitegroups', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(groupData)
})
.then(r => r.json())
.then(data => console.log('Group created:', data.d.Title));

C# CSOM:

var groupInfo = new GroupCreationInformation
{
    Title = "Project Team",
    Description = "Project team members"
};

var group = ctx.Web.SiteGroups.Add(groupInfo);
ctx.Load(group);
ctx.ExecuteQuery();

Console.WriteLine($"Group created: {group.Title} (ID: {group.Id})");

PowerShell PnP:

# Create a SharePoint group
New-PnPGroup -Title "Project Team" -Description "Project team members"

Adding Users to Groups

REST API:

// Add user to group
fetch('http://localhost:5000/_api/web/sitegroups(7)/users', {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + btoa('user:password'),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    __metadata: { type: 'SP.User' },
    LoginName: 'jdoe@contoso.com'
  })
})
.then(r => r.json())
.then(data => console.log('User added to group'));

C# CSOM:

var group = ctx.Web.SiteGroups.GetByName("Project Team");
var user = ctx.Web.EnsureUser("jdoe@contoso.com");

group.Users.AddUser(user);
ctx.ExecuteQuery();

Console.WriteLine($"Added {user.Title} to {group.Title}");

PowerShell PnP:

# Add user to group
Add-PnPGroupMember -Group "Project Team" -LoginName "jdoe@contoso.com"

Permission Scenarios

Scenario 1: Secure Document Library

Requirement: Create a document library with restricted access for specific team.

using Microsoft.SharePoint.Client;

var ctx = new ClientContext("http://localhost:5000");
ctx.Credentials = new System.Net.NetworkCredential("admin", "password");

// 1. Create document library
var listInfo = new ListCreationInformation
{
    Title = "Confidential Documents",
    TemplateType = (int)ListTemplateType.DocumentLibrary
};
var list = ctx.Web.Lists.Add(listInfo);
ctx.Load(list);
ctx.ExecuteQuery();

// 2. Break inheritance (don't copy role assignments)
list.BreakRoleInheritance(copyRoleAssignments: false, clearSubscopes: true);
ctx.ExecuteQuery();

// 3. Grant Full Control to administrators
var adminGroup = ctx.Web.SiteGroups.GetByName("Site Owners");
var fullControlRole = ctx.Web.RoleDefinitions.GetByName("Full Control");
ctx.Load(adminGroup);
ctx.Load(fullControlRole);
ctx.ExecuteQuery();

var adminRoleAssignment = new RoleDefinitionBindingCollection(ctx);
adminRoleAssignment.Add(fullControlRole);
list.RoleAssignments.Add(adminGroup, adminRoleAssignment);
ctx.ExecuteQuery();

// 4. Grant Contribute to project team
var teamGroup = ctx.Web.SiteGroups.GetByName("Project Team");
var contributeRole = ctx.Web.RoleDefinitions.GetByName("Contribute");
ctx.Load(teamGroup);
ctx.Load(contributeRole);
ctx.ExecuteQuery();

var teamRoleAssignment = new RoleDefinitionBindingCollection(ctx);
teamRoleAssignment.Add(contributeRole);
list.RoleAssignments.Add(teamGroup, teamRoleAssignment);
ctx.ExecuteQuery();

Console.WriteLine("Secure document library created");

Scenario 2: Item-Level Permissions

Requirement: Restrict specific document to selected users only.

// Get the document
var list = ctx.Web.Lists.GetByTitle("Confidential Documents");
var item = list.GetItemById(1);
ctx.Load(item);
ctx.ExecuteQuery();

// Break inheritance on item
item.BreakRoleInheritance(copyRoleAssignments: false, clearSubscopes: true);
ctx.ExecuteQuery();

// Grant Read to specific user
var user = ctx.Web.EnsureUser("manager@contoso.com");
var readRole = ctx.Web.RoleDefinitions.GetByName("Read");
ctx.Load(user);
ctx.Load(readRole);
ctx.ExecuteQuery();

var userRoleAssignment = new RoleDefinitionBindingCollection(ctx);
userRoleAssignment.Add(readRole);
item.RoleAssignments.Add(user, userRoleAssignment);
ctx.ExecuteQuery();

Console.WriteLine("Item-level permissions set");

Scenario 3: Nested AD Groups with Permissions

Requirement: Use AD group hierarchy for permission management.

Setup (identities.json):

{
  "Groups": [
    {
      "DistinguishedName": "CN=All Employees,OU=Groups,DC=contoso,DC=com",
      "SamAccountName": "All Employees",
      "Members": [
        "CN=IT Department,OU=Groups,DC=contoso,DC=com",
        "CN=HR Department,OU=Groups,DC=contoso,DC=com"
      ]
    },
    {
      "DistinguishedName": "CN=IT Department,OU=Groups,DC=contoso,DC=com",
      "SamAccountName": "IT Department",
      "Members": [
        "CN=John Doe,OU=Users,DC=contoso,DC=com"
      ],
      "MemberOf": [
        "CN=All Employees,OU=Groups,DC=contoso,DC=com"
      ]
    }
  ],
  "Users": [
    {
      "DistinguishedName": "CN=John Doe,OU=Users,DC=contoso,DC=com",
      "SamAccountName": "jdoe",
      "MemberOf": [
        "CN=IT Department,OU=Groups,DC=contoso,DC=com"
      ]
    }
  ]
}

Grant Permission to Top-Level Group:

// Grant Read to "All Employees" AD group
var list = ctx.Web.Lists.GetByTitle("Company News");
var adGroup = ctx.Web.EnsureUser("All Employees");  // AD group
var readRole = ctx.Web.RoleDefinitions.GetByName("Read");

ctx.Load(adGroup);
ctx.Load(readRole);
ctx.ExecuteQuery();

var roleAssignment = new RoleDefinitionBindingCollection(ctx);
roleAssignment.Add(readRole);
list.RoleAssignments.Add(adGroup, roleAssignment);
ctx.ExecuteQuery();

// Now "jdoe" has Read permission (nested membership resolved):
// jdoe → IT Department → All Employees → Read permission

Effective Permissions Calculation

The server calculates effective permissions by:

  1. Resolving User Identity - Check AD storage, then SharePoint users
  2. Getting All Groups - Direct memberships + nested AD groups + SharePoint groups
  3. Collecting Role Assignments - Check all applicable role assignments for user and groups
  4. Combining Permissions - Union (OR) all permission masks from all roles
  5. Checking Inheritance - If object inherits, check parent permissions too

Example Calculation:

User: John Doe (jdoe)
├─ Direct Role: None
├─ SharePoint Group: "Site Members" → Contribute (0x10000BF)
├─ AD Group: "IT Department" → Design (0x8F3FFF3F)
└─ Nested AD Group: "All Employees" → Read (0x1030021)

Effective Permissions = 0x10000BF | 0x8F3FFF3F | 0x1030021
                      = 0x8F3FFFF (union of all permissions)

Best Practices

1. Use Groups, Not Individual Users

Good Practice:

// Grant permission to group
var group = ctx.Web.SiteGroups.GetByName("Project Team");
list.RoleAssignments.Add(group, roleAssignment);

Bad Practice:

// Grant permission to 50 individual users
foreach (var userEmail in emails)
{
    var user = ctx.Web.EnsureUser(userEmail);
    list.RoleAssignments.Add(user, roleAssignment);  // Harder to manage
}

2. Minimize Broken Inheritance

Good Practice: - Use inheritance where possible - Only break when absolutely necessary - Document why inheritance was broken

Bad Practice: - Breaking inheritance on every item - Creates management nightmare - Performance issues with many unique permissions

3. Use Nested AD Groups

Good Practice:

All Employees (has Read permission)
  ├─ IT Department
  │   └─ IT Admins (has Full Control via separate assignment)
  └─ HR Department

Benefits: - Single permission assignment covers all employees - Easy to add/remove users via AD group membership - Reflects organizational structure

4. Test Permissions Before Deployment

// Verify user has expected permissions
var list = ctx.Web.Lists.GetByTitle("Documents");
var user = ctx.Web.CurrentUser;

ctx.Load(list, l => l.EffectiveBasePermissions);
ctx.Load(user);
ctx.ExecuteQuery();

bool hasExpectedPermission = list.EffectiveBasePermissions.Has(PermissionKind.AddListItems);
if (!hasExpectedPermission)
{
    throw new Exception($"User {user.Title} missing expected permission");
}

Troubleshooting

User Cannot Access Content

Diagnosis Steps:

// 1. Verify user can be resolved
var user = ctx.Web.EnsureUser("jdoe@contoso.com");
ctx.Load(user);
ctx.ExecuteQuery();
Console.WriteLine($"User: {user.Title} (ID: {user.Id})");

// 2. Check group memberships
var groups = user.Groups;
ctx.Load(groups);
ctx.ExecuteQuery();
Console.WriteLine($"Member of {groups.Count} groups:");
foreach (var group in groups)
{
    Console.WriteLine($"  - {group.Title}");
}

// 3. Check effective permissions
var list = ctx.Web.Lists.GetByTitle("Documents");
ctx.Load(list, l => l.EffectiveBasePermissions);
ctx.ExecuteQuery();

bool canView = list.EffectiveBasePermissions.Has(PermissionKind.ViewListItems);
bool canEdit = list.EffectiveBasePermissions.Has(PermissionKind.EditListItems);
Console.WriteLine($"Can View: {canView}, Can Edit: {canEdit}");

// 4. Check role assignments on list
var roleAssignments = list.RoleAssignments;
ctx.Load(roleAssignments, ras => ras.Include(ra => ra.Member, ra => ra.RoleDefinitionBindings));
ctx.ExecuteQuery();
Console.WriteLine($"Role Assignments: {roleAssignments.Count}");
foreach (var ra in roleAssignments)
{
    Console.WriteLine($"  Principal: {ra.Member.Title}");
    foreach (var roleDef in ra.RoleDefinitionBindings)
    {
        Console.WriteLine($"    Role: {roleDef.Name}");
    }
}

Permission Changes Not Taking Effect

Common Issues:

  1. Inheritance Not Broken:

    var list = ctx.Web.Lists.GetByTitle("Documents");
    ctx.Load(list, l => l.HasUniqueRoleAssignments);
    ctx.ExecuteQuery();
    
    if (!list.HasUniqueRoleAssignments)
    {
        Console.WriteLine("List inherits permissions from parent");
        list.BreakRoleInheritance(copyRoleAssignments: false, clearSubscopes: true);
        ctx.ExecuteQuery();
    }
    

  2. Caching Issues:

  3. Restart server to reload MockData
  4. Clear browser cache if using web UI

  5. AD Group Membership Not Updated:

  6. Verify MemberOf arrays in identities.json
  7. Restart server to reload AD data

API Reference Summary

ACL Service (Server-Side)

public interface IACLService
{
    // Check specific permission
    Task<bool> CheckPermissionAsync(string userLogin, string objectPath, ulong permission);

    // Get combined permission mask
    Task<ulong> GetEffectivePermissionsAsync(string userLogin, string objectPath);

    // Get all groups (AD + SharePoint)
    Task<List<string>> GetUserAllGroupsAsync(string userLogin);

    // Resolve identity
    Task<ResolvedIdentity?> ResolveIdentityAsync(string login);

    // Get role assignments
    Task<List<SPRoleAssignment>> GetRoleAssignmentsAsync(string objectPath);
}

Object Path Formats: - Web: web:/sites/site - List: list:/sites/site/Lists/ListName - Item: item:/sites/site/Lists/ListName/ItemID - File: file:/sites/site/Shared Documents/file.docx


Features

Reference

Setup


Last Updated: November 15, 2025 Version: 1.0.0

Navigation: Home | Documentation | Features | Permissions