From f1eaf97b37bfc3978bb775530e891ad4aab656df Mon Sep 17 00:00:00 2001 From: Abhishek Shroff Date: Thu, 22 May 2025 11:39:43 +0530 Subject: [PATCH] [client] Send invite email on user creation --- server/internal/command/user/cmd.go | 2 +- .../command/user/{add.go => invite.go} | 70 +++++++------------ server/internal/core/user/create.go | 7 +- server/internal/core/user/user.go | 2 +- 4 files changed, 30 insertions(+), 51 deletions(-) rename server/internal/command/user/{add.go => invite.go} (55%) diff --git a/server/internal/command/user/cmd.go b/server/internal/command/user/cmd.go index 0678047a..915c410e 100644 --- a/server/internal/command/user/cmd.go +++ b/server/internal/command/user/cmd.go @@ -15,7 +15,7 @@ func SetupCommand() *cobra.Command { Short: "User Management", } cmd.AddCommand([]*cobra.Command{ - setupUserAddCommand(), + setupInviteCommand(), setupUserModCommand(), setupUserListCommand(), setupUserPasswdCommand(), diff --git a/server/internal/command/user/add.go b/server/internal/command/user/invite.go similarity index 55% rename from server/internal/command/user/add.go rename to server/internal/command/user/invite.go index 3eed3be8..f472d76c 100644 --- a/server/internal/command/user/add.go +++ b/server/internal/command/user/invite.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "strings" - "syscall" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" @@ -13,53 +12,24 @@ import ( "github.com/shroff/phylum/server/internal/core/db" "github.com/shroff/phylum/server/internal/core/fs" "github.com/shroff/phylum/server/internal/core/user" + "github.com/shroff/phylum/server/internal/mail" "github.com/spf13/cobra" - "golang.org/x/term" ) -func setupUserAddCommand() *cobra.Command { +func setupInviteCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "add email", - Short: "Add User", + Use: "invite email", + Short: "Invite User", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { email := args[0] - displayName := email + displayName := "" if n, err := cmd.Flags().GetString("name"); err != nil { fmt.Println("invalid value for flag 'name': " + err.Error()) os.Exit(1) } else if n != "" { - displayName = email - } - - password, err := cmd.Flags().GetString("password") - if err != nil { - fmt.Println("invalid password: " + err.Error()) - os.Exit(1) - } else if password == "" { - os.Stdout.WriteString("Password: ") - bytes, err := term.ReadPassword(syscall.Stdin) - os.Stdout.WriteString("\n") - if err != nil { - fmt.Println("unable to read password: " + err.Error()) - os.Exit(1) - } - password = string(bytes) - - os.Stdout.WriteString("Confirm Password: ") - bytes, err = term.ReadPassword(syscall.Stdin) - os.Stdout.WriteString("\n") - if err != nil { - fmt.Println("unable to read password: " + err.Error()) - os.Exit(1) - } - passwordConf := string(bytes) - - if password != passwordConf { - fmt.Println("password does not match confirmation") - os.Exit(1) - } + displayName = n } f := common.RootFileSystem() @@ -70,9 +40,9 @@ func setupUserAddCommand() *cobra.Command { homePath = strings.TrimRight(basePath, "/") + "/" + email } - err = db.Get(context.Background()).RunInTx(func(db db.Handler) error { + var u user.User + err := db.Get(context.Background()).RunInTx(func(db db.Handler) error { userManager := user.ManagerFromDB(db) - var userID int32 var home fs.Resource if homePath != "" { f = f.WithDb(db) @@ -90,14 +60,14 @@ func setupUserAddCommand() *cobra.Command { Valid: true, } } - if user, err := userManager.CreateUser(email, displayName, password, homeID); err != nil { + if user, err := userManager.CreateUser(email, displayName, homeID); err != nil { return err } else { - userID = user.ID + u = user } if homeID.Valid { - if _, err := home.UpdatePermissions(userID, fs.PermissionRead|fs.PermissionWrite|fs.PermissionShare); err != nil { + if _, err := home.UpdatePermissions(u.ID, fs.PermissionRead|fs.PermissionWrite|fs.PermissionShare); err != nil { return err } } @@ -105,14 +75,26 @@ func setupUserAddCommand() *cobra.Command { return nil }) if err != nil { - fmt.Println("could not add user: " + err.Error()) - os.Exit(3) + fmt.Println("could not create user: " + err.Error()) + os.Exit(1) + } + if sendInvite(u) != nil { + fmt.Println("could not send email: " + err.Error()) + os.Exit(1) } }, } cmd.Flags().StringP("name", "n", "", "Full Name") - cmd.Flags().StringP("password", "p", "", "Password") cmd.Flags().StringP("base-dir", "b", "/home", "Base directory for home") cmd.Flags().BoolP("no-create-home", "M", false, "Do not make home directory") return cmd } + +func sendInvite(user user.User) error { + return mail.Send(mail.Message{ + ToName: user.DisplayName, + ToEmail: user.Email, + Subject: "Create your account", + Body: "Create your account", + }) +} diff --git a/server/internal/core/user/create.go b/server/internal/core/user/create.go index 43d07c3c..ae70a12b 100644 --- a/server/internal/core/user/create.go +++ b/server/internal/core/user/create.go @@ -7,20 +7,17 @@ import ( "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 ErrInvalidEmailAddress = errors.NewError(http.StatusBadRequest, "invalid_email_address", "Invalid email address") var ErrUserExists = errors.NewError(http.StatusBadRequest, "user_already_exists", "User already exists") -func (m manager) CreateUser(email, displayName, password string, home pgtype.UUID) (User, error) { +func (m manager) CreateUser(email, displayName string, home pgtype.UUID) (User, error) { const q = ` INSERT INTO users(email, display_name, password_hash, home) VALUES ($1, $2, $3, $4) RETURNING id, email, display_name, home, permissions` - if hash, err := crypt.GenerateArgon2EncodedHash(password); err != nil { - return User{}, err - } else if rows, err := m.db.Query(q, strings.ToLower(email), displayName, hash, home); err != nil { + if rows, err := m.db.Query(q, strings.ToLower(email), displayName, "", home); err != nil { return User{}, err } else if user, err := pgx.CollectExactlyOneRow(rows, scanUser); err != nil { if strings.Contains(err.Error(), "valid_email") { diff --git a/server/internal/core/user/user.go b/server/internal/core/user/user.go index e2ba0167..442a9c14 100644 --- a/server/internal/core/user/user.go +++ b/server/internal/core/user/user.go @@ -36,7 +36,7 @@ func (u User) OpenFileSystem(ctx context.Context) fs.FileSystem { type Manager interface { // create.go - CreateUser(email, displayName, password string, home pgtype.UUID) (User, error) + CreateUser(email, displayName string, home pgtype.UUID) (User, error) // select.go ListUsers(since *time.Time) ([]User, error)