From f6a42f62e08b35dd080ea9d19c1ceb8ed1e5afc7 Mon Sep 17 00:00:00 2001 From: Abhishek Shroff Date: Wed, 7 Aug 2024 16:43:12 +0530 Subject: [PATCH] Automatically create user home folder --- server/internal/app/app.go | 20 +++- server/internal/app/core/filesystem.go | 7 +- server/internal/app/users.go | 13 ++- server/internal/command/appcmd/resource.go | 2 +- .../internal/db/migrations/data/001_users.sql | 10 +- .../db/migrations/data/002_resources.sql | 2 +- .../db/migrations/data/006_user_home.sql | 5 + server/internal/db/models.go | 3 + server/internal/db/users.sql.go | 96 +++++++++++++++++-- server/sql/queries/users.sql | 31 +++++- 10 files changed, 163 insertions(+), 26 deletions(-) create mode 100644 server/internal/db/migrations/data/006_user_home.sql diff --git a/server/internal/app/app.go b/server/internal/app/app.go index 970679af..c59c5594 100644 --- a/server/internal/app/app.go +++ b/server/internal/app/app.go @@ -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 } diff --git a/server/internal/app/core/filesystem.go b/server/internal/app/core/filesystem.go index 00ce55ed..40db8861 100644 --- a/server/internal/app/core/filesystem.go +++ b/server/internal/app/core/filesystem.go @@ -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 } diff --git a/server/internal/app/users.go b/server/internal/app/users.go index 1e593f63..8599f2d0 100644 --- a/server/internal/app/users.go +++ b/server/internal/app/users.go @@ -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) { diff --git a/server/internal/command/appcmd/resource.go b/server/internal/command/appcmd/resource.go index c34dc962..6f5c93d7 100644 --- a/server/internal/command/appcmd/resource.go +++ b/server/internal/command/appcmd/resource.go @@ -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)") } diff --git a/server/internal/db/migrations/data/001_users.sql b/server/internal/db/migrations/data/001_users.sql index 1328157b..d42d1723 100644 --- a/server/internal/db/migrations/data/001_users.sql +++ b/server/internal/db/migrations/data/001_users.sql @@ -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' ); diff --git a/server/internal/db/migrations/data/002_resources.sql b/server/internal/db/migrations/data/002_resources.sql index d4eb0329..5f9a696b 100644 --- a/server/internal/db/migrations/data/002_resources.sql +++ b/server/internal/db/migrations/data/002_resources.sql @@ -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; diff --git a/server/internal/db/migrations/data/006_user_home.sql b/server/internal/db/migrations/data/006_user_home.sql new file mode 100644 index 00000000..a41e4ec3 --- /dev/null +++ b/server/internal/db/migrations/data/006_user_home.sql @@ -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; \ No newline at end of file diff --git a/server/internal/db/models.go b/server/internal/db/models.go index ae4536ca..4da15215 100644 --- a/server/internal/db/models.go +++ b/server/internal/db/models.go @@ -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 } diff --git a/server/internal/db/users.sql.go b/server/internal/db/users.sql.go index 09689686..62f6133f 100644 --- a/server/internal/db/users.sql.go +++ b/server/internal/db/users.sql.go @@ -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 } diff --git a/server/sql/queries/users.sql b/server/sql/queries/users.sql index 1d26e7dc..138163a4 100644 --- a/server/sql/queries/users.sql +++ b/server/sql/queries/users.sql @@ -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; \ No newline at end of file +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; \ No newline at end of file