🏗️(caldav) migrate from davical to sabre/dav
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
# DAViCal CalDAV Server
|
||||
# Based on Debian with Apache and PHP
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apache2 \
|
||||
libapache2-mod-php \
|
||||
php-pgsql \
|
||||
php-xml \
|
||||
php-curl \
|
||||
php-imap \
|
||||
php-ldap \
|
||||
davical \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Enable required Apache modules
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# Copy Apache configuration
|
||||
COPY davical.conf /etc/apache2/sites-available/davical.conf
|
||||
RUN a2dissite 000-default && a2ensite davical
|
||||
|
||||
# Copy DAViCal configuration
|
||||
COPY config.php /etc/davical/config.php
|
||||
|
||||
# Copy and setup entrypoint
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# Set permissions
|
||||
RUN chown -R www-data:www-data /var/log/apache2
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* DAViCal Configuration
|
||||
* This file is mounted as /etc/davical/config.php
|
||||
* Overrides the default config generated by the fintechstudios/davical image
|
||||
*/
|
||||
|
||||
// Database connection - uses shared calendars database in public schema
|
||||
// The image will set these from PGHOST, PGDATABASE, PGUSER, PGPASSWORD
|
||||
$c->pg_connect[] = 'host=' . getenv('PGHOST') . ' port=' . (getenv('PGPORT') ?: '5432') . ' dbname=' . getenv('PGDATABASE') . ' user=' . getenv('PGUSER') . ' password=' . getenv('PGPASSWORD');
|
||||
|
||||
// System name
|
||||
$c->system_name = 'Calendars DAViCal Server';
|
||||
|
||||
// Admin email
|
||||
$c->admin_email = 'admin@example.com';
|
||||
|
||||
// Allow public access for CalDAV discovery
|
||||
$c->public_freebusy_url = true;
|
||||
|
||||
// Default locale
|
||||
$c->default_locale = 'en_US.UTF-8';
|
||||
|
||||
// Logging - enable for debugging authentication issues
|
||||
$c->log_caldav_queries = true;
|
||||
|
||||
// Trust proxy headers for auth
|
||||
$c->trust_x_forwarded = true;
|
||||
|
||||
// Configure base path when behind reverse proxy
|
||||
// Override SCRIPT_NAME so DAViCal generates correct URLs
|
||||
// DAViCal uses $_SERVER['SCRIPT_NAME'] to determine the base path for URLs
|
||||
// We set it to the proxy path WITHOUT /caldav.php since DAViCal will add that itself
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_PREFIX'])) {
|
||||
$_SERVER['SCRIPT_NAME'] = rtrim($_SERVER['HTTP_X_FORWARDED_PREFIX'], '/');
|
||||
} elseif (isset($_SERVER['HTTP_X_SCRIPT_NAME'])) {
|
||||
$_SERVER['SCRIPT_NAME'] = rtrim($_SERVER['HTTP_X_SCRIPT_NAME'], '/');
|
||||
}
|
||||
|
||||
// Custom authentication function to use X-Forwarded-User header
|
||||
// This function is called by DAViCal's authentication system
|
||||
function authenticate_via_forwarded_user( $username, $password ) {
|
||||
// Check if X-Forwarded-User header is present
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_USER'])) {
|
||||
$forwarded_user = trim($_SERVER['HTTP_X_FORWARDED_USER']);
|
||||
|
||||
// If the username from Basic Auth matches X-Forwarded-User, authenticate
|
||||
// Users with password '*' are externally authenticated
|
||||
if (strtolower($username) === strtolower($forwarded_user)) {
|
||||
// Return the username to authenticate as this user
|
||||
// DAViCal will check if user exists and has password '*'
|
||||
return $forwarded_user;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to standard authentication
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use custom authentication hook
|
||||
$c->authenticate_hook = array(
|
||||
'call' => 'authenticate_via_forwarded_user',
|
||||
'config' => array()
|
||||
);
|
||||
@@ -1,22 +0,0 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
DocumentRoot /usr/share/davical/htdocs
|
||||
DirectoryIndex index.php
|
||||
|
||||
Alias /images/ /usr/share/davical/htdocs/images/
|
||||
|
||||
<Directory /usr/share/davical/htdocs>
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
AcceptPathInfo On
|
||||
|
||||
# CalDAV principal URL
|
||||
RewriteEngine On
|
||||
RewriteRule ^/caldav/(.*)$ /caldav.php/$1 [L]
|
||||
RewriteRule ^/\.well-known/caldav /caldav.php [R=301,L]
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/davical_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/davical_access.log combined
|
||||
</VirtualHost>
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/bash
|
||||
###
|
||||
# Run DB migrations necessary to use Davical.
|
||||
# Will create the database on first-run, and only run necessary migrations on subsequent runs.
|
||||
#
|
||||
# Requires the following environment variables in addition to the container variables.
|
||||
# - ROOT_PGUSER
|
||||
# - ROOT_PGPASSWORD
|
||||
# - DAVICAL_ADMIN_PASS
|
||||
###
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z ${ROOT_PGUSER+x} ]; then
|
||||
echo "ROOT_PGUSER must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${ROOT_PGPASSWORD+x} ]; then
|
||||
echo "ROOT_PGPASSWORD must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${DAVICAL_ADMIN_PASS+x} ]; then
|
||||
echo "DAVICAL_ADMIN_PASS must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z ${DBA_PGPASSWORD+x} ]; then
|
||||
DBA_PGPASSWORD=$PGPASSWORD
|
||||
fi
|
||||
|
||||
if [ -z ${DAVICAL_SCHEMA+x} ]; then
|
||||
DAVICAL_SCHEMA=$DBA_PGUSER
|
||||
fi
|
||||
|
||||
# store PG environment so it can be overridden as-needed
|
||||
DAVICAL_PGUSER=$PGUSER
|
||||
DAVICAL_PGPASSWORD=$PGPASSWORD
|
||||
DAVICAL_PGDATABASE=$PGDATABASE
|
||||
|
||||
run_migrations() {
|
||||
echo "Running dba/update-davical-database, which should automatically apply any necessary DB migrations."
|
||||
/usr/share/davical/dba/update-davical-database \
|
||||
--dbname $DAVICAL_PGDATABASE \
|
||||
--dbuser $DBA_PGUSER \
|
||||
--dbhost $PGHOST \
|
||||
--dbpass $DBA_PGPASSWORD \
|
||||
--appuser $DAVICAL_PGUSER \
|
||||
--owner $DBA_PGUSER
|
||||
}
|
||||
|
||||
export PGUSER=$ROOT_PGUSER
|
||||
export PGPASSWORD=$ROOT_PGPASSWORD
|
||||
export PGDATABASE=
|
||||
|
||||
# Wait for PG connection
|
||||
retries=10
|
||||
until pg_isready -q -t 3; do
|
||||
[[ retries -eq 0 ]] && echo "Could not connect to Postgres" && exit 1
|
||||
echo "Waiting for Postgres to be available"
|
||||
retries=$((retries-1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Check whether the database has already been setup, with awl tables.
|
||||
tables=$(psql -d $DAVICAL_PGDATABASE -c "\\dt")
|
||||
if echo "$tables" | grep -q "awl_db_revision"; then
|
||||
# The database already exists - just run any outstanding migrations
|
||||
run_migrations
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Database has not been created - running first-time database setup"
|
||||
|
||||
# the rest of the commands are run as the dba superuser
|
||||
export PGUSER=$DBA_PGUSER
|
||||
export PGPASSWORD=$DBA_PGPASSWORD
|
||||
export PGDATABASE=$DAVICAL_PGDATABASE
|
||||
|
||||
psql -qXAt -f /usr/share/awl/dba/awl-tables.sql
|
||||
psql -qXAt -f /usr/share/awl/dba/schema-management.sql
|
||||
psql -qXAt -f /usr/share/davical/dba/davical.sql
|
||||
run_migrations
|
||||
psql -qXAt -f /usr/share/davical/dba/base-data.sql
|
||||
|
||||
# DAViCal only uses salted SHA1 at-best, but it's better than storing the password in plaintext!
|
||||
# see https://wiki.davical.org/index.php?title=Force_Admin_Password
|
||||
# from https://gitlab.com/davical-project/awl/-/blob/3f044e2dc8435c2eeba61a3c41ec11c820711ab3/inc/DataUpdate.php#L48-58
|
||||
salted_password=$(php -r 'require "/usr/share/awl/inc/AWLUtilities.php"; echo session_salted_sha1($argv[1]);' "$DAVICAL_ADMIN_PASS")
|
||||
psql -qX \
|
||||
-v pw="'$salted_password'" \
|
||||
<<EOF
|
||||
UPDATE usr SET password = :pw WHERE user_no = 1;
|
||||
EOF
|
||||
@@ -37,10 +37,4 @@ EOSQL
|
||||
# Create databases for all services
|
||||
# The main 'calendar' database is created by default via POSTGRES_DB
|
||||
|
||||
# DAViCal database
|
||||
create_database "davical" "davical" "davical_pass"
|
||||
|
||||
# Keycloak database
|
||||
create_database "keycloak" "keycloak" "keycloak_pass"
|
||||
|
||||
echo "All databases initialized successfully!"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Initialize shared calendars database for local development
|
||||
# All services (Django, DAViCal, Keycloak) use the same database in public schema
|
||||
# All services (Django, Caldav, Keycloak) use the same database in public schema
|
||||
# This script runs as POSTGRES_USER on first database initialization
|
||||
|
||||
set -e
|
||||
|
||||
53
docker/sabredav/Dockerfile
Normal file
53
docker/sabredav/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
# sabre/dav CalDAV Server
|
||||
# Based on Debian with Apache and PHP
|
||||
FROM php:8.2-apache-bookworm
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
git \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo pdo_pgsql
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Create application directory
|
||||
WORKDIR /var/www/sabredav
|
||||
|
||||
# Copy composer files
|
||||
COPY composer.json ./
|
||||
|
||||
# Install sabre/dav and dependencies
|
||||
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Copy server configuration
|
||||
COPY server.php ./
|
||||
COPY sabredav.conf /etc/apache2/sites-available/sabredav.conf
|
||||
COPY init-database.sh /usr/local/bin/init-database.sh
|
||||
|
||||
# Copy SQL schema files for database initialization
|
||||
COPY sql/ ./sql/
|
||||
|
||||
# Copy custom principal backend
|
||||
COPY src/ ./src/
|
||||
|
||||
# Enable Apache modules and site
|
||||
RUN a2enmod rewrite headers \
|
||||
&& a2dissite 000-default \
|
||||
&& a2ensite sabredav \
|
||||
&& chmod +x /usr/local/bin/init-database.sh
|
||||
|
||||
# Set permissions
|
||||
RUN chown -R www-data:www-data /var/www/sabredav \
|
||||
&& chmod -R 755 /var/www/sabredav
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["apache2-foreground"]
|
||||
16
docker/sabredav/composer.json
Normal file
16
docker/sabredav/composer.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "calendars/sabredav-server",
|
||||
"description": "sabre/dav CalDAV server for calendars",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"sabre/dav": "^4.5",
|
||||
"ext-pdo": "*",
|
||||
"ext-pdo_pgsql": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Calendars\\SabreDav\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
87
docker/sabredav/init-database.sh
Normal file
87
docker/sabredav/init-database.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
###
|
||||
# Initialize sabre/dav database schema in PostgreSQL
|
||||
# This script creates all necessary tables for sabre/dav to work
|
||||
###
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z ${PGHOST+x} ]; then
|
||||
echo "PGHOST must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGDATABASE+x} ]; then
|
||||
echo "PGDATABASE must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGUSER+x} ]; then
|
||||
echo "PGUSER must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGPASSWORD+x} ]; then
|
||||
echo "PGPASSWORD must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export PGHOST
|
||||
export PGPORT=${PGPORT:-5432}
|
||||
export PGDATABASE
|
||||
export PGUSER
|
||||
export PGPASSWORD
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
retries=30
|
||||
until pg_isready -q -h "$PGHOST" -p "$PGPORT" -U "$PGUSER"; do
|
||||
[[ retries -eq 0 ]] && echo "Could not connect to Postgres" && exit 1
|
||||
echo "Waiting for Postgres to be available..."
|
||||
retries=$((retries-1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "PostgreSQL is ready. Initializing sabre/dav database schema..."
|
||||
|
||||
# SQL files directory (will be copied into container)
|
||||
SQL_DIR="/var/www/sabredav/sql"
|
||||
|
||||
# Check if tables already exist
|
||||
TABLES_EXIST=$(psql -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name IN ('users', 'principals', 'calendars')" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$TABLES_EXIST" -gt "0" ]; then
|
||||
echo "sabre/dav tables already exist, skipping initialization"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create tables
|
||||
echo "Creating sabre/dav tables..."
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.users.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.users.sql"
|
||||
echo "Created users table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.principals.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.principals.sql"
|
||||
echo "Created principals table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.calendars.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.calendars.sql"
|
||||
echo "Created calendars table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.addressbooks.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.addressbooks.sql"
|
||||
echo "Created addressbooks table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.locks.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.locks.sql"
|
||||
echo "Created locks table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.propertystorage.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.propertystorage.sql"
|
||||
echo "Created propertystorage table"
|
||||
fi
|
||||
|
||||
echo "sabre/dav database schema initialized successfully!"
|
||||
28
docker/sabredav/sabredav.conf
Normal file
28
docker/sabredav/sabredav.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
DocumentRoot /var/www/sabredav
|
||||
|
||||
<Directory /var/www/sabredav>
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
Options -Indexes +FollowSymLinks
|
||||
</Directory>
|
||||
|
||||
# Set REMOTE_USER from X-Forwarded-User header (set by Django proxy)
|
||||
# This allows sabre/dav to use Apache auth backend
|
||||
<IfModule mod_headers.c>
|
||||
RequestHeader set REMOTE_USER %{HTTP:X-Forwarded-User}e env=HTTP_X_FORWARDED_USER
|
||||
</IfModule>
|
||||
|
||||
# Rewrite rules for CalDAV
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ server.php [QSA,L]
|
||||
|
||||
# Well-known CalDAV discovery
|
||||
RewriteRule ^\.well-known/caldav / [R=301,L]
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/sabredav_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/sabredav_access.log combined
|
||||
</VirtualHost>
|
||||
79
docker/sabredav/server.php
Normal file
79
docker/sabredav/server.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* sabre/dav CalDAV Server
|
||||
* Configured to use PostgreSQL backend and Apache authentication
|
||||
*/
|
||||
|
||||
use Sabre\DAV\Auth;
|
||||
use Sabre\DAVACL;
|
||||
use Sabre\CalDAV;
|
||||
use Sabre\CardDAV;
|
||||
use Sabre\DAV;
|
||||
use Calendars\SabreDav\AutoCreatePrincipalBackend;
|
||||
|
||||
// Composer autoloader
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// Set REMOTE_USER from X-Forwarded-User header (set by Django proxy)
|
||||
// This allows sabre/dav Apache auth backend to work with proxied requests
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_USER']) && !isset($_SERVER['REMOTE_USER'])) {
|
||||
$_SERVER['REMOTE_USER'] = $_SERVER['HTTP_X_FORWARDED_USER'];
|
||||
}
|
||||
|
||||
// Get base URI from environment variable (set by compose.yaml)
|
||||
// This ensures sabre/dav generates URLs with the correct proxy path
|
||||
$baseUri = getenv('CALENDARS_BASE_URI') ?: '/';
|
||||
|
||||
// Database connection from environment variables
|
||||
$dbHost = getenv('PGHOST') ?: 'postgresql';
|
||||
$dbPort = getenv('PGPORT') ?: '5432';
|
||||
$dbName = getenv('PGDATABASE') ?: 'calendars';
|
||||
$dbUser = getenv('PGUSER') ?: 'pgroot';
|
||||
$dbPass = getenv('PGPASSWORD') ?: 'pass';
|
||||
|
||||
// Create PDO connection
|
||||
$pdo = new PDO(
|
||||
"pgsql:host={$dbHost};port={$dbPort};dbname={$dbName}",
|
||||
$dbUser,
|
||||
$dbPass,
|
||||
[
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]
|
||||
);
|
||||
|
||||
// Create backend
|
||||
$authBackend = new Auth\Backend\Apache();
|
||||
|
||||
// Create authentication plugin
|
||||
$authPlugin = new Auth\Plugin($authBackend);
|
||||
|
||||
// Create CalDAV backend
|
||||
$caldavBackend = new CalDAV\Backend\PDO($pdo);
|
||||
|
||||
// Create CardDAV backend (optional, for future use)
|
||||
$carddavBackend = new CardDAV\Backend\PDO($pdo);
|
||||
|
||||
// Create principal backend with auto-creation support
|
||||
$principalBackend = new AutoCreatePrincipalBackend($pdo);
|
||||
|
||||
// Create directory tree
|
||||
$nodes = [
|
||||
new CalDAV\Principal\Collection($principalBackend),
|
||||
new CalDAV\CalendarRoot($principalBackend, $caldavBackend),
|
||||
new CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
|
||||
];
|
||||
|
||||
// Create server
|
||||
$server = new DAV\Server($nodes);
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
// Add plugins
|
||||
$server->addPlugin($authPlugin);
|
||||
$server->addPlugin(new CalDAV\Plugin());
|
||||
$server->addPlugin(new CardDAV\Plugin());
|
||||
$server->addPlugin(new DAVACL\Plugin());
|
||||
$server->addPlugin(new DAV\Browser\Plugin());
|
||||
|
||||
// Start server
|
||||
$server->start();
|
||||
44
docker/sabredav/sql/pgsql.addressbooks.sql
Normal file
44
docker/sabredav/sql/pgsql.addressbooks.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
CREATE TABLE addressbooks (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(255),
|
||||
displayname VARCHAR(255),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
synctoken INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY addressbooks
|
||||
ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX addressbooks_ukey
|
||||
ON addressbooks USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE cards (
|
||||
id SERIAL NOT NULL,
|
||||
addressbookid INTEGER NOT NULL,
|
||||
carddata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY cards
|
||||
ADD CONSTRAINT cards_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX cards_ukey
|
||||
ON cards USING btree (addressbookid, uri);
|
||||
|
||||
CREATE TABLE addressbookchanges (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
synctoken INTEGER NOT NULL,
|
||||
addressbookid INTEGER NOT NULL,
|
||||
operation SMALLINT NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY addressbookchanges
|
||||
ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX addressbookchanges_addressbookid_synctoken_ix
|
||||
ON addressbookchanges USING btree (addressbookid, synctoken);
|
||||
108
docker/sabredav/sql/pgsql.calendars.sql
Normal file
108
docker/sabredav/sql/pgsql.calendars.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
CREATE TABLE calendarobjects (
|
||||
id SERIAL NOT NULL,
|
||||
calendardata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
calendarid INTEGER NOT NULL,
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL,
|
||||
componenttype VARCHAR(8),
|
||||
firstoccurence INTEGER,
|
||||
lastoccurence INTEGER,
|
||||
uid VARCHAR(200)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarobjects
|
||||
ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarobjects_ukey
|
||||
ON calendarobjects USING btree (calendarid, uri);
|
||||
|
||||
|
||||
CREATE TABLE calendars (
|
||||
id SERIAL NOT NULL,
|
||||
synctoken INTEGER NOT NULL DEFAULT 1,
|
||||
components VARCHAR(21)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendars
|
||||
ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
CREATE TABLE calendarinstances (
|
||||
id SERIAL NOT NULL,
|
||||
calendarid INTEGER NOT NULL,
|
||||
principaluri VARCHAR(100),
|
||||
access SMALLINT NOT NULL DEFAULT '1', -- '1 = owner, 2 = read, 3 = readwrite'
|
||||
displayname VARCHAR(100),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
calendarorder INTEGER NOT NULL DEFAULT 0,
|
||||
calendarcolor VARCHAR(10),
|
||||
timezone TEXT,
|
||||
transparent SMALLINT NOT NULL DEFAULT '0',
|
||||
share_href VARCHAR(100),
|
||||
share_displayname VARCHAR(100),
|
||||
share_invitestatus SMALLINT NOT NULL DEFAULT '2' -- '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid'
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarinstances
|
||||
ADD CONSTRAINT calendarinstances_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_uri
|
||||
ON calendarinstances USING btree (principaluri, uri);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_calendarid
|
||||
ON calendarinstances USING btree (principaluri, calendarid);
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_share_href
|
||||
ON calendarinstances USING btree (principaluri, share_href);
|
||||
|
||||
CREATE TABLE calendarsubscriptions (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
principaluri VARCHAR(100) NOT NULL,
|
||||
source TEXT,
|
||||
displayname VARCHAR(100),
|
||||
refreshrate VARCHAR(10),
|
||||
calendarorder INTEGER NOT NULL DEFAULT 0,
|
||||
calendarcolor VARCHAR(10),
|
||||
striptodos SMALLINT NULL,
|
||||
stripalarms SMALLINT NULL,
|
||||
stripattachments SMALLINT NULL,
|
||||
lastmodified INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarsubscriptions
|
||||
ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarsubscriptions_ukey
|
||||
ON calendarsubscriptions USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE calendarchanges (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
synctoken INTEGER NOT NULL,
|
||||
calendarid INTEGER NOT NULL,
|
||||
operation SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarchanges
|
||||
ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX calendarchanges_calendarid_synctoken_ix
|
||||
ON calendarchanges USING btree (calendarid, synctoken);
|
||||
|
||||
CREATE TABLE schedulingobjects (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(255),
|
||||
calendardata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY schedulingobjects
|
||||
ADD CONSTRAINT schedulingobjects_pkey PRIMARY KEY (id);
|
||||
19
docker/sabredav/sql/pgsql.locks.sql
Normal file
19
docker/sabredav/sql/pgsql.locks.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE locks (
|
||||
id SERIAL NOT NULL,
|
||||
owner VARCHAR(100),
|
||||
timeout INTEGER,
|
||||
created INTEGER,
|
||||
token VARCHAR(100),
|
||||
scope SMALLINT,
|
||||
depth SMALLINT,
|
||||
uri TEXT
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY locks
|
||||
ADD CONSTRAINT locks_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX locks_token_ix
|
||||
ON locks USING btree (token);
|
||||
|
||||
CREATE INDEX locks_uri_ix
|
||||
ON locks USING btree (uri);
|
||||
30
docker/sabredav/sql/pgsql.principals.sql
Normal file
30
docker/sabredav/sql/pgsql.principals.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE principals (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
email VARCHAR(80),
|
||||
displayname VARCHAR(80)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY principals
|
||||
ADD CONSTRAINT principals_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX principals_ukey
|
||||
ON principals USING btree (uri);
|
||||
|
||||
CREATE TABLE groupmembers (
|
||||
id SERIAL NOT NULL,
|
||||
principal_id INTEGER NOT NULL,
|
||||
member_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY groupmembers
|
||||
ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX groupmembers_ukey
|
||||
ON groupmembers USING btree (principal_id, member_id);
|
||||
|
||||
INSERT INTO principals (uri,email,displayname) VALUES
|
||||
('principals/admin', 'admin@example.org','Administrator'),
|
||||
('principals/admin/calendar-proxy-read', null, null),
|
||||
('principals/admin/calendar-proxy-write', null, null);
|
||||
|
||||
13
docker/sabredav/sql/pgsql.propertystorage.sql
Normal file
13
docker/sabredav/sql/pgsql.propertystorage.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE propertystorage (
|
||||
id SERIAL NOT NULL,
|
||||
path VARCHAR(1024) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
valuetype INT,
|
||||
value BYTEA
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY propertystorage
|
||||
ADD CONSTRAINT propertystorage_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX propertystorage_ukey
|
||||
ON propertystorage (path, name);
|
||||
14
docker/sabredav/sql/pgsql.users.sql
Normal file
14
docker/sabredav/sql/pgsql.users.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE users (
|
||||
id SERIAL NOT NULL,
|
||||
username VARCHAR(50),
|
||||
digesta1 VARCHAR(32)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX users_ukey
|
||||
ON users USING btree (username);
|
||||
|
||||
INSERT INTO users (username,digesta1) VALUES
|
||||
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
|
||||
53
docker/sabredav/src/AutoCreatePrincipalBackend.php
Normal file
53
docker/sabredav/src/AutoCreatePrincipalBackend.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom principal backend that auto-creates principals when they don't exist.
|
||||
* This allows Apache authentication to work without pre-creating principals.
|
||||
*/
|
||||
|
||||
namespace Calendars\SabreDav;
|
||||
|
||||
use Sabre\DAVACL\PrincipalBackend\PDO as BasePDO;
|
||||
use Sabre\DAV\MkCol;
|
||||
|
||||
class AutoCreatePrincipalBackend extends BasePDO
|
||||
{
|
||||
/**
|
||||
* Returns a specific principal, specified by it's path.
|
||||
* Auto-creates the principal if it doesn't exist.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getPrincipalByPath($path)
|
||||
{
|
||||
$principal = parent::getPrincipalByPath($path);
|
||||
|
||||
// If principal doesn't exist, create it automatically
|
||||
if (!$principal && strpos($path, 'principals/') === 0) {
|
||||
// Extract username from path (e.g., "principals/user@example.com" -> "user@example.com")
|
||||
$username = substr($path, strlen('principals/'));
|
||||
|
||||
// Create principal directly in database
|
||||
// Access protected pdo property from parent
|
||||
$pdo = $this->pdo;
|
||||
$tableName = $this->tableName;
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO ' . $tableName . ' (uri, email, displayname) VALUES (?, ?, ?) ON CONFLICT (uri) DO NOTHING'
|
||||
);
|
||||
$stmt->execute([$path, $username, $username]);
|
||||
|
||||
// Retry getting the principal
|
||||
$principal = parent::getPrincipalByPath($path);
|
||||
} catch (\Exception $e) {
|
||||
// If creation fails, return null
|
||||
error_log("Failed to auto-create principal: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $principal;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user