Skip to content

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 users
  • C:\CesiviData\TutorialC\ - FileSystem data storage
  • C:\CesiviData\TutorialC\Logs\ - Server logs

Next Steps


See Also