User Management Guide¶
Complete guide to managing users, permissions, and scopes in Slidefactory.
Table of Contents¶
- Overview
- Authentication Providers
- Scope-Based Authorization
- CLI User Management
- Docker-Specific Usage
- Entra (Azure AD) Integration
- Common Workflows
- Troubleshooting
Overview¶
Slidefactory uses a scope-based authorization system that provides fine-grained access control to workflows, templates, presentations, and other resources.
Key Concepts¶
Authentication (Who are you?) - Local users: Username/password stored in database - Entra (Azure AD): Microsoft authentication with SSO
Authorization (What can you do?) - Scopes: Permissions like workflows:read, templates:write, presentations:generate - Wildcard: * grants full admin access - Hierarchical: Scopes can be global or resource-specific
User Database - All users stored in app_users table - Scopes stored as JSONB array - Supports both local and Entra users
Authentication Providers¶
Local Authentication¶
Users with username/password stored in Slidefactory database.
Characteristics: - Password hashed with bcrypt - Managed via CLI - No external dependencies - Good for: Development, service accounts, emergency access
Usage:
# Create local user
docker-compose exec web slidefactory user create-local admin@example.com \
--name "Admin User" \
--preset admin
# Change password
docker-compose exec web slidefactory user change-password admin@example.com
Azure AD (Entra) Authentication¶
Users authenticate via Microsoft Azure AD (Entra).
Characteristics: - SSO (Single Sign-On) support - Group-based scopes (JIT provisioning) - Auto-creates users on first login - Scopes loaded from Entra groups - Good for: Enterprise deployments, team access
Configuration:
# Environment variables required
AZURE_TENANT_ID=your-tenant-id
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret
See Entra Integration for details.
Scope-Based Authorization¶
Scope Format¶
Scopes follow a hierarchical pattern:
Examples:
* # Full access (superadmin)
workflows:read # Read all workflows
workflows:esg2:read # Read specific workflow
workflows:esg2:execute # Execute specific workflow
templates:esg2:write # Write templates for workflow
presentations:generate # Generate presentations
results:read # Read all results
contexts:write # Write context/documents
users:write # Manage users (admin only)
Scope Categories¶
Global Scopes¶
| Scope | Description |
|---|---|
* | Full access to everything (superadmin) |
Workflow Scopes¶
| Scope | Description |
|---|---|
workflows:read | View all workflows |
workflows:{workflow_id}:read | View specific workflow |
workflows:{workflow_id}:execute | Execute specific workflow |
Template Scopes¶
| Scope | Description |
|---|---|
templates:read | Read all templates |
templates:write | Create/update all templates |
templates:delete | Delete any template |
templates:{workflow_id}:read | Read templates for specific workflow |
templates:{workflow_id}:write | Manage templates for specific workflow |
templates:{workflow_id}:delete | Delete templates for specific workflow |
Presentation Scopes¶
| Scope | Description |
|---|---|
presentations:read | View all presentations |
presentations:generate | Generate presentations |
Result Scopes¶
| Scope | Description |
|---|---|
results:read | View all results |
results:write | Create/update results |
results:delete | Delete results |
Context/Document Scopes¶
| Scope | Description |
|---|---|
contexts:read | View all contexts/documents |
contexts:write | Manage contexts/documents |
User Management Scopes¶
| Scope | Description |
|---|---|
users:read | View users |
users:write | Manage users (admin function) |
Scope Presets¶
CLI provides convenient presets for common user types:
admin¶
Full access to everything.
workflow-admin¶
Full control over a specific workflow.
# Usage: --preset workflow-admin --workflow esg2
Scopes:
- workflows:esg2:read
- workflows:esg2:execute
- templates:esg2:write
- presentations:generate
- results:write
- contexts:write
workflow-user¶
Regular user access to a workflow.
# Usage: --preset workflow-user --workflow esg2
Scopes:
- workflows:esg2:read
- workflows:esg2:execute
- templates:esg2:read
- presentations:generate
- presentations:read
- results:read
viewer¶
Read-only access to presentations.
api-user¶
For service accounts/automation.
CLI User Management¶
Prerequisites¶
# Ensure Docker services are running
docker-compose up -d
# Check CLI is available
docker-compose exec web slidefactory user --help
Commands Reference¶
All commands shown below should be prefixed with docker-compose exec web when running in Docker:
List Users¶
View all users in the system.
# Basic list
docker-compose exec web slidefactory user list
# Show Entra groups
docker-compose exec web slidefactory user list --show-groups
# Show all scopes
docker-compose exec web slidefactory user list --show-scopes
# Filter by provider
docker-compose exec web slidefactory user list --provider local
docker-compose exec web slidefactory user list --provider azure_ad
Output Example:
Email Name Provider Active Overridden Scopes
------------------------------------------------------------------------------------------------
admin@example.com Admin User local Yes No 1
user@company.com John Doe azure_ad Yes No 8
viewer@company.com Jane Smith azure_ad Yes Yes 2
Total: 3 users
Show User Details¶
View detailed information for a specific user.
Output Example:
============================================================
User: user@example.com
============================================================
Name: John Doe
Provider: azure_ad
Active: True
Scopes Overridden: False
Created: 2025-10-15 14:23:45
Last Login: 2025-11-04 09:15:32
Scopes (8):
- workflows:esg2:read
- workflows:esg2:execute
- templates:esg2:read
- presentations:generate
- presentations:read
- results:read
- contexts:read
- workflows:read
Entra Groups (2):
- slidefactory-esg-team
- slidefactory-users
Create Local User¶
Create a new user with password authentication.
# With preset
docker-compose exec web slidefactory user create-local admin@example.com \
--name "Admin User" \
--preset admin
# With specific workflow
docker-compose exec web slidefactory user create-local user@example.com \
--name "ESG Team Member" \
--preset workflow-user \
--workflow esg2
# With custom scopes
docker-compose exec web slidefactory user create-local api@example.com \
--name "API Service Account" \
--scopes "presentations:generate,presentations:read,results:read"
# With password on command line (not recommended for production)
docker-compose exec web slidefactory user create-local test@example.com \
--name "Test User" \
--password "test123" \
--preset viewer
Interactive Password Prompt: If --password is omitted, you'll be prompted securely:
Password:
Confirm password:
✓ Local user admin@example.com created successfully!
Name: Admin User
Provider: local
Scopes (1):
- *
Add Scope¶
Add a single scope to an existing user.
Output:
Note: Sets scopes_overridden=True for Entra users.
Remove Scope¶
Remove a scope from a user.
Output:
Note: Sets scopes_overridden=True for Entra users.
Set Scopes¶
Replace all scopes for a user (comma-separated).
docker-compose exec web slidefactory user set-scopes user@example.com \
"workflows:read,presentations:read,presentations:generate"
Output:
✓ Set 3 scopes for user user@example.com
- presentations:generate
- presentations:read
- workflows:read
Warning: This replaces ALL existing scopes. Use with caution.
Reset to Groups (Entra Only)¶
Reset Entra user's scopes to match their current group memberships.
# With confirmation prompt
docker-compose exec web slidefactory user reset-to-groups user@example.com
# Skip confirmation
docker-compose exec web slidefactory user reset-to-groups user@example.com --yes
Output:
Resetting scopes based on groups: slidefactory-esg-team, slidefactory-users
New scopes (14):
- contexts:read
- presentations:generate
- presentations:read
- results:read
- templates:esg2:read
- workflows:esg2:execute
- workflows:esg2:read
- workflows:read
... (more scopes)
Proceed with reset? (yes/no): yes
✓ Reset scopes for user user@example.com
Use Case: When Entra group memberships change and you want to reapply group-based scopes.
Activate User¶
Enable a deactivated user account.
Output:
Deactivate User¶
Disable a user account (prevents login).
Output:
Note: Deactivated users cannot log in but remain in the database.
Change Password (Local Only)¶
Change password for a local user.
# Interactive prompt
docker-compose exec web slidefactory user change-password user@example.com
# With password on command line (not recommended)
docker-compose exec web slidefactory user change-password user@example.com --password "newpass123"
Output:
Error if Entra user:
List Entra Groups¶
View configured Entra group to scope mappings.
Output:
Configured Entra Groups (8):
============================================================
slidefactory-admins
Scopes: 1
- *
slidefactory-esg-team
Scopes: 8
- workflows:esg2:read
- workflows:esg2:execute
- templates:esg2:read
- presentations:generate
- presentations:read
... and 3 more
slidefactory-esg-admins
Scopes: 14
- workflows:esg2:read
- workflows:esg2:execute
- templates:esg2:read
- templates:esg2:write
- templates:esg2:delete
... and 9 more
Docker-Specific Usage¶
Running CLI Commands in Docker¶
The CLI is included in the Docker image and can be run inside the web container using docker-compose exec.
Prerequisites¶
- Docker containers must be running:
docker-compose up -d
Basic Usage¶
# Basic syntax
docker-compose exec web slidefactory user <command>
# Examples
docker-compose exec web slidefactory user list
docker-compose exec web slidefactory user show admin@example.com
docker-compose exec web slidefactory user create-local \
newuser@example.com \
--name "New User" \
--preset viewer
Method 1: docker-compose exec (Recommended)¶
When to use: Containers are already running
# List users
docker-compose exec web slidefactory user list
# Create admin
docker-compose exec web slidefactory user create-local \
admin@company.com \
--name "Administrator" \
--preset admin
# Show user details
docker-compose exec web slidefactory user show admin@company.com
Advantages: - Fast (uses running container) - Direct database access (no network configuration needed) - No local Python setup required
Method 2: docker-compose run (One-Off Commands)¶
When to use: Containers are not running, or you want isolated execution
# Start just for this command, then stop
docker-compose run --rm web slidefactory user list
# Create user without starting all services
docker-compose run --rm web slidefactory user create-local \
admin@example.com \
--name "Admin" \
--preset admin
Advantages: - Works even if services are stopped - Clean environment - --rm automatically removes container after command
Interactive Password Entry¶
For commands requiring password input (create-local, change-password):
# Interactive prompt (recommended)
docker-compose exec web slidefactory user create-local \
user@example.com \
--name "Interactive User" \
--preset workflow-user \
--workflow esg2
# Will prompt:
# Password:
# Confirm password:
Alternative (non-interactive):
# Pass password on command line (less secure, visible in logs)
docker-compose exec web slidefactory user create-local \
user@example.com \
--name "Scripted User" \
--password "temppass123" \
--preset viewer
Verifying Database Connection¶
The CLI inside Docker automatically connects to the database via the internal Docker network.
Test database connection:
# Check database is ready
docker-compose exec postgres pg_isready
# Test CLI database access
docker-compose exec web slidefactory user list
Common Docker Workflows¶
Bootstrap Admin User (First Time Setup)¶
# 1. Start Docker services
docker-compose up -d
# 2. Wait for database to be ready
docker-compose exec postgres pg_isready
# 3. Create first admin
docker-compose exec web slidefactory user create-local \
admin@company.com \
--name "System Administrator" \
--preset admin
Create Team Members¶
# ESG Team member
docker-compose exec web slidefactory user create-local \
esg.user@company.com \
--name "ESG Team Member" \
--preset workflow-user \
--workflow esg2
# Viewer
docker-compose exec web slidefactory user create-local \
viewer@company.com \
--name "Read-Only User" \
--preset viewer
Manage Existing User¶
# Show user details
docker-compose exec web slidefactory user show user@company.com
# Add scope
docker-compose exec web slidefactory user add-scope \
user@company.com \
"presentations:generate"
# Deactivate user
docker-compose exec web slidefactory user deactivate user@company.com
Docker Compose Environment Files¶
User management respects environment configuration in Docker.
Local Development (docker-compose.override.yml):
Key Variables:
# .env.local
DATABASE_URL=postgresql+psycopg2://postgres:postgres@postgres:5432/slidefactory
AZURE_TENANT_ID=your-tenant-id # For Entra integration
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-secret
Automated User Provisioning Script¶
Create a bash script for batch user creation in Docker:
scripts/create_users.sh:
#!/bin/bash
# Create multiple users in Docker environment
# Admin
docker-compose exec -T web slidefactory user create-local \
admin@company.com \
--name "Admin" \
--password "changeme" \
--preset admin
# Team members
docker-compose exec -T web slidefactory user create-local \
esg1@company.com \
--name "ESG User 1" \
--password "changeme" \
--preset workflow-user \
--workflow esg2
docker-compose exec -T web slidefactory user create-local \
esg2@company.com \
--name "ESG User 2" \
--password "changeme" \
--preset workflow-user \
--workflow esg2
echo "✓ Users created successfully"
Usage:
# Make executable
chmod +x scripts/create_users.sh
# Ensure Docker services are running
docker-compose up -d
# Run the script
./scripts/create_users.sh
Note: The -T flag disables pseudo-TTY allocation for non-interactive script execution.
Entra (Azure AD) Integration¶
Overview¶
Entra (Azure AD) integration provides: - SSO: Users authenticate with Microsoft credentials - JIT Provisioning: Users auto-created on first login - Group-Based Scopes: Permissions inherited from Entra groups - Centralized Management: IT controls access via Azure AD
Configuration¶
Environment Variables:
# .env or .env.local
AZURE_TENANT_ID=12345678-1234-1234-1234-123456789abc
AZURE_CLIENT_ID=87654321-4321-4321-4321-987654321cba
AZURE_CLIENT_SECRET=your-client-secret-here
AZURE_REDIRECT_URI=https://your-domain.com/auth/azure/callback
Entra App Registration: 1. Create app registration in Azure Portal 2. Configure redirect URI: https://your-domain.com/auth/azure/callback 3. Enable ID tokens in Authentication settings 4. Create client secret in Certificates & Secrets 5. Configure API permissions: User.Read, GroupMember.Read.All
Group to Scope Mapping¶
Scopes are mapped to Entra groups in app/auth/entra_mappings.py.
Example Configuration:
ENTRA_GROUP_SCOPES = {
"slidefactory-admins": [
"*" # Full access
],
"slidefactory-esg-team": [
"workflows:esg2:read",
"workflows:esg2:execute",
"templates:esg2:read",
"presentations:generate",
"presentations:read",
"results:read"
],
"slidefactory-viewers": [
"presentations:read",
"results:read"
]
}
JIT Provisioning Flow¶
First Login: 1. User signs in via Azure AD 2. Slidefactory retrieves user's Entra groups 3. Scopes calculated from group mappings (union of all groups) 4. User record created in app_users table 5. User gains access with computed scopes
Subsequent Logins: 1. User signs in via Azure AD 2. Scopes loaded from database (NOT recalculated) 3. Last login timestamp updated 4. Entra groups updated in entra_groups field (for audit)
Scope Override Behavior¶
| Scenario | Scope Source | scopes_overridden |
|---|---|---|
| First login (Entra) | Computed from groups | False |
| Subsequent logins | Database | False |
| Admin adds scope via CLI | Database (modified) | True |
| Admin removes scope via CLI | Database (modified) | True |
| Admin sets scopes via CLI | Database (replaced) | True |
| Admin resets to groups | Recomputed from groups | False |
Key Point: Once an admin manually modifies scopes, the user becomes "overridden" and group changes no longer auto-apply.
Managing Entra Users¶
View Entra Users¶
# List only Entra users
docker-compose exec web slidefactory user list --provider azure_ad --show-groups
# Show specific Entra user
docker-compose exec web slidefactory user show user@company.com
Add Scope to Entra User¶
# Manually grant additional permission
docker-compose exec web slidefactory user add-scope user@company.com "workflows:esg3:read"
# User is now "overridden" - group changes won't auto-apply
Reset Entra User to Groups¶
# Recompute scopes from current group memberships
docker-compose exec web slidefactory user reset-to-groups user@company.com
# This clears the "overridden" flag
Entra Group Changes¶
When IT adds/removes user from Entra group:
| User State | Behavior |
|---|---|
| Not overridden | Next login: Group list updated, but scopes unchanged (database wins) |
| Overridden | Next login: Group list updated, scopes unchanged (admin manual changes preserved) |
To apply new group memberships:
# Option 1: Reset specific user
docker-compose exec web slidefactory user reset-to-groups user@company.com
# Option 2: User must be deleted and re-provisioned on next login
docker-compose exec web python -c "from app.util.database import SessionLocal; from app.auth.users.models import User; db = SessionLocal(); db.query(User).filter(User.email=='user@company.com').delete(); db.commit(); print('User deleted - will be reprovisioned on next login')"
Troubleshooting Entra Integration¶
User can't log in: 1. Check Azure App Registration configuration 2. Verify redirect URI matches exactly 3. Check client secret is valid 4. Review application logs: docker-compose logs web | grep azure
User has wrong permissions: 1. Check Entra group membership in Azure Portal 2. View user's stored groups: docker-compose exec web slidefactory user show user@company.com 3. Check group mappings: docker-compose exec web slidefactory user list-groups 4. Reset user to groups: docker-compose exec web slidefactory user reset-to-groups user@company.com
Scopes not updating after group change: - This is expected behavior (database is source of truth after first login) - Use reset-to-groups command to reapply group-based scopes
Common Workflows¶
Initial Setup (New Deployment)¶
# 1. Start services
docker-compose up -d
# 2. Create emergency admin (local)
docker-compose exec web slidefactory user create-local \
admin@company.com \
--name "Emergency Admin" \
--preset admin
# 3. Configure Entra integration (edit .env)
# Add AZURE_TENANT_ID, AZURE_CLIENT_ID, etc.
# 4. Restart to apply config
docker-compose restart web
# 5. Have first Entra user log in (auto-provisioned)
# 6. Verify Entra user
docker-compose exec web slidefactory user list --provider azure_ad
Team Member Onboarding¶
Option 1: Entra (Recommended for enterprise) 1. IT adds user to Entra group (e.g., slidefactory-esg-team) 2. User visits Slidefactory and signs in with Microsoft 3. Account auto-created with group-based scopes 4. User gains access immediately
Option 2: Local user (for contractors/special cases)
docker-compose exec web slidefactory user create-local \
contractor@company.com \
--name "Contractor Name" \
--preset workflow-user \
--workflow esg2
# Send credentials securely to contractor
Granting Additional Access¶
Scenario: User needs access to a second workflow.
# Check current scopes
docker-compose exec web slidefactory user show user@company.com
# Add workflow access
docker-compose exec web slidefactory user add-scope \
user@company.com \
"workflows:esg3:read"
docker-compose exec web slidefactory user add-scope \
user@company.com \
"workflows:esg3:execute"
docker-compose exec web slidefactory user add-scope \
user@company.com \
"templates:esg3:read"
# Verify changes
docker-compose exec web slidefactory user show user@company.com
Revoking Access¶
Deactivate user:
Remove specific scopes:
docker-compose exec web slidefactory user remove-scope \
user@company.com \
"workflows:esg3:execute"
Reset to minimum access:
Service Account Setup¶
For API integrations:
docker-compose exec web slidefactory user create-local \
api-service@company.com \
--name "API Service Account" \
--password "secure-random-password" \
--preset api-user
# Or with custom scopes
docker-compose exec web slidefactory user create-local \
ci-cd@company.com \
--name "CI/CD Pipeline" \
--password "generated-secret" \
--scopes "presentations:generate,presentations:read"
Audit User Access¶
List all users with scopes:
Check who has admin access:
Find users with specific scope:
Emergency Access Recovery¶
If admin account is locked/lost:
-
Create new emergency admin via database:
# Access database directly docker-compose exec postgres psql -U postgres -d slidefactory # Create emergency admin (manual SQL) INSERT INTO app_users (email, name, password, scopes, auth_provider, active, created_at) VALUES ( 'emergency@company.com', 'Emergency Admin', -- Password hash for "temppassword123" (example - generate your own) '$2b$12$examplehash...', '["*"]'::jsonb, 'local', true, NOW() ); -
Or use environment variable:
Troubleshooting¶
User Can't Log In¶
Local user:
# Check user exists and is active
docker-compose exec web slidefactory user show user@example.com
# If inactive, activate
docker-compose exec web slidefactory user activate user@example.com
# Reset password
docker-compose exec web slidefactory user change-password user@example.com
Entra user:
# Check user exists
docker-compose exec web slidefactory user show user@company.com
# Check Azure configuration
docker-compose exec web env | grep AZURE_
# Check application logs
docker-compose logs web | grep -i "azure\|entra\|login"
User Has Wrong Permissions¶
Check scopes:
For Entra users:
# Check if scopes are overridden
docker-compose exec web slidefactory user show user@example.com | grep "Scopes Overridden"
# If No: Reset to group-based scopes
docker-compose exec web slidefactory user reset-to-groups user@example.com
# If Yes: Manually adjust scopes
docker-compose exec web slidefactory user add-scope user@example.com "needed:scope"
CLI Command Fails¶
Database connection error:
# Check database is running
docker-compose ps postgres
# Check DATABASE_URL
docker-compose exec web env | grep DATABASE_URL
# Test database connection
docker-compose exec postgres pg_isready
Import errors:
# Ensure you're using the slidefactory CLI command
docker-compose exec web slidefactory user list # Correct
docker-compose exec web python cli/commands/user.py list # Wrong
Scope Not Taking Effect¶
Clear session: User must log out and log back in for scope changes to take effect.
Verify in database:
docker-compose exec postgres psql -U postgres -d slidefactory -c \
"SELECT email, scopes FROM app_users WHERE email='user@example.com';"
Password Reset Not Working¶
Check password hash:
# Password must be bcrypt hashed
docker-compose exec web python -c "
import bcrypt
password = 'newpass123'
hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
print(hash.decode('utf-8'))
"
Reset via CLI:
Best Practices¶
Security¶
- Never share admin credentials - Create individual accounts
- Use Entra for team members - Better security and audit trail
- Rotate service account passwords - Especially for API integrations
- Minimum privilege - Grant only necessary scopes
- Regular audits - Review user access quarterly
- Deactivate, don't delete - Preserve audit trail
Scope Management¶
- Use presets - Consistent permissions across users
- Document custom scopes - Why was manual override needed?
- Test with limited account - Verify scope restrictions work
- Prefer group-based - Easier to manage at scale
- Avoid wildcard - Only for true superadmins
Docker Operations¶
- Use exec for live systems - Faster, doesn't interrupt services
- Use run for setup - Clean environment for bootstrapping
- Script repetitive tasks - Batch user creation, etc.
- Version control scripts - Track user provisioning changes
- Test in dev first - Especially scope changes
Reference¶
User Model Fields¶
| Field | Type | Description |
|---|---|---|
id | Integer | Primary key |
email | String(255) | Unique identifier, indexed |
name | String(255) | Display name |
password | String(255) | Bcrypt hash (nullable for Entra) |
scopes | JSONB | Array of scope strings |
auth_provider | String(50) | "local" or "azure_ad" |
entra_groups | JSONB | Last known Entra groups (audit) |
scopes_overridden | Boolean | Manual changes made |
active | Boolean | Account enabled |
last_login | DateTime | Last successful login |
created_at | DateTime | Account creation timestamp |
attributes | JSONB | Legacy field (deprecated) |
CLI Exit Codes¶
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (user not found, invalid input, etc.) |
Environment Variables¶
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string |
AZURE_TENANT_ID | Entra only | Azure AD tenant ID |
AZURE_CLIENT_ID | Entra only | Azure app client ID |
AZURE_CLIENT_SECRET | Entra only | Azure app secret |
EMERGENCY_ADMIN_EMAIL | No | Bootstrap admin email |
EMERGENCY_ADMIN_PASSWORD | No | Bootstrap admin password |
See Also¶
- BRANDING.md - Whitelabel customization
- Authorization System Design - Authorization system design (archived)
Source Code References (not in documentation): - app/auth/entra_mappings.py - Group to scope mappings - app/auth/permissions.py - Permission decorators
Last Updated: 2025-11-04 Version: 1.0