package cmd import ( "bytes" "fmt" "text/template" "git.maurice.fr/thomas/mailout/pkg/database" "git.maurice.fr/thomas/mailout/pkg/models" "github.com/pterm/pterm" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "golang.org/x/crypto/bcrypt" ) var ( flagUserActive bool flagUserHome string flagUserQuota int64 flagUserPassword string flagUserGID int flagUserUID int ) var UserCmd = &cobra.Command{ Use: "user", Short: "manages users", } var UserAddCmd = &cobra.Command{ Use: "add [user_address] [password]", Short: "adds a user", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } username, domain, err := splitUser(args[0]) if err != nil { logrus.WithError(err).Fatal("could not parse user") } qRes := make([]models.User, 0) var user models.User userExists := false logger := logrus.WithField("user", args[0]) err = db.Where(&models.User{Domain: domain, Username: username}).Find(&qRes).Error if err != nil { logger.WithError(err).Fatal("could not check user's existence") } if len(qRes) == 0 { userExists = false } else { logger.Warning("user already exists, it's password will be updated") user = qRes[0] userExists = true } passwordHash, err := bcrypt.GenerateFromPassword([]byte(args[1]), bcrypt.DefaultCost) if err != nil { logger.WithError(err).Fatal("could not compute user password hash") } if userExists { err = db.Model(&models.User{}).Where(&user).Update("password", fmt.Sprintf("{BLF-CRYPT}%s", string(passwordHash))).Error if err != nil { logger.WithError(err).Fatal("could not update user password") } logger.Infof("updated user %s", user.ID) return } user = models.User{ Username: username, Domain: domain, Active: flagUserActive, Password: fmt.Sprintf("{BLF-CRYPT}%s", string(passwordHash)), } if flagUserHome == "" { flagUserHome = cfg.Defaults.HomeTemplate } tmpl, err := template.New("").Parse(flagUserHome) if err != nil { logrus.WithError(err).Fatal("could not parse the default home template") } buf := bytes.NewBufferString("") err = tmpl.Execute(buf, user) if err != nil { logrus.WithError(err).Fatal("could not render template") } user.Home = buf.String() err = db.Save(&user).Error if err != nil { logger.WithError(err).Fatal("could not create user") } logger.Infof("created user %s", user.ID) }, } var UserListCmd = &cobra.Command{ Use: "list", Short: "list users", Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } qRes := make([]models.User, 0) err = db.Find(&qRes).Error if err != nil { logrus.WithError(err).Fatal("could not list users") } tData := pterm.TableData{ {"id", "username", "domain", "active", "uid", "gid", "home"}, } for _, u := range qRes { tData = append(tData, []string{u.ID.String(), u.Username, u.Domain, fmt.Sprintf("%v", u.Active), fmt.Sprintf("%d", u.UID), fmt.Sprintf("%d", u.GID), u.Home}) } pterm.DefaultTable.WithHasHeader().WithData(tData).Render() }, } var UserDeleteCmd = &cobra.Command{ Use: "delete", Short: "delete users", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } userQuery, err := buildUserQuery(args[0]) if err != nil { logrus.WithError(err).Fatal("unable to determine user") } err = db.Where(&userQuery).Delete(&models.User{}).Error if err != nil { logrus.WithError(err).Fatal("could not delete user") } logrus.Infof("deleted user %s if it existed", args[0]) }, } var UserActivateCmd = &cobra.Command{ Use: "activate", Short: "activates a user", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } userQuery, err := buildUserQuery(args[0]) if err != nil { logrus.WithError(err).Fatal("unable to determine user") } err = db.Model(&models.User{}).Where(&userQuery).Update("active", true).Error if err != nil { logrus.WithError(err).Fatal("could not activate user") } logrus.Infof("activated user %s", args[0]) }, } var UserDeactivateCmd = &cobra.Command{ Use: "deactivate", Short: "deactivates a user", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } userQuery, err := buildUserQuery(args[0]) if err != nil { logrus.WithError(err).Fatal("unable to determine user") } err = db.Model(&models.User{}).Where(&userQuery).Update("active", false).Error if err != nil { logrus.WithError(err).Fatal("could not deactivate user") } logrus.Infof("deactivated user %s", args[0]) }, } var UserEditCmd = &cobra.Command{ Use: "edit ", Short: "edites a user", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { db, err := database.NewDB(cfg) if err != nil { logrus.WithError(err).Fatal("could not connect to the database") } userQuery, err := buildUserQuery(args[0]) if err != nil { logrus.WithError(err).Fatal("unable to determine user") } qRes := make([]models.User, 0) err = db.Model(&models.User{}).Find(&qRes, userQuery).Error if err != nil { logrus.WithError(err).Fatal("could not preload user") } if len(qRes) == 0 { logrus.Fatal("no such user") } user := qRes[0] if flagUserHome != "" { tmpl, err := template.New("").Parse(flagUserHome) if err != nil { logrus.WithError(err).Fatal("could not parse the default home template") } buf := bytes.NewBufferString("") err = tmpl.Execute(buf, user) if err != nil { logrus.WithError(err).Fatal("could not render home template") } user.Home = buf.String() } if flagUserPassword != "" { passwordHash, err := bcrypt.GenerateFromPassword([]byte(flagUserPassword), bcrypt.DefaultCost) if err != nil { logrus.WithError(err).Fatal("could not compute user password hash") } user.Password = fmt.Sprintf("{BLF-CRYPT}%s", string(passwordHash)) } if flagUserGID != -1 { user.GID = flagUserGID } if flagUserUID != -1 { user.UID = flagUserUID } err = db.Save(&user).Error if err != nil { logrus.WithError(err).Fatal("could not update user") } logrus.Infof("edited user %s", args[0]) }, } func InitUserCmd() { UserCmd.AddCommand(UserAddCmd) UserCmd.AddCommand(UserEditCmd) UserCmd.AddCommand(UserListCmd) UserCmd.AddCommand(UserDeleteCmd) UserCmd.AddCommand(UserActivateCmd) UserCmd.AddCommand(UserDeactivateCmd) UserAddCmd.PersistentFlags().BoolVarP(&flagUserActive, "active", "a", true, "whether or not the created user is active") UserAddCmd.PersistentFlags().StringVarP(&flagUserHome, "home", "", "", "template to use for user's home directories") UserEditCmd.PersistentFlags().StringVarP(&flagUserPassword, "password", "p", "", "User password") UserEditCmd.PersistentFlags().StringVarP(&flagUserHome, "home", "", "", "home (user's mailbox)") UserEditCmd.PersistentFlags().Int64VarP(&flagUserQuota, "quota", "q", -1, "Quota in bytes for the user") UserEditCmd.PersistentFlags().IntVarP(&flagUserUID, "uid", "u", -1, "user's uid") UserEditCmd.PersistentFlags().IntVarP(&flagUserGID, "gid", "g", -1, "user's gid") }