diff --git a/pkg/routes/caldav/auth.go b/pkg/routes/caldav/auth.go index 9dd85e50a..1613fb9d1 100644 --- a/pkg/routes/caldav/auth.go +++ b/pkg/routes/caldav/auth.go @@ -18,9 +18,12 @@ package caldav import ( "errors" + "strings" + "time" "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/log" + "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user" "xorm.io/xorm" @@ -28,10 +31,62 @@ import ( "golang.org/x/crypto/bcrypt" ) +func checkAPIToken(s *xorm.Session, username, token string) (*user.User, error) { + apiToken, err := models.GetTokenFromTokenString(s, token) + if err != nil { + if models.IsErrAPITokenInvalid(err) { + log.Debugf("[caldav auth] Invalid API token provided for user %s", username) + return nil, nil + } + return nil, err + } + + if time.Now().After(apiToken.ExpiresAt) { + log.Debugf("[caldav auth] API token %d has expired", apiToken.ID) + return nil, nil + } + + if !apiToken.HasCaldavAccess() { + log.Debugf("[caldav auth] API token %d does not have caldav access permission", apiToken.ID) + return nil, nil + } + + u, err := user.GetUserByID(s, apiToken.OwnerID) + if err != nil { + if user.IsErrUserStatusError(err) { + log.Debugf("[caldav auth] API token %d owner account is disabled or locked", apiToken.ID) + return nil, nil + } + return nil, err + } + + if u.Username != username { + log.Debugf("[caldav auth] API token %d owner %s does not match provided username %s", apiToken.ID, u.Username, username) + return nil, nil + } + + return u, nil +} + func BasicAuth(c *echo.Context, username, password string) (bool, error) { s := db.NewSession() defer s.Close() + // If the password looks like an API token, validate it as one. + // Don't fall through to other auth methods — tk_ prefix is unambiguous. + if strings.HasPrefix(password, models.APITokenPrefix) { + u, err := checkAPIToken(s, username, password) + if err != nil { + log.Errorf("Error during API token auth for caldav: %v", err) + return false, nil + } + if u != nil { + c.Set("userBasicAuth", u) + return true, nil + } + return false, nil + } + credentials := &user.Login{ Username: username, Password: password,