Synology Photos manages user permissions using its own internal database, not the filesystem ACLs. This means:
- Users with read access to the
/photosshared folder on the filesystem (e.g., via SMB/SAMBA) can see all photos, regardless of the restrictions set in Synology Photos. - In Synology Photos, users only see the photos and folders they have been explicitly authorized to access, as defined in the Photos database.
Pain Point: There is a mismatch between what users can access via the filesystem and what they are allowed to see in Synology Photos. This can lead to privacy or security issues, as users may access files outside their intended scope if they use direct filesystem access.
Goal: The intention of this project is to find a way to align Synology Photos permissions (as managed in its database) with the actual filesystem ACLs/permissions, so that access is consistent whether users access photos via Synology Photos or directly through the filesystem.
A practical example of this solution is deploying Immich (self-hosted photo management) to access photos through SAMBA while respecting Synology Photos user permissions.
With this implementation:
- Immich can connect to the Synology via SAMBA using specific user credentials
- Each user's Immich instance will only see photos they're authorized to access in Synology Photos
- The filesystem permissions automatically enforce the same access controls as the Synology Photos application
- No need to maintain duplicate permission structures or worry about users accessing unauthorized content
This enables seamless integration between Synology Photos' permission system and external applications that access photos through standard filesystem protocols.
To analyze and reverse engineer Synology Photos permissions, the first step is to extract the database from your Synology NAS and import it into a local PostgreSQL instance for inspection.
Run the following commands on your Synology as root:
# as root
mkdir /volume1/pg_dump
chown postgres: /volume1/pg_dump
su - postgres
cd /volume1/pg_dump
pg_dump synofoto > synofoto.sqlThis will create a SQL dump of the synofoto database.
Create a docker-compose.yml file with the following content:
services:
db:
image: postgres:15
container_name: synofoto-db
restart: unless-stopped
environment:
POSTGRES_USER: syno
POSTGRES_PASSWORD: synopass
POSTGRES_DB: synofoto
ports:
- "5432:5432"Start the database:
docker-compose up -dCopy the SQL dump into the container:
docker cp ./synofoto.sql synofoto-db:/tmp/synofoto.sqlPrepare the import (create the required role):
docker exec -u postgres synofoto-db psql -d synofoto -c 'CREATE ROLE "SynologyPhotos";'Import the database dump:
docker exec -it synofoto-db bash
psql -U syno -d synofoto -f /tmp/synofoto.sqlThe main tables involved are:
folder: Contains folder metadata. Shared folders haveid_user = 0.share_permission: Stores permissions for users/groups on shared folders, linked viapassphrase_share.user_info: Stores user information (id, uid, name).
- Permissions are stored as bitmaps in the
permissioncolumn (e.g., 3 for view, 7 for download, 15 upload, 31 for manage) - Each row in
share_permissionlinks a user/group (target_id) to a shared folder viapassphrase_share.
To list all users with permissions on a shared folder of id = 92 (excluding system user 0):
SELECT sp.target_id AS user_id, ui.uid AS username, ui.name AS user_fullname, sp.permission
FROM share_permission sp
JOIN user_info ui ON sp.target_id = ui.id
JOIN folder f ON f.passphrase_share = sp.passphrase_share
WHERE f.id = 92 AND sp.target_id != 0 AND sp.permission > 0;For folder id = 92, the query returns:
| user_id | username | user_fullname | permission |
|---|---|---|---|
| 3 | 1026 | valentin | 15 |
| 10 | 1033 | bonzac | 3 |
| 5 | 1028 | mathilde | 3 |
| 11 | 1029 | famille | 3 |
✅ COMPLETED - The solution has been implemented with a comprehensive set of scripts.
Exports all shared folder permissions from the Synology Photos database to structured JSON format. Usage:
./export_permissions_json.sh [output_file]Key Features:
- Extracts complete permission structure from synofoto database
- Role-based permission decoding (viewer, downloader, uploader, manager, admin)
- JSON output with validation and optional formatting
- Exports saved to dedicated
exports/folder - Comprehensive logging and error handling
Permission Structure: The script maps database permission bitmaps to human-readable roles:
- 1 = viewer (view only)
- 3 = downloader (view + download)
- 7 = uploader (view + download + upload)
- 15 = manager (view + download + upload + manage)
- 31 = admin (all permissions)
Output JSON Structure:
{
"export_info": {
"timestamp": "2025-01-31T15:30:45+01:00",
"source_database": "synofoto",
"permission_bitmap_legend": { ... }
},
"shared_folders": [
{
"folder_id": 304,
"folder_name": "/Scans/2024/janvier",
"users": [
{
"user_id": 11,
"username": "famille",
"permission_bitmap": 3,
"permissions_decoded": {
"role": "downloader",
"permissions": ["view", "download"]
}
}
]
}
],
"summary": {
"total_shared_folders": 25,
"total_user_permissions": 157
}
}Database Query: The script uses this core query to extract permissions:
SELECT
f.id as folder_id,
f.name as folder_name,
f.passphrase_share,
ui.id as user_id,
ui.name as username,
ui.uid as user_uid,
sp.permission as permission_bitmap
FROM folder f
JOIN share_permission sp ON f.passphrase_share = sp.passphrase_share
LEFT JOIN user_info ui ON sp.target_id = ui.id
WHERE f.id > 1 AND sp.permission > 0 AND sp.target_id != 0
ORDER BY f.id, ui.name;Synchronizes filesystem ACLs with database permissions for a specific folder. Usage:
./sync_permissions.sh [FOLDER_ID]Key Features:
- Aligns database permissions with filesystem ACLs for read-only access
- Handles complex parent-child folder permission hierarchies
- Implements intelligent path traversal with execute-only permissions
- Preserves system security by granting only read access regardless of database permission level
Ensures coherence between database permissions and filesystem access. Usage:
./permission_audit.sh # Full detailed audit (default)
./permission_audit.sh summary # Quick overview without detailed folder output
./permission_audit.sh folder <FOLDER_ID> # Audit specific folder
./permission_audit.sh user <username> # Audit specific user across all folders
./permission_audit.sh debug <FOLDER_ID> # Debug ACL details for specific folderFeatures:
- Comprehensive validation of permission alignment
- Detailed reporting of mismatches and inconsistencies
- Summary mode for quick system-wide overview
- Tracks execute-only permissions for path traversal
Processes all shared folders in the database automatically. Usage:
./batch_sync.sh # Normal mode with full output
./batch_sync.sh --silent # Silent mode (minimal output, errors only)Features:
- Discovers all shared folders from the database
- Processes each folder sequentially with full logging
- Handles edge cases where folders are processed before knowing child requirements
- Automatic log rotation and comprehensive error handling
- Silent mode for automated scheduling
The system implements sophisticated path traversal logic:
- Parent-Child Re-evaluation: Because folders are processed before knowing if they have child folders requiring different permissions, the system recalculates permissions from top parent to child at each level
- Execute-Only Trick: Users who need access to child folders but not parent folders receive execute-only (
x) permissions on parent directories, allowing navigation without granting read access to parent content - Dynamic ACL Adjustment: Parent deny rules are replaced with execute-only permissions when users need traversal access to reach authorized subfolders
Example: If user bonzac has access to /Scans/Family but not /Scans, the system grants:
- Execute-only permission on
/Scans(for traversal) - Full read permission on
/Scans/Family
- Read-Only Alignment: All filesystem permissions are read-only, regardless of database permission level (view, download, upload, manage)
- Minimal Privilege: Users receive only the minimum filesystem permissions needed to access their authorized content
- System Preservation: System ACL rules are preserved and never modified
For regular maintenance, run:
./batch_sync.sh && ./permission_audit.sh summaryFor automated/scheduled runs:
./batch_sync.sh --silent && ./permission_audit.sh summaryFor database backup and analysis:
./export_permissions_json.sh- Scripts: Main directory contains all executable scripts
- Logs:
logs/folder contains execution logs for all scripts - Exports:
exports/folder contains JSON exports from permission analysis - Personal:
personal/folder contains user-specific customizations and extensions
This will:
- Synchronize all shared folder permissions system-wide
- Provide a summary audit to verify coherence
Tested and validated system-wide.
For production environments, it's recommended to run the sync and audit automatically on a nightly basis using Synology's built-in Task Scheduler.
- Access Synology DSM: Log into your Synology DSM as administrator
- Open Task Scheduler: Go to Control Panel → Task Scheduler
- Create New Task: Click "Create" → "Scheduled Task" → "User-defined script"
- Configure Task:
- Task Name:
Synology Photos Permission Sync - User:
root - Schedule: Daily at 2:00 AM (or preferred time during low usage)
- Task Settings → User-defined script:
/volume1/tools/Synology/synology-photos-shared-permissions/nightly_sync_audit.sh
- Settings → Send run details by email: ✅ Enabled
- Send run details only when the script terminates abnormally: ✅ Enabled
- Task Name:
Synology's Task Scheduler automatically handles email notifications:
- ✅ Silent on Success: No email sent when script exits with code 0
- 📧 Alert on Issues: Sends email notification when script exits with code 1:
- Batch sync failures
- Permission audit detects misalignments
- Includes full script output and error details
- Batch Sync: Runs
./batch_sync.sh --silentto align all folder permissions - Permission Audit: Runs
./permission_audit.sh summaryto verify alignment - Return Codes:
- Exit 0: All permissions perfectly aligned (no email)
- Exit 1: Issues detected (triggers Synology email notification)
- Log Management: Automatically rotates logs, keeping last 30 days
Test the scheduled task manually:
# Test the nightly script
sudo /volume1/tools/Synology/synology-photos-shared-permissions/nightly_sync_audit.sh
[2025-07-29 15:52:12] Starting nightly Synology Photos permission sync and audit
[2025-07-29 15:52:12] Running batch permission sync...
[2025-07-29 16:02:10] Batch sync completed successfully
[2025-07-29 16:02:10] Running permission audit...
[2025-07-29 16:04:00] Permission audit completed - All permissions aligned
# Check exit code
echo "Exit code: $?"All nightly runs are logged to:
logs/nightly_sync_audit_YYYYMMDD_HHMMSS.log
The project is organized with the following structure:
sync_permissions.sh- Individual folder synchronizationpermission_audit.sh- Coherence validationbatch_sync.sh- System-wide processingnightly_sync_audit.sh- Automated scheduling script
Development and testing utilities:
extended_test.sh- Extended testing scenariossimple_test.sh- Basic functionality teststest_problematic_folders.sh- Edge case testingvalidate_permissions.sh- Permission validation utilityfix_ownership.sh- PhotoStation migration cleanup: Fixes orphaned folders from PhotoStation → Synology Photos migration (UID 138862). Run this after migration to clean up unattached folders before permission synchronization.
All execution logs are automatically stored here with timestamped filenames.
This approach allows you to enumerate all users with access to a shared folder in Synology Photos, along with their permission levels, by querying the underlying database.
