Tutorial C: Cesivi + Local NTLM + FileSystem on Windows¶
Overview¶
This tutorial guides you through setting up Cesivi Server with: - Authentication: NTLM with configuration-based users (no AD required) - Storage: FileSystem (files and folders on disk) - Platform: Windows
Use Case: Local development simulating Windows/NTLM authentication without Active Directory. Perfect for testing SharePoint migrations, desktop applications, and legacy tools.
Time Required: ~10 minutes
Prerequisites¶
- Windows 10/11 or Windows Server 2019+
- .NET 10.0 SDK installed
- PowerShell 5.1 or later
Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ Windows Host │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Cesivi Server │ │
│ │ :5000 │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │ NTLM Auth │ │ FileSystem │ │ TfIdf │ │ │
│ │ │ (Config- │ │ Storage │ │ Search │ │ │
│ │ │ based) │ │ │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ C:\CesiviData │
│ ├── Sites\ │
│ ├── Webs\ │
│ ├── Lists\ │
│ └── Logs\ │
└─────────────────────────────────────────────────────────────┘
Step 1: Create Data Directory¶
# Create the data directory structure
$dataPath = "C:\CesiviData\TutorialC"
New-Item -ItemType Directory -Path $dataPath -Force
New-Item -ItemType Directory -Path "$dataPath\Logs" -Force
Write-Host "Created data directory: $dataPath"
Step 2: Create Configuration with NTLM Users¶
Navigate to the Cesivi.Server directory:
cd C:\Source\_AI\Cesivi2\Cesivi.Server
Create appsettings.TutorialC.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Cesivi": "Debug",
"Cesivi.Common.Identity": "Debug"
}
},
"Cesivi": {
"DataRootPath": "C:/CesiviData/TutorialC",
"LogPath": "C:/CesiviData/TutorialC/Logs",
"HostName": "localhost",
"UseHttps": false,
"HttpPort": 5000,
"StorageProvider": "FileSystem",
"SearchEngine": "TfIdf",
"Identity": {
"Providers": {
"NTLM": {
"Enabled": true,
"Priority": 100,
"Backend": "Configuration",
"Configuration": {
"Users": [
{
"Username": "administrator",
"Domain": "SHAREPOINT",
"Password": "Admin@123",
"DisplayName": "SharePoint Administrator",
"Email": "admin@sharepoint.local",
"Groups": ["Administrators", "Site Owners", "Full Control"]
},
{
"Username": "editor",
"Domain": "SHAREPOINT",
"Password": "Editor@123",
"DisplayName": "Content Editor",
"Email": "editor@sharepoint.local",
"Groups": ["Contributors", "Site Members"]
},
{
"Username": "reader",
"Domain": "SHAREPOINT",
"Password": "Reader@123",
"DisplayName": "Read Only User",
"Email": "reader@sharepoint.local",
"Groups": ["Visitors", "Site Visitors"]
},
{
"Username": "developer",
"Domain": "CONTOSO",
"Password": "Dev@123",
"DisplayName": "Developer Account",
"Email": "developer@contoso.com",
"Groups": ["Developers", "Site Members"]
}
]
}
},
"AcceptAll": {
"Enabled": false
}
}
},
"Authentication": {
"AcceptAllCredentials": false,
"AllowAnonymous": false,
"EnableNTLM": true,
"EnableJWT": false,
"EnableBasic": true
}
}
}
Step 3: Start Cesivi Server¶
# Set environment to use our custom config
$env:ASPNETCORE_ENVIRONMENT = "TutorialC"
# Start the server
dotnet run
You should see output like:
info: Cesivi[0]
Cesivi Server started
Listening on: http://localhost:5000
Storage Provider: FileSystem
Search Engine: TfIdf
Identity Providers: NTLM (100)
NTLM Backend: Configuration
Configured Users: 4
Step 4: Test NTLM Authentication with PowerShell¶
Open a new PowerShell terminal:
Test as Administrator¶
# Create credential object
$adminCred = New-Object System.Management.Automation.PSCredential(
"SHAREPOINT\administrator",
(ConvertTo-SecureString "Admin@123" -AsPlainText -Force)
)
# Test REST API
$web = Invoke-RestMethod -Uri "http://localhost:5000/_api/web" `
-Credential $adminCred `
-Headers @{ "Accept" = "application/json;odata=verbose" }
Write-Host "Web Title: $($web.d.Title)"
Write-Host "Web URL: $($web.d.Url)"
Test as Editor¶
$editorCred = New-Object System.Management.Automation.PSCredential(
"SHAREPOINT\editor",
(ConvertTo-SecureString "Editor@123" -AsPlainText -Force)
)
# Get current user info
$user = Invoke-RestMethod -Uri "http://localhost:5000/_api/web/currentuser" `
-Credential $editorCred `
-Headers @{ "Accept" = "application/json;odata=verbose" }
Write-Host "Current User: $($user.d.Title)"
Write-Host "User Email: $($user.d.Email)"
Test Cross-Domain User¶
$devCred = New-Object System.Management.Automation.PSCredential(
"CONTOSO\developer",
(ConvertTo-SecureString "Dev@123" -AsPlainText -Force)
)
$devUser = Invoke-RestMethod -Uri "http://localhost:5000/_api/web/currentuser" `
-Credential $devCred `
-Headers @{ "Accept" = "application/json;odata=verbose" }
Write-Host "Developer User: $($devUser.d.Title)"
Write-Host "Domain: CONTOSO"
Step 5: Test with CSOM (.NET)¶
Create a simple test application:
using Microsoft.SharePoint.Client;
using System.Net;
class Program
{
static void Main()
{
// Connect as administrator
var context = new ClientContext("http://localhost:5000");
context.Credentials = new NetworkCredential(
"administrator",
"Admin@123",
"SHAREPOINT"
);
// Load web
var web = context.Web;
context.Load(web, w => w.Title, w => w.Url, w => w.CurrentUser);
context.ExecuteQuery();
Console.WriteLine($"Web Title: {web.Title}");
Console.WriteLine($"Web URL: {web.Url}");
Console.WriteLine($"Current User: {web.CurrentUser.Title}");
// Create a list
var listInfo = new ListCreationInformation
{
Title = "NTLM Test List",
TemplateType = (int)ListTemplateType.GenericList
};
var list = web.Lists.Add(listInfo);
context.Load(list);
context.ExecuteQuery();
Console.WriteLine($"Created list: {list.Title}");
// Add an item
var itemInfo = new ListItemCreationInformation();
var item = list.AddItem(itemInfo);
item["Title"] = "Test Item from CSOM";
item.Update();
context.ExecuteQuery();
Console.WriteLine("Added item to list!");
}
}
Step 6: Verify FileSystem Storage¶
Check the data directory to see how data is stored:
# List the data directory structure
Get-ChildItem "C:\CesiviData\TutorialC" -Recurse | Format-Table FullName, Length
# You should see folders like:
# - Sites\
# - Webs\
# - Lists\
# - Items\
# - Files\
Examine Stored Data¶
# View a list configuration (JSON file)
Get-ChildItem "C:\CesiviData\TutorialC\Lists" -Recurse -Filter "*.json" |
Select-Object -First 1 |
ForEach-Object { Get-Content $_.FullName | ConvertFrom-Json | Format-List }
Step 7: Test Authentication Failure¶
Verify that invalid credentials are rejected:
# Wrong password
$badCred = New-Object System.Management.Automation.PSCredential(
"SHAREPOINT\administrator",
(ConvertTo-SecureString "WrongPassword" -AsPlainText -Force)
)
try {
Invoke-RestMethod -Uri "http://localhost:5000/_api/web" `
-Credential $badCred `
-Headers @{ "Accept" = "application/json;odata=verbose" }
} catch {
Write-Host "Authentication failed (expected): $($_.Exception.Message)" -ForegroundColor Green
}
# Unknown user
$unknownCred = New-Object System.Management.Automation.PSCredential(
"SHAREPOINT\unknownuser",
(ConvertTo-SecureString "SomePassword" -AsPlainText -Force)
)
try {
Invoke-RestMethod -Uri "http://localhost:5000/_api/web" `
-Credential $unknownCred `
-Headers @{ "Accept" = "application/json;odata=verbose" }
} catch {
Write-Host "Unknown user rejected (expected): $($_.Exception.Message)" -ForegroundColor Green
}
Step 8: Test with PnP PowerShell (Optional)¶
If you have PnP PowerShell installed:
# Connect with NTLM credentials
$cred = Get-Credential -UserName "SHAREPOINT\administrator" -Message "Enter password (Admin@123)"
Connect-PnPOnline -Url "http://localhost:5000" -Credentials $cred
# Get web info
Get-PnPWeb | Select-Object Title, Url
# Get lists
Get-PnPList | Select-Object Title, ItemCount
# Create a new list
New-PnPList -Title "PnP Created List" -Template GenericList
# Disconnect
Disconnect-PnPOnline
Step 9: Add More Users Dynamically¶
You can add users to the configuration file and restart:
# Read current config
$config = Get-Content "appsettings.TutorialC.json" | ConvertFrom-Json
# Add a new user
$newUser = @{
Username = "newuser"
Domain = "SHAREPOINT"
Password = "NewUser@123"
DisplayName = "New User"
Email = "newuser@sharepoint.local"
Groups = @("Contributors")
}
$config.Cesivi.Identity.Providers.NTLM.Configuration.Users += $newUser
# Save config
$config | ConvertTo-Json -Depth 10 | Set-Content "appsettings.TutorialC.json"
Write-Host "Restart the server to apply changes"
Troubleshooting¶
"401 Unauthorized" with correct credentials¶
Cause: Domain name case sensitivity or format
Solution:
- Use exact domain from config (e.g., SHAREPOINT not sharepoint)
- Format: DOMAIN\username (not username@domain.com)
- Check server logs for NTLM negotiation details
"User not found in configuration"¶
Cause: Username or domain doesn't match
Solution:
# Check configured users
$config = Get-Content "appsettings.TutorialC.json" | ConvertFrom-Json
$config.Cesivi.Identity.Providers.NTLM.Configuration.Users | Format-Table Username, Domain
"FileSystem access denied"¶
Cause: Permissions on data directory
Solution:
# Check current user has write access
$dataPath = "C:\CesiviData\TutorialC"
$acl = Get-Acl $dataPath
$acl.Access | Format-Table IdentityReference, FileSystemRights
"NTLM challenge not received"¶
Cause: Client not sending NTLM headers
Solution:
- Use -Credential parameter in Invoke-RestMethod
- For curl: use --ntlm flag
- Check HTTP headers in logs
Advanced: Using Windows Integrated Authentication¶
If running on a domain-joined machine, you can use integrated auth:
# Use current Windows identity
Invoke-RestMethod -Uri "http://localhost:5000/_api/web" `
-UseDefaultCredentials `
-Headers @{ "Accept" = "application/json;odata=verbose" }
Note: This requires the server to recognize the Windows user. Add your Windows account to the config:
{
"Username": "YourWindowsUsername",
"Domain": "YOURDOMAIN",
"Password": "",
"DisplayName": "Your Name",
"Email": "you@domain.com",
"Groups": ["Administrators"]
}
Clean Up¶
# Stop the server (Ctrl+C)
# Remove data directory
Remove-Item -Recurse -Force "C:\CesiviData\TutorialC"
# Remove config file
Remove-Item "appsettings.TutorialC.json"
Summary¶
You have successfully set up: - Local NTLM authentication with configuration-based users - FileSystem storage for persistent data - Multiple user accounts with different domains and groups
Configured Users¶
| Username | Domain | Password | Role |
|---|---|---|---|
| administrator | SHAREPOINT | Admin@123 | Full Control |
| editor | SHAREPOINT | Editor@123 | Contributor |
| reader | SHAREPOINT | Reader@123 | Visitor |
| developer | CONTOSO | Dev@123 | Developer |
Key Files¶
appsettings.TutorialC.json- Server configuration with usersC:\CesiviData\TutorialC\- FileSystem data storageC:\CesiviData\TutorialC\Logs\- Server logs
Next Steps¶
- Try Tutorial A for OAuth2/OIDC authentication
- Try Tutorial B for AD/LDAP integration
- Read NTLM Setup Guide for advanced NTLM options