mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-01-04 10:39:47 -06:00
[server] Use pgtype.UUID for User.Home
This commit is contained in:
@@ -38,7 +38,7 @@ func Require(c *gin.Context) {
|
||||
panic(err)
|
||||
} else {
|
||||
c.Set(keyUser, u)
|
||||
c.Set(keyFileSystem, u.OpenFileSystem(ctx))
|
||||
c.Set(keyFileSystem, u.OpenHomeFileSystem(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/api/authenticator"
|
||||
"github.com/shroff/phylum/server/internal/core/errors"
|
||||
"github.com/shroff/phylum/server/internal/core/user"
|
||||
@@ -15,9 +15,9 @@ type loginResponse struct {
|
||||
User loggedInUserResponse `json:"user"`
|
||||
}
|
||||
type loggedInUserResponse struct {
|
||||
Username string `json:"username"`
|
||||
Display string `json:"display"`
|
||||
Home uuid.UUID `json:"home"`
|
||||
Username string `json:"username"`
|
||||
Display string `json:"display"`
|
||||
Home pgtype.UUID `json:"home"`
|
||||
}
|
||||
|
||||
func SetupRoutes(r *gin.RouterGroup) {
|
||||
|
||||
@@ -30,7 +30,7 @@ func SetupRoutes(r *gin.RouterGroup) {
|
||||
func handleHomeRoute(c *gin.Context) {
|
||||
user := authenticator.GetUser(c)
|
||||
f := authenticator.GetFileSystem(c)
|
||||
r, err := f.ResourceByID(user.Home)
|
||||
r, err := f.ResourceByPath("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shroff/phylum/server/internal/core/user"
|
||||
)
|
||||
|
||||
const keyFileSystem = "filesystem"
|
||||
|
||||
func createBasicAuthHandler() func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
authSuccess := false
|
||||
if username, pass, ok := c.Request.BasicAuth(); ok {
|
||||
ctx := c.Request.Context()
|
||||
if u, err := user.ManagerFromContext(ctx).VerifyUserPassword(username, pass); err == nil {
|
||||
c.Set(keyFileSystem, u.OpenFileSystem(ctx))
|
||||
authSuccess = true
|
||||
} else if !errors.Is(err, user.ErrCredentialsInvalid) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if !authSuccess {
|
||||
c.Header("WWW-Authenticate", "Basic realm=\"Phylum WebDAV\"")
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
webdav "github.com/shroff/phylum/server/internal/api/webdav/impl"
|
||||
"github.com/shroff/phylum/server/internal/core/fs"
|
||||
"github.com/shroff/phylum/server/internal/core/user"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -20,29 +22,29 @@ func SetupHandler(r *gin.RouterGroup) {
|
||||
prefix: r.BasePath(),
|
||||
lockSystem: webdav.NewMemLS(),
|
||||
}
|
||||
r.Use(createBasicAuthHandler())
|
||||
r.Handle("OPTIONS", "*path", handler.HandleRequest)
|
||||
r.Handle("GET", "*path", handler.HandleRequest)
|
||||
r.Handle("PUT", "*path", handler.HandleRequest)
|
||||
r.Handle("HEAD", "*path", handler.HandleRequest)
|
||||
r.Handle("POST", "*path", handler.HandleRequest)
|
||||
r.Handle("DELETE", "*path", handler.HandleRequest)
|
||||
r.Handle("MOVE", "*path", handler.HandleRequest)
|
||||
r.Handle("COPY", "*path", handler.HandleRequest)
|
||||
r.Handle("MKCOL", "*path", handler.HandleRequest)
|
||||
r.Handle("PROPFIND", "*path", handler.HandleRequest)
|
||||
r.Handle("PROPPATCH", "*path", handler.HandleRequest)
|
||||
r.Handle("LOCK", "*path", handler.HandleRequest)
|
||||
r.Handle("UNLOCK", "*path", handler.HandleRequest)
|
||||
r.Any("/:user/*path", handler.HandleRequest)
|
||||
}
|
||||
|
||||
func (h *handler) HandleRequest(c *gin.Context) {
|
||||
val, ok := c.Get(keyFileSystem)
|
||||
if !ok {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
authSuccess := false
|
||||
var f fs.FileSystem
|
||||
if username, pass, ok := c.Request.BasicAuth(); ok {
|
||||
ctx := c.Request.Context()
|
||||
if u, err := user.ManagerFromContext(ctx).VerifyUserPassword(username, pass); err == nil {
|
||||
// TODO: consider allowing opening other users' home folders with permission checks
|
||||
if u.Username == c.Param("user") {
|
||||
f = u.OpenHomeFileSystem(ctx)
|
||||
authSuccess = true
|
||||
}
|
||||
} else if !errors.Is(err, user.ErrCredentialsInvalid) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if !authSuccess {
|
||||
c.Header("WWW-Authenticate", "Basic realm=\"Phylum WebDAV\"")
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
f := val.(fs.FileSystem)
|
||||
|
||||
webdavHandler := webdav.Handler{
|
||||
Prefix: h.prefix,
|
||||
|
||||
@@ -42,7 +42,7 @@ func UserFileSystem(cmd *cobra.Command) fs.FileSystem {
|
||||
if user == nil {
|
||||
f = fs.OpenOmniscient(context.Background())
|
||||
} else {
|
||||
f = user.OpenFileSystem(context.Background())
|
||||
f = user.OpenHomeFileSystem(context.Background())
|
||||
}
|
||||
}
|
||||
return f
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/command/common"
|
||||
"github.com/shroff/phylum/server/internal/core/db"
|
||||
"github.com/shroff/phylum/server/internal/core/fs"
|
||||
@@ -61,19 +63,6 @@ func setupUserAddCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
f := common.RootFileSystem()
|
||||
root := f.RootID()
|
||||
if rootPathOrUUID, err := cmd.Flags().GetString("chroot"); err != nil {
|
||||
fmt.Println("invalid value for flag 'chroot': " + err.Error())
|
||||
os.Exit(2)
|
||||
} else if rootPathOrUUID != "" {
|
||||
if r, err := f.ResourceByPathOrUUID(rootPathOrUUID); err != nil {
|
||||
fmt.Println("invalid value for flag 'chroot': " + err.Error())
|
||||
os.Exit(1)
|
||||
} else {
|
||||
root = r.ID()
|
||||
}
|
||||
}
|
||||
|
||||
noCreateHome, _ := cmd.Flags().GetBool("no-create-home")
|
||||
homePath := ""
|
||||
if !noCreateHome {
|
||||
@@ -89,22 +78,33 @@ func setupUserAddCommand() *cobra.Command {
|
||||
err = db.Get(context.Background()).RunInTx(func(db db.Handler) error {
|
||||
userManager := user.ManagerFromDB(db)
|
||||
var u user.User
|
||||
if user, err := userManager.CreateUser(username, displayName, password, root); err != nil {
|
||||
var home fs.Resource
|
||||
if homePath != "" {
|
||||
f = common.RootFileSystem().WithDb(db)
|
||||
var err error
|
||||
home, err = f.CreateResourceByPath(homePath, true, true, fs.ResourceBindConflictResolutionEnsure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var homeID pgtype.UUID
|
||||
if home.ID() != uuid.Nil {
|
||||
homeID = pgtype.UUID{
|
||||
Bytes: home.ID(),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
if user, err := userManager.CreateUser(username, displayName, password, homeID); err != nil {
|
||||
return err
|
||||
} else {
|
||||
u = user
|
||||
}
|
||||
|
||||
if homePath != "" {
|
||||
f = f.WithDb(db)
|
||||
home, err := f.CreateResourceByPath(homePath, true, true, fs.ResourceBindConflictResolutionEnsure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if homeID.Valid {
|
||||
if _, err := home.UpdatePermissions(u.Username, fs.PermissionRead|fs.PermissionWrite|fs.PermissionShare); err != nil {
|
||||
return err
|
||||
}
|
||||
return userManager.UpdateUserHome(u, home.ID())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/command/common"
|
||||
"github.com/shroff/phylum/server/internal/core/db"
|
||||
"github.com/shroff/phylum/server/internal/core/user"
|
||||
@@ -28,7 +28,7 @@ func setupUserModCommand() *cobra.Command {
|
||||
displayName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
rootFS := common.RootFileSystem()
|
||||
homeID := uuid.Nil
|
||||
var homeID pgtype.UUID
|
||||
if pathOrUUID, err := cmd.Flags().GetString("home"); err != nil {
|
||||
fmt.Println("invalid value for flag 'home': " + err.Error())
|
||||
os.Exit(1)
|
||||
@@ -37,7 +37,10 @@ func setupUserModCommand() *cobra.Command {
|
||||
fmt.Println("invalid value for flag 'home': " + err.Error())
|
||||
os.Exit(1)
|
||||
} else {
|
||||
homeID = r.ID()
|
||||
homeID = pgtype.UUID{
|
||||
Bytes: r.ID(),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +51,7 @@ func setupUserModCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if homeID != uuid.Nil {
|
||||
if homeID.Valid {
|
||||
if err := m.UpdateUserHome(u, homeID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ CREATE TABLE users(
|
||||
deleted TIMESTAMP,
|
||||
display_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
home uuid NOT NULL REFERENCES resources(id),
|
||||
home uuid REFERENCES resources(id),
|
||||
permissions INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/core/db"
|
||||
"github.com/shroff/phylum/server/internal/core/storage"
|
||||
"github.com/shroff/phylum/server/internal/core/util/crypt"
|
||||
@@ -53,7 +54,7 @@ type FileSystem interface {
|
||||
Search(query string, includeDeleted bool) ([]Resource, error)
|
||||
|
||||
// shared.go
|
||||
SharedResources(excluded uuid.UUID) ([]Resource, error)
|
||||
SharedResources(excluded pgtype.UUID) ([]Resource, error)
|
||||
|
||||
// trash.go
|
||||
TrashList(cursor string, n uint) ([]Resource, string, error)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func (f filesystem) SharedResources(excluded uuid.UUID) ([]Resource, error) {
|
||||
func (f filesystem) SharedResources(excluded pgtype.UUID) ([]Resource, error) {
|
||||
const q = `SELECT r.*, (SELECT ` + publinkFieldsQuery + ` FROM publinks l WHERE l.root = r.id) as links FROM resources r
|
||||
WHERE grants ? $1::TEXT
|
||||
AND r.id != $2::UUID
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/core/errors"
|
||||
@@ -58,7 +57,7 @@ func (m manager) ReadAccessToken(accessToken string) (User, error) {
|
||||
var username string
|
||||
var displayName string
|
||||
var permissions int32
|
||||
var home uuid.UUID
|
||||
var home pgtype.UUID
|
||||
if err := row.Scan(&expires, &username, &displayName, &permissions, &home); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
err = ErrCredentialsInvalid
|
||||
|
||||
@@ -4,15 +4,15 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/core/errors"
|
||||
"github.com/shroff/phylum/server/internal/core/util/crypt"
|
||||
)
|
||||
|
||||
var ErrUserExists = errors.NewError(http.StatusNotFound, "user_already_exists", "user already exists")
|
||||
|
||||
func (m manager) CreateUser(username, displayName, password string, home uuid.UUID) (User, error) {
|
||||
func (m manager) CreateUser(username, displayName, password string, home pgtype.UUID) (User, error) {
|
||||
const q = ` INSERT INTO users(username, display_name, password_hash, home)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING username, display_name, password_hash, home, permissions`
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/core/util/crypt"
|
||||
)
|
||||
|
||||
func (m manager) UpdateUserHome(user User, home uuid.UUID) error {
|
||||
func (m manager) UpdateUserHome(user User, home pgtype.UUID) error {
|
||||
const q = "UPDATE users SET home = $2::UUID, modified = NOW() WHERE username = $1::TEXT"
|
||||
if _, err := m.db.Exec(q, user.Username, home); err != nil {
|
||||
return err
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/core/fs"
|
||||
)
|
||||
|
||||
@@ -17,7 +18,7 @@ type BasicUser struct {
|
||||
type User struct {
|
||||
BasicUser
|
||||
PasswordHash string
|
||||
Home uuid.UUID
|
||||
Home pgtype.UUID
|
||||
Permissions int32
|
||||
}
|
||||
|
||||
@@ -42,20 +43,23 @@ func scanUser(row pgx.CollectableRow) (User, error) {
|
||||
return u, err
|
||||
}
|
||||
|
||||
func (u User) OpenFileSystem(ctx context.Context) fs.FileSystem {
|
||||
return fs.Open(ctx, u.Username, u.Home, u.Permissions&PermissionAllFiles != 0)
|
||||
func (u User) OpenHomeFileSystem(ctx context.Context) fs.FileSystem {
|
||||
if !u.Home.Valid {
|
||||
return nil
|
||||
}
|
||||
return fs.Open(ctx, u.Username, u.Home.Bytes, u.Permissions&PermissionAllFiles != 0)
|
||||
}
|
||||
|
||||
type Manager interface {
|
||||
// create.go
|
||||
CreateUser(username, displayName, password string, home uuid.UUID) (User, error)
|
||||
CreateUser(username, displayName, password string, home pgtype.UUID) (User, error)
|
||||
|
||||
// select.go
|
||||
ListUsers(since *time.Time) ([]BasicUser, error)
|
||||
UserByEmail(email string) (User, error)
|
||||
|
||||
// update.go
|
||||
UpdateUserHome(user User, home uuid.UUID) error
|
||||
UpdateUserHome(user User, home pgtype.UUID) error
|
||||
UpdateUserDisplayName(user User, displayName string) error
|
||||
UpdateUserPassword(user User, password string) error
|
||||
|
||||
|
||||
Reference in New Issue
Block a user