mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-02-19 03:58:46 -06:00
Automatically create user home folder
This commit is contained in:
@@ -3,15 +3,17 @@ package app
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shroff/phylum/server/internal/app/core"
|
||||
"github.com/shroff/phylum/server/internal/db"
|
||||
"github.com/shroff/phylum/server/internal/storage"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Debug bool
|
||||
db *db.DbHandler
|
||||
cs storage.Storage
|
||||
Debug bool
|
||||
adminfs core.FileSystem
|
||||
db *db.DbHandler
|
||||
cs storage.Storage
|
||||
}
|
||||
|
||||
var Default *App
|
||||
@@ -23,6 +25,18 @@ func Initialize(db *db.DbHandler, cs storage.Storage, debug bool) error {
|
||||
cs: cs,
|
||||
}
|
||||
|
||||
if root, err := Default.UserByUsername(context.Background(), "phylum"); err != nil {
|
||||
return err
|
||||
} else if fs, err := Default.OpenFileSystem(context.Background(), root.ID()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
Default.adminfs = fs
|
||||
if _, err := fs.ResourceByPath("/home"); err != nil {
|
||||
_, err := fs.CreateMemberResource(fs.Root(), uuid.New(), "home", true)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
type FileSystem interface {
|
||||
OpenWithRoot(Resource) FileSystem
|
||||
Root() Resource
|
||||
ResourceByPath(path string) (Resource, error)
|
||||
ResourceByID(id uuid.UUID) (Resource, error)
|
||||
OpenRead(r Resource, start, length int64) (io.ReadCloser, error)
|
||||
@@ -68,6 +69,10 @@ func OpenFileSystem(db *db.DbHandler, ctx context.Context, cs storage.Storage, r
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f filesystem) Root() Resource {
|
||||
return f.root
|
||||
}
|
||||
|
||||
func (f filesystem) OpenWithRoot(root Resource) FileSystem {
|
||||
return filesystem{
|
||||
db: f.db,
|
||||
@@ -110,7 +115,7 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
|
||||
|
||||
func (f filesystem) ResourceByID(id uuid.UUID) (Resource, error) {
|
||||
res, err := f.db.Queries().ResourceByID(f.ctx, db.ResourceByIDParams{Root: f.root.ID(), Permission: f.root.Permission(), ResourceID: id, UserID: f.user})
|
||||
// TODO: check found
|
||||
// TODO: verify found
|
||||
if err == pgx.ErrNoRows || !res.Found || res.Permission == 0 {
|
||||
err = fs.ErrNotExist
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/shroff/phylum/server/internal/cryptutil"
|
||||
"github.com/shroff/phylum/server/internal/db"
|
||||
@@ -30,10 +31,18 @@ func (u user) DisplayName() string { return u.displayName }
|
||||
func (a App) CreateUser(ctx context.Context, username, displayName, password string) error {
|
||||
if hash, err := cryptutil.GenerateArgon2EncodedHash(password, cryptutil.DefaultArgon2Params()); err != nil {
|
||||
return err
|
||||
} else if err = a.db.Queries().CreateUser(ctx, db.CreateUserParams{Username: username, DisplayName: displayName, PasswordHash: hash}); err != nil {
|
||||
} else if u, err := a.db.Queries().CreateUser(ctx, db.CreateUserParams{Username: username, DisplayName: displayName, PasswordHash: hash}); err != nil {
|
||||
return err
|
||||
} else if home, err := a.adminfs.ResourceByPath("/home"); err != nil {
|
||||
return err
|
||||
} else if home, err := a.adminfs.CreateMemberResource(home, uuid.New(), username, true); err != nil {
|
||||
return err
|
||||
} else if err := a.adminfs.UpdateOwner(home, u.ID); err != nil {
|
||||
return err
|
||||
} else {
|
||||
homeID := home.ID()
|
||||
return a.db.Queries().UpdateUserHome(ctx, db.UpdateUserHomeParams{ID: u.ID, Home: &homeID})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a App) ListUsers(ctx context.Context) ([]User, error) {
|
||||
|
||||
@@ -275,6 +275,6 @@ func readUserIDFromFlagsreadUsername(cmd *cobra.Command) {
|
||||
func setupUsernameFlags(cmd *cobra.Command) {
|
||||
flags := cmd.Flags()
|
||||
flags.Int32P("user", "u", 0, "Specify User ID for resource operations (cannot be used with -U)")
|
||||
flags.StringP("username", "U", "root", "Specify Username for resource operations (cannot be used with -u)")
|
||||
flags.StringP("username", "U", "phylum", "Specify Username for resource operations (cannot be used with -u)")
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
CREATE TABLE users(
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
created TIMESTAMP NOT NULL,
|
||||
modified TIMESTAMP NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
deleted TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO users(username, display_name, password_hash) VALUES(
|
||||
'root',
|
||||
'Admin',
|
||||
INSERT INTO users(username, created, modified, display_name, password_hash) VALUES(
|
||||
'phylum',
|
||||
NOW(),
|
||||
NOW(),
|
||||
'Phylum',
|
||||
'CANNOT_LOG_IN'
|
||||
);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ SELECT '00000000-0000-0000-0000-000000000000',
|
||||
NULL,
|
||||
0,
|
||||
''
|
||||
FROM users where username = 'root');
|
||||
FROM users where username = 'phylum');
|
||||
|
||||
CREATE UNIQUE INDEX unique_member_resource_name ON resources(parent, name) WHERE deleted IS NULL;
|
||||
|
||||
|
||||
5
server/internal/db/migrations/data/006_user_home.sql
Normal file
5
server/internal/db/migrations/data/006_user_home.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE users ADD home uuid REFERENCES resources(id) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
|
||||
---- create above / drop below ----
|
||||
|
||||
ALTER TABLE users DROP COLUMN home;
|
||||
@@ -44,7 +44,10 @@ type StorageBackend struct {
|
||||
type User struct {
|
||||
ID int32
|
||||
Username string
|
||||
Created pgtype.Timestamp
|
||||
Modified pgtype.Timestamp
|
||||
DisplayName string
|
||||
PasswordHash string
|
||||
Deleted pgtype.Timestamp
|
||||
Home *uuid.UUID
|
||||
}
|
||||
|
||||
@@ -7,14 +7,16 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :exec
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users(
|
||||
username, display_name, password_hash
|
||||
username, created, modified, display_name, password_hash
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
)
|
||||
$1, NOW(), NOW(), $2, $3
|
||||
) RETURNING id, username, created, modified, display_name, password_hash, deleted, home
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
@@ -23,13 +25,24 @@ type CreateUserParams struct {
|
||||
PasswordHash string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error {
|
||||
_, err := q.db.Exec(ctx, createUser, arg.Username, arg.DisplayName, arg.PasswordHash)
|
||||
return err
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, createUser, arg.Username, arg.DisplayName, arg.PasswordHash)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.DisplayName,
|
||||
&i.PasswordHash,
|
||||
&i.Deleted,
|
||||
&i.Home,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listUsers = `-- name: ListUsers :many
|
||||
SELECT id, username, display_name, password_hash, deleted from users WHERE deleted IS NULL
|
||||
SELECT id, username, created, modified, display_name, password_hash, deleted, home from users WHERE deleted IS NULL
|
||||
`
|
||||
|
||||
func (q *Queries) ListUsers(ctx context.Context) ([]User, error) {
|
||||
@@ -44,9 +57,12 @@ func (q *Queries) ListUsers(ctx context.Context) ([]User, error) {
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.DisplayName,
|
||||
&i.PasswordHash,
|
||||
&i.Deleted,
|
||||
&i.Home,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -58,8 +74,62 @@ func (q *Queries) ListUsers(ctx context.Context) ([]User, error) {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateUserDisplayName = `-- name: UpdateUserDisplayName :exec
|
||||
UPDATE users
|
||||
SET
|
||||
display_name = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type UpdateUserDisplayNameParams struct {
|
||||
DisplayName string
|
||||
ID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserDisplayName(ctx context.Context, arg UpdateUserDisplayNameParams) error {
|
||||
_, err := q.db.Exec(ctx, updateUserDisplayName, arg.DisplayName, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserHome = `-- name: UpdateUserHome :exec
|
||||
UPDATE users
|
||||
SET
|
||||
home = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type UpdateUserHomeParams struct {
|
||||
Home *uuid.UUID
|
||||
ID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserHome(ctx context.Context, arg UpdateUserHomeParams) error {
|
||||
_, err := q.db.Exec(ctx, updateUserHome, arg.Home, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserPasswordHash = `-- name: UpdateUserPasswordHash :exec
|
||||
UPDATE users
|
||||
SET
|
||||
password_hash = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type UpdateUserPasswordHashParams struct {
|
||||
PasswordHash string
|
||||
ID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserPasswordHash(ctx context.Context, arg UpdateUserPasswordHashParams) error {
|
||||
_, err := q.db.Exec(ctx, updateUserPasswordHash, arg.PasswordHash, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const userByID = `-- name: UserByID :one
|
||||
SELECT id, username, display_name, password_hash, deleted from users WHERE id = $1
|
||||
SELECT id, username, created, modified, display_name, password_hash, deleted, home from users WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) UserByID(ctx context.Context, id int32) (User, error) {
|
||||
@@ -68,15 +138,18 @@ func (q *Queries) UserByID(ctx context.Context, id int32) (User, error) {
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.DisplayName,
|
||||
&i.PasswordHash,
|
||||
&i.Deleted,
|
||||
&i.Home,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const userByUsername = `-- name: UserByUsername :one
|
||||
SELECT id, username, display_name, password_hash, deleted from users WHERE username = $1
|
||||
SELECT id, username, created, modified, display_name, password_hash, deleted, home from users WHERE username = $1
|
||||
`
|
||||
|
||||
func (q *Queries) UserByUsername(ctx context.Context, username string) (User, error) {
|
||||
@@ -85,9 +158,12 @@ func (q *Queries) UserByUsername(ctx context.Context, username string) (User, er
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.DisplayName,
|
||||
&i.PasswordHash,
|
||||
&i.Deleted,
|
||||
&i.Home,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
-- name: CreateUser :exec
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users(
|
||||
username, display_name, password_hash
|
||||
username, created, modified, display_name, password_hash
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
);
|
||||
$1, NOW(), NOW(), $2, $3
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UserByUsername :one
|
||||
SELECT * from users WHERE username = $1;
|
||||
@@ -12,4 +12,25 @@ SELECT * from users WHERE username = $1;
|
||||
SELECT * from users WHERE id = $1;
|
||||
|
||||
-- name: ListUsers :many
|
||||
SELECT * from users WHERE deleted IS NULL;
|
||||
SELECT * from users WHERE deleted IS NULL;
|
||||
|
||||
-- name: UpdateUserDisplayName :exec
|
||||
UPDATE users
|
||||
SET
|
||||
display_name = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: UpdateUserPasswordHash :exec
|
||||
UPDATE users
|
||||
SET
|
||||
password_hash = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: UpdateUserHome :exec
|
||||
UPDATE users
|
||||
SET
|
||||
home = $1,
|
||||
modified = NOW()
|
||||
WHERE id = $2;
|
||||
Reference in New Issue
Block a user