mirror of
https://git.sr.ht/~sircmpwn/tokidoki
synced 2025-12-12 14:17:21 +01:00
Add CalDAV support, refactor
The filesystem storage backend now implements the required functions to act as a basic CalDAV server. Some refactoring was done based on the go-webdav development: introduce a UserPrincipalBackend, a new function to serve the user principal URL, and more. See this PR for lots of details: https://github.com/emersion/go-webdav/pull/62 Also adds a simple facility for debug output.
This commit is contained in:
parent
5728f1ee27
commit
001917295d
7 changed files with 487 additions and 85 deletions
|
|
@ -1,19 +1,83 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/emersion/go-webdav"
|
||||
"github.com/emersion/go-webdav/caldav"
|
||||
"github.com/emersion/go-webdav/carddav"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"git.sr.ht/~sircmpwn/tokidoki/auth"
|
||||
"git.sr.ht/~sircmpwn/tokidoki/debug"
|
||||
"git.sr.ht/~sircmpwn/tokidoki/storage"
|
||||
)
|
||||
|
||||
type userPrincipalBackend struct{}
|
||||
|
||||
func (u *userPrincipalBackend) CurrentUserPrincipal(ctx context.Context) (string, error) {
|
||||
authCtx, ok := auth.FromContext(ctx)
|
||||
if !ok {
|
||||
panic("Invalid data in auth context!")
|
||||
}
|
||||
if authCtx == nil {
|
||||
return "", fmt.Errorf("unauthenticated requests are not supported")
|
||||
}
|
||||
|
||||
userDir := base64.RawStdEncoding.EncodeToString([]byte(authCtx.UserName))
|
||||
return "/" + userDir + "/", nil
|
||||
}
|
||||
|
||||
type tokidokiHandler struct {
|
||||
upBackend webdav.UserPrincipalBackend
|
||||
authBackend auth.AuthProvider
|
||||
caldavBackend caldav.Backend
|
||||
carddavBackend carddav.Backend
|
||||
}
|
||||
|
||||
func (u *tokidokiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
userPrincipalPath, err := u.upBackend.CurrentUserPrincipal(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
var homeSets []webdav.BackendSuppliedHomeSet
|
||||
if u.caldavBackend != nil {
|
||||
path, err := u.caldavBackend.CalendarHomeSetPath(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
} else {
|
||||
homeSets = append(homeSets, caldav.NewCalendarHomeSet(path))
|
||||
}
|
||||
}
|
||||
if u.carddavBackend != nil {
|
||||
path, err := u.carddavBackend.AddressbookHomeSetPath(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
} else {
|
||||
homeSets = append(homeSets, carddav.NewAddressBookHomeSet(path))
|
||||
}
|
||||
}
|
||||
|
||||
opts := webdav.ServeUserPrincipalOptions{
|
||||
UserPrincipalPath: userPrincipalPath,
|
||||
HomeSets: homeSets,
|
||||
}
|
||||
|
||||
if webdav.ServeUserPrincipal(w, r, opts) {
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
addr string
|
||||
|
|
@ -23,6 +87,7 @@ func main() {
|
|||
flag.StringVar(&addr, "addr", ":8080", "listening address")
|
||||
flag.StringVar(&authURL, "auth.url", "", "auth backend URL (required)")
|
||||
flag.StringVar(&storageURL, "storage.url", "", "storage backend URL (required)")
|
||||
flag.BoolVar(&debug.Enable, "debug", false, "enable debug output")
|
||||
flag.Parse()
|
||||
|
||||
if len(flag.Args()) != 0 || authURL == "" || storageURL == "" {
|
||||
|
|
@ -48,17 +113,36 @@ func main() {
|
|||
}
|
||||
mux.Use(authProvider.Middleware())
|
||||
|
||||
backend, err := storage.NewFromURL(storageURL)
|
||||
upBackend := &userPrincipalBackend{}
|
||||
|
||||
caldavBackend, carddavBackend, err := storage.NewFromURL(storageURL, "/calendar/", "/contacts/", upBackend)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to load storage backend: %s", err.Error())
|
||||
}
|
||||
mux.Mount("/", &carddav.Handler{Backend: backend})
|
||||
|
||||
carddavHandler := carddav.Handler{Backend: carddavBackend}
|
||||
caldavHandler := caldav.Handler{Backend: caldavBackend}
|
||||
handler := tokidokiHandler{
|
||||
upBackend: upBackend,
|
||||
authBackend: authProvider,
|
||||
caldavBackend: caldavBackend,
|
||||
carddavBackend: carddavBackend,
|
||||
}
|
||||
|
||||
mux.Mount("/", &handler)
|
||||
mux.Mount("/.well-known/caldav", &caldavHandler)
|
||||
mux.Mount("/.well-known/carddav", &carddavHandler)
|
||||
mux.Mount("/{user}/contacts", &carddavHandler)
|
||||
mux.Mount("/{user}/calendar", &caldavHandler)
|
||||
|
||||
server := http.Server{
|
||||
Addr: addr,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
log.Printf("Server running on %s", addr)
|
||||
debug.Printf("Debug output enabled")
|
||||
|
||||
err = server.ListenAndServe()
|
||||
if err != http.ErrServerClosed {
|
||||
log.Fatalf("ListenAndServe: %s", err.Error())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue