diff --git a/cmd/user/create.go b/cmd/user/create.go index 438505c5cb0ad0a862bba4ed6d9bff8f10b699a2..03dd0ba1aeee370f106b6b171b2fd063b03d0717 100644 --- a/cmd/user/create.go +++ b/cmd/user/create.go @@ -25,24 +25,6 @@ const ( MAX_UID = 6000 ) -type userOpts struct { - GRR string - GID string - UID string - Resp string - Name string - Nobkp string - Ltype string - Shell string - Passwd string - Webdir string - Status string - Course string - Expiry string - Homedir string - Confirm bool -} - var CreateUserCmd = &cobra.Command{ Use: "create", Short: "Create new user", @@ -122,6 +104,7 @@ func createUserFunc(cmd *cobra.Command, args []string) error { // creates and validates user inputs into the User model func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) { var u model.User + var opts model.Opts users, err := getUsers() if err != nil { return u, false, err @@ -132,7 +115,7 @@ func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) { return u, false, err } - opts, err := retrieveOpts(cmd) + err = opts.RetrieveOpts(cmd) if err != nil { return u, false, err } @@ -582,7 +565,7 @@ Note: To search for the account use "useradm user show -l %s"`, login) return nil } -func validateInputs(opts userOpts) error { +func validateInputs(opts model.Opts) error { var err error err = validateGID(opts.GID) @@ -606,66 +589,6 @@ func validateInputs(opts userOpts) error { return nil } -func getFlagString(cmd *cobra.Command, flag string) (string, error) { - flagValue, err := cmd.Flags().GetString(flag) - if err != nil { - return "", fmt.Errorf("Failed to get flag %q: %w", flag, err) - } - return flagValue, nil -} - -func retrieveOpts(cmd *cobra.Command) (userOpts, error) { - var opts userOpts - var err error - - opts.GRR, err = getFlagString(cmd, "grr") - if err != nil { return opts, err } - - opts.Resp, err = getFlagString(cmd, "resp") - if err != nil { return opts, err } - - opts.Name, err = getFlagString(cmd, "name") - if err != nil { return opts, err } - - opts.GID, err = getFlagString(cmd, "group") - if err != nil { return opts, err } - - opts.UID, err = getFlagString(cmd, "login") - if err != nil { return opts, err } - - opts.Ltype, err = getFlagString(cmd, "type") - if err != nil { return opts, err } - - opts.Shell, err = getFlagString(cmd, "shell") - if err != nil { return opts, err } - - opts.Webdir, err = getFlagString(cmd, "path") - if err != nil { return opts, err } - - opts.Nobkp, err = getFlagString(cmd, "nobkp") - if err != nil { return opts, err } - - opts.Status, err = getFlagString(cmd, "status") - if err != nil { return opts, err } - - opts.Course, err = getFlagString(cmd, "course") - if err != nil { return opts, err } - - opts.Expiry, err = getFlagString(cmd, "expiry") - if err != nil { return opts, err } - - opts.Passwd, err = getFlagString(cmd, "passwd") - if err != nil { return opts, err } - - opts.Homedir, err = getFlagString(cmd, "homedir") - if err != nil { return opts, err } - - opts.Confirm, err = cmd.Flags().GetBool("confirm") - if err != nil { return opts, err } - - return opts, nil -} - func isValidDate(arr []string) bool { if len(arr) != 3 { return false diff --git a/cmd/user/delete.go b/cmd/user/delete.go index 115af95ed6d00cb70b8859b91c06d52d1f4ef051..07a0340d61402611802ed5a3e3d3a086cd219e81 100644 --- a/cmd/user/delete.go +++ b/cmd/user/delete.go @@ -36,15 +36,13 @@ func init() { } func deleteUserFunc(cmd *cobra.Command, args []string) error { + var opts model.Opts success := false - userLogin, err := getFlagString(cmd, "login") + err := opts.RetrieveOpts(cmd) if err != nil { return err } - confirm, err := cmd.Flags().GetBool("confirm") - if err != nil { return err } - - u, err := locateUser(userLogin) + u, err := locateUser(opts.UID) if err != nil { return err } defer func() { @@ -61,7 +59,7 @@ func deleteUserFunc(cmd *cobra.Command, args []string) error { fmt.Printf("Found %v\n\n", u.ToString()) - confirmationPrompt(confirm, "removal") + confirmationPrompt(opts.Confirm, "removal") err = removeDirs(u) if err != nil { return err } diff --git a/cmd/user/mod.go b/cmd/user/mod.go index 856ed39641477f00930aa3bb3d937929948613bc..5cfd58a24f0a6f282aa2bf02dab518857334d515 100644 --- a/cmd/user/mod.go +++ b/cmd/user/mod.go @@ -4,129 +4,113 @@ import ( "os" "fmt" "bufio" - "errors" "strings" + "os/exec" "crypto/rand" + "gopkg.in/yaml.v3" "github.com/spf13/cobra" "gitlab.c3sl.ufpr.br/tss24/useradm/model" ) +type cfg struct { + Name string `yaml:"name"` + GRR string `yaml:"grr"` + Group string `yaml:"group"` + Status string `yaml:"status"` + Shell string `yaml:"shell"` + Course string `yaml:"course"` + Resp string `yaml:"resp"` + Expiry string `yaml:"expiry"` +} + var ModCmd = &cobra.Command{ - Use: "mod", + Use: "mod [username]", Short: "Modify user information", + Args: cobra.ExactArgs(1), RunE: modifyUserFunc, } -func init() { - // possible flags - ModCmd.Flags().StringP("grr", "r", "", "New user GRR") - ModCmd.Flags().StringP("shell", "s", "", "New user shell") - ModCmd.Flags().StringP("login", "l", "", "User login name") - ModCmd.Flags().StringP("homedir", "d", "", "New user home") - ModCmd.Flags().BoolP ("unblock", "u", false, "Unblocks the user") - ModCmd.Flags().StringP("group", "g", "", "New user initial group") - ModCmd.Flags().StringP("path", "w", "", "Create Webdir, path/login") - ModCmd.Flags().StringP("name", "n", "", "New user full name (use quotes for spaces)") - ModCmd.Flags().StringP("password", "p", "", "New password in plain text (use \"auto\" to autogenerate)") - ModCmd.Flags().BoolP ("block", "b", false, "Block the user (changes the password and sets their shell to /bin/false)") - - // required flags - ModCmd.MarkFlagRequired("login") - - ModCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt") -} - func modifyUserFunc(cmd *cobra.Command, args []string) error { - confirm, err := cmd.Flags().GetBool("confirm") - if err != nil { - return err - } - block, err := cmd.Flags().GetBool("block") - if err != nil { - return err - } - unblock, err := cmd.Flags().GetBool("unblock") - if err != nil { - return err - } - userNameNew, err := cmd.Flags().GetString("name") - if err != nil { - return err - } - userGIDNew, err := cmd.Flags().GetString("group") - if err != nil { - return err - } - userGRRNew, err := cmd.Flags().GetString("grr") - if err != nil { - return err - } - userWebdirNew, err := cmd.Flags().GetString("path") - if err != nil { - return err - } - userUID, err := cmd.Flags().GetString("login") - if err != nil { - return err - } - userShellNew, err := cmd.Flags().GetString("shell") + var users []model.User + users, err := getUsers() if err != nil { - return err - } - userHomedirNew, err := cmd.Flags().GetString("homedir") - if err != nil { - return err - } - userPasswordNew, err := cmd.Flags().GetString("password") - if err != nil { - return err - } - if block && unblock { - return errors.New("Can't block and unblock at the same time!\n") - } - - var userStatusNew string - if block { - userShellNew = "/bin/false" - userStatusNew = "Blocked!" - userPasswordNew = "auto" - } else if unblock { - userShellNew = "/bin/bash" - userStatusNew = "Active" - userPasswordNew = "auto" - } - - userMod := model.User{ - UID: userUID, - GID: userGIDNew, - GRR: userGRRNew, - Name: userNameNew, - Shell: userShellNew, - Webdir: userWebdirNew, - Status: userStatusNew, - Homedir: userHomedirNew, - Password: userPasswordNew, - } - - if cmd.Flags().Changed("password") || block || unblock { - if userPasswordNew == "auto" { - userPasswordNew = genPassword() - userMod.Password = "[auto-generated]" - } else { - userMod.Password = "[explicitly set]" - } - } else { - userMod.Password = "[unchanged]" - } - - fmt.Println(userMod.ToString()) - - confirmationPrompt(confirm, "update") - - fmt.Println("Done!") - - fmt.Println(userPasswordNew) + return err + } + + login := args[0] + res := searchUser(users, false, login, "", "", "", "", "") + if len(res) >= 1 { + err = fmt.Errorf("More than one user found") + return err + } + u := res[0] + + state := cfg{ + Name: u.Name, + GRR: u.GRR, + Group: u.GID, + Status: u.Status, + Shell: u.Shell, + Course: u.Course, + Resp: u.Resp, + Expiry: u.Expiry, + } + + // Criar arquivo temporário + tmpFile, err := os.CreateTemp("", "config-*.yaml") + if err != nil { + err = fmt.Errorf("Error trying to create temp file:", err) + return err + } + defer os.Remove(tmpFile.Name()) + + // Serializar apenas os campos editáveis + data, err := yaml.Marshal(state) + if err != nil { + err = fmt.Errorf("Error serializing the yaml:", err) + return err + } + + // Escrever no arquivo temporário + if err := os.WriteFile(tmpFile.Name(), data, 0644); err != nil { + err = fmt.Errorf("Error writing to temp file:", err) + return err + } + + // Abrir no editor + editor := os.Getenv("EDITOR") + if editor == "" { + editor = "nano" + } + + comd := exec.Command(editor, tmpFile.Name()) + comd.Stdin = os.Stdin + comd.Stdout = os.Stdout + comd.Stderr = os.Stderr + + if err := comd.Run(); err != nil { + err = fmt.Errorf("Error opening the editor:", err) + return err + } + + // Ler o conteúdo editado + editedData, err := os.ReadFile(tmpFile.Name()) + if err != nil { + err = fmt.Errorf("Error reading the modified file:", err) + return err + } + + // Desserializar apenas os campos editáveis + var newState cfg + if err := yaml.Unmarshal(editedData, &newState); err != nil { + err = fmt.Errorf("Error de-serializing the yaml:", err) + return err + } + + // Exibir a struct atualizada + fmt.Println("New config:") + fmt.Printf("%+v\n", newState) return nil } @@ -158,6 +142,3 @@ func confirmationPrompt(confirm bool, operation string) { } } } - - - diff --git a/go.mod b/go.mod index 3f874a8060217d11cd03c2516afd52e0388edd66..0b1c8cbc5552913ab98c55cbfce966aa10a71064 100644 --- a/go.mod +++ b/go.mod @@ -14,4 +14,5 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/crypto v0.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/model/opts.go b/model/opts.go new file mode 100644 index 0000000000000000000000000000000000000000..9f56116e54615d217e54da60607bef3b63bedad9 --- /dev/null +++ b/model/opts.go @@ -0,0 +1,138 @@ +package model + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +type Opts struct { + GRR string + GID string + UID string + Resp string + Name string + Nobkp string + Ltype string + Shell string + Passwd string + Webdir string + Status string + Course string + Expiry string + Homedir string + Block bool + Unblock bool + Confirm bool +} + +func (o *Opts) ToString() string { + return fmt.Sprintf(`Opts: + GRR %s + GID %s + UID %s + Resp %s + Name %s + Nobkp %s + Ltype %s + Shell %s + Passwd %s + Webdir %s + Status %s + Course %s + Expiry %s + Homedir %s + Block %v + Unblock %v + Confirm %v`, + o.GRR, o.GID, o.UID, o.Resp, o.Name, o.Nobkp, o.Ltype, o.Shell, + o.Passwd, o.Webdir, o.Status, o.Course, o.Expiry, o.Homedir, + o.Block, o.Unblock, o.Confirm) +} + + +func (o *Opts) RetrieveOpts(cmd *cobra.Command) error { + var err error + + o.GRR, err = getFlagString(cmd, "grr") + if err != nil { return err } + + o.Resp, err = getFlagString(cmd, "resp") + if err != nil { return err } + + o.Name, err = getFlagString(cmd, "name") + if err != nil { return err } + + o.GID, err = getFlagString(cmd, "group") + if err != nil { return err } + + o.UID, err = getFlagString(cmd, "login") + if err != nil { return err } + + o.Ltype, err = getFlagString(cmd, "type") + if err != nil { return err } + + o.Shell, err = getFlagString(cmd, "shell") + if err != nil { return err } + + o.Webdir, err = getFlagString(cmd, "path") + if err != nil { return err } + + o.Nobkp, err = getFlagString(cmd, "nobkp") + if err != nil { return err } + + o.Status, err = getFlagString(cmd, "status") + if err != nil { return err } + + o.Course, err = getFlagString(cmd, "course") + if err != nil { return err } + + o.Expiry, err = getFlagString(cmd, "expiry") + if err != nil { return err } + + o.Passwd, err = getFlagString(cmd, "passwd") + if err != nil { return err } + + o.Homedir, err = getFlagString(cmd, "homedir") + if err != nil { return err } + + o.Confirm, err = getFlagBool(cmd, "confirm") + if err != nil { return err } + + o.Unblock, err = getFlagBool(cmd, "unblock") + if err != nil { return err } + + o.Block, err = getFlagBool(cmd, "block") + if err != nil { return err } + + return nil +} + +// since this model is used in most subcommands, some +// flags may exist in one command but not in another, +// so if it doesn't exist just set as empty before +// the error occurs, it won't be used anyway... :D + +func getFlagString(cmd *cobra.Command, flag string) (string, error) { + if cmd.Flags().Lookup(flag) == nil { + return "", nil + } + + flagValue, err := cmd.Flags().GetString(flag) + if err != nil { + return "", fmt.Errorf("failed to get flag %q: %w", flag, err) + } + return flagValue, nil +} + +func getFlagBool(cmd *cobra.Command, flag string) (bool, error) { + if cmd.Flags().Lookup(flag) == nil { + return false, nil + } + + flagValue, err := cmd.Flags().GetBool(flag) + if err != nil { + return false, fmt.Errorf("failed to get flag %q: %w", flag, err) + } + return flagValue, nil +}