diff --git a/.gitignore b/.gitignore index 590d1aa9543349f694ad6251ff3704e6ef913638..bb199913df7f9c45e837bafcd9c752eee2b54b4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ useradm useradm.py -full_commands.py diff --git a/cmd/user/create.go b/cmd/user/create.go index 6f4dbef83654b18799ca8bff3bfbff00cafd4630..540dc546e76e0d39901681d38b74c60e1f80a711 100644 --- a/cmd/user/create.go +++ b/cmd/user/create.go @@ -9,20 +9,10 @@ import ( "gitlab.c3sl.ufpr.br/tss24/useradm/utils" ) -const ( - MAIL_ALIAS_FILE = "/etc/aliases" // aliases db path for query - DEF_PERMISSION = "0700" // default user dir permission - BLK_PERMISSION = "0000" // user dir permission if blocked - HTML_BASE_PERM = "0755" // set public_html to this on creation - MAX_VARIANCE = 200 // tries made to create login automatically - MIN_UID = 1000 - MAX_UID = 6000 -) - var CreateCmd = &cobra.Command{ Use: "create", Short: "Create new user", - RunE: createUserFunc, + RunE: createUserCmd, } func init() { @@ -49,7 +39,7 @@ func init() { CreateCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt") } -func createUserFunc(cmd *cobra.Command, args []string) error { +func createUserCmd(cmd *cobra.Command, args []string) error { l, err := model.ConnLDAP() if err != nil { return err @@ -76,6 +66,11 @@ func createUserFunc(cmd *cobra.Command, args []string) error { return err } + err = utils.ClearCache() + if err != nil { + fmt.Printf("Failed to reload cache, changes might take a while\nError: %v", err) + } + fmt.Println("User created!") fmt.Printf("\nUser Login: %v", usr.UID) fmt.Printf("\nUser Password: %v\n\n", usr.Password) @@ -90,22 +85,22 @@ func createNewUserModel(cmd *cobra.Command, l *ldap.Conn) (model.User, bool, err var u model.User var opts model.Opts - users, err := model.GetAllUsersLDAP(l) + err := opts.RetrieveOpts(cmd) if err != nil { return u, false, err } - groups, err := model.GetAllGroupsLDAP(l) - if err != nil { + if err := validateInputs(opts); err != nil { return u, false, err } - err = opts.RetrieveOpts(cmd) + users, err := model.GetAllUsersLDAP(l) if err != nil { return u, false, err } - if err := validateInputs(opts); err != nil { + groups, err := model.GetAllGroupsLDAP(l) + if err != nil { return u, false, err } @@ -117,21 +112,6 @@ func createNewUserModel(cmd *cobra.Command, l *ldap.Conn) (model.User, bool, err return u, false, fmt.Errorf("GRR is required for \"ini\" login type") } - opts.Homedir, err = utils.GenDirPath("/home", opts.GID, opts.UID, opts.Homedir) - if err != nil { - return u, false, err - } - - opts.Nobkp, err = utils.GenDirPath("/nobackup", opts.GID, opts.UID, opts.Nobkp) - if err != nil { - return u, false, err - } - - opts.Webdir, err = utils.GenDirPath("/home/html/inf", "", opts.UID, opts.Webdir) - if err != nil { - return u, false, err - } - u = model.User{ UID: opts.UID, GID: opts.GID, @@ -156,6 +136,27 @@ func createNewUserModel(cmd *cobra.Command, l *ldap.Conn) (model.User, bool, err } } + if opts.Homedir == "" { + u.Homedir, err = utils.GenDirPath("/home", u.GID, u.UID, u.Homedir) + if err != nil { + return u, false, err + } + } + + if opts.Nobkp == "" { + u.Nobackup, err = utils.GenDirPath("/nobackup", u.GID, u.UID, u.Nobackup) + if err != nil { + return u, false, err + } + } + + if opts.Webdir == "" { + u.Webdir, err = utils.GenDirPath("/home/html/inf", "", u.UID, u.Webdir) + if err != nil { + return u, false, err + } + } + // get a new UIDNumber err = u.GetNewUIDNumber(l) if err != nil { diff --git a/cmd/user/mod.go b/cmd/user/mod.go index fbb397f1635cf04a9346a973434879c2c560cf29..4b9e9f307d317319c1d0f3b5eb181534bd81a81e 100644 --- a/cmd/user/mod.go +++ b/cmd/user/mod.go @@ -1,5 +1,6 @@ package user +// FIXME: mudanças não são mostradas kkkkkk import ( "fmt" "os" @@ -25,16 +26,17 @@ type cfg struct { var ModCmd = &cobra.Command{ Use: "mod [username]", Short: "Modify user information", - Long: "Opens a file for editing the users config. Uses $EDITOR variable", - Args: cobra.ExactArgs(1), - RunE: modifyUserFunc, + Long: `Opens a file for editing the users config. +Uses $EDITOR variable, if it is not set, use vim`, + Args: cobra.ExactArgs(1), + RunE: modUserCmd, } func init() { ModCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt") } -func modifyUserFunc(cmd *cobra.Command, args []string) error { +func modUserCmd(cmd *cobra.Command, args []string) error { var opts model.Opts l, err := model.ConnLDAP() @@ -216,7 +218,7 @@ func promptUserYaml(state cfg) (cfg, error) { // abrir o editor editor := os.Getenv("EDITOR") if editor == "" { - editor = "nano" + editor = "vim" } comd := exec.Command(editor, tmpFile.Name()) diff --git a/cmd/user/remove.go b/cmd/user/remove.go index 24fbf44d20c6133f64ae8b51b6ce11be924b38c1..20246b6c2618adbd8a419c0134fb2e7725e89650 100644 --- a/cmd/user/remove.go +++ b/cmd/user/remove.go @@ -4,33 +4,24 @@ import ( "fmt" "log" "path/filepath" - "strconv" - "time" "github.com/spf13/cobra" "gitlab.c3sl.ufpr.br/tss24/useradm/model" "gitlab.c3sl.ufpr.br/tss24/useradm/utils" ) -var ( - ANO = strconv.Itoa(time.Now().Year()) - NO_BKP_TRASH = "/nobackup/contas_removidas/" + ANO - HOME_TRASH = "/home/contas_removidas/" + ANO - WEB_TRASH = "/home/contas_removidas/html/" + ANO -) - var RemoveCmd = &cobra.Command{ Use: "remove [username]", Short: "Delete a user", Args: cobra.ExactArgs(1), - RunE: removeUserFunc, + RunE: removeUserCmd, } func init() { RemoveCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt") } -func removeUserFunc(cmd *cobra.Command, args []string) error { +func removeUserCmd(cmd *cobra.Command, args []string) error { var opts model.Opts success := false @@ -54,16 +45,16 @@ func removeUserFunc(cmd *cobra.Command, args []string) error { defer func() { if !success { log.Println("Found error, rolling back dirs...") - _ = utils.MoveAndChown(filepath.Join(NO_BKP_TRASH, + _ = utils.MoveAndChown(filepath.Join(model.NO_BKP_TRASH, filepath.Base(u.Nobackup)), u.Nobackup, u.UID, u.GID) - _ = utils.MoveAndChown(filepath.Join(HOME_TRASH, + _ = utils.MoveAndChown(filepath.Join(model.HOME_TRASH, filepath.Base(u.Homedir)), u.Homedir, u.UID, u.GID) - _ = utils.MoveAndChown(filepath.Join(WEB_TRASH, + _ = utils.MoveAndChown(filepath.Join(model.WEB_TRASH, filepath.Base(u.Webdir)), u.Webdir, u.UID, u.GID) } }() - fmt.Printf("Found %v\n\n", u.ToString()) + fmt.Printf("Found %v\n\n", u.FullToString()) utils.ConfirmationPrompt(opts.Confirm, "removal") diff --git a/cmd/user/reset.go b/cmd/user/reset.go index aea0b262e3c27b5590f99b9febca4c895c5f2390..22cf5da32391ff42961ff3b96e8dd5d3590228d4 100644 --- a/cmd/user/reset.go +++ b/cmd/user/reset.go @@ -12,14 +12,14 @@ var ResetCmd = &cobra.Command{ Use: "reset [username]", Short: "Reset the password of a user", Args: cobra.ExactArgs(1), - RunE: resetPass, + RunE: resetPassCmd, } func init() { ResetCmd.Flags().StringP("passwd", "p", "", "User's new password") } -func resetPass(cmd *cobra.Command, args []string) error { +func resetPassCmd(cmd *cobra.Command, args []string) error { pass, err := cmd.Flags().GetString("passwd") if err != nil { return err @@ -35,17 +35,12 @@ func resetPass(cmd *cobra.Command, args []string) error { pass = utils.GenPassword() } - users, err := model.GetAllUsersLDAP(l) + login := args[0] + _, err = model.Locate(l, login) if err != nil { return err } - login := args[0] - res := model.Search(users, false, true, login, "", "", "", "", "") - if len(res) != 1 { - return fmt.Errorf("More than one user found") - } - utils.ConfirmationPrompt(false, "password reset") err = model.ModKRBPassword(login, pass) if err != nil { diff --git a/cmd/user/temp.go b/cmd/user/temp.go index cd76e7b020656c6721d779c45432fc22b4b5c641..7170e146b1781e0fee61c98c516e14feed89db4d 100644 --- a/cmd/user/temp.go +++ b/cmd/user/temp.go @@ -56,8 +56,9 @@ func tempCreate(cmd *cobra.Command, args []string) error { } for i := 1; i <= opts.Number; i++ { - if model.LoginExists(users, opts.UID+strconv.Itoa(i)) { - return fmt.Errorf("User found with login %v%v, won't overwrite", opts.UID, i) + login := opts.UID + strconv.Itoa(i) + if model.LoginExists(users, login) { + return fmt.Errorf("User found with login %v, won't overwrite", login) } } @@ -113,6 +114,8 @@ func tempCreate(cmd *cobra.Command, args []string) error { } } + _ = utils.ClearCache() + success = true return nil } @@ -137,7 +140,7 @@ func createTempUser(base model.User, num int) error { base.UID = base.UID + numstring // gen dn - base.DN = "uid=" + base.UID + ",ou=usuarios,dc=c3local" + base.SetDN(base.UID) // no webdir for temps @@ -168,27 +171,7 @@ func createTempUser(base model.User, num int) error { fmt.Printf("Pronto para criar:\n%v\n\n", base.FullToString()) utils.ConfirmationPrompt(false, "creation") - // TODO: change for base.Create() - // create ldap - err = base.AddToLDAP(l) - if err != nil { - return err - } - - // create kerberos - err = model.CreateKRBPrincipal(base.UID) - if err != nil { - return err - } - - // change pass - err = model.ModKRBPassword(base.UID, base.Password) - if err != nil { - return err - } - - // create dirs - err = base.CreateDirs() + err = base.Create(l) if err != nil { return err } diff --git a/model/krb.go b/model/krb.go index 2c9493b4c2cb49660b55b33385d6e8be218b6726..8f9054659da3f08a4656c250cf91eb639498902d 100644 --- a/model/krb.go +++ b/model/krb.go @@ -1,8 +1,10 @@ package model import ( + "bytes" "fmt" "os/exec" + "regexp" ) // creates a KerberosPrincipal for the user @@ -19,16 +21,34 @@ func CreateKRBPrincipal(login string) error { } // command that changes the password >:D -// the command kadmin.local returns 0 if the password change -// fails, bruh. so we have to check differently. -// FIXME: maybe do the validation with regex? +// the command kadmin.local returns 0 if the password +// change fails, bruh. so we have to check differently. +// in case you find another error message, please include it here :) func ModKRBPassword(login, password string) error { cmd := exec.Command("kadmin.local", "-q", fmt.Sprintf("cpw -pw %s %s", password, login)) - output, _ := cmd.CombinedOutput() - if len(output) > 105 { - return fmt.Errorf("Error found changing password, output: \n%v", string(output[:])) + var output bytes.Buffer + cmd.Stdout = &output + cmd.Stderr = &output + + err := cmd.Run() + outStr := output.String() + + // made like this so it is easy to add more :] + failPatt := []string{ + "does not contain enough character", + "is too short", + } + + for _, pattern := range failPatt { + if match, _ := regexp.MatchString(pattern, outStr); match { + return fmt.Errorf("Failed to change Kerberos password: \n%s", outStr) + } + } + + if err != nil { + return fmt.Errorf("Command execution failed: %v\nOutput:\n%s", err, outStr) } return nil diff --git a/model/ldap.go b/model/ldap.go index 4e430b5557d3a4af14615e4de8ac52b1504264eb..10e6f82dc6dc364ebfa0667d0dd31be0e3eda3e0 100644 --- a/model/ldap.go +++ b/model/ldap.go @@ -39,6 +39,7 @@ func ConnLDAP() (*ldap.Conn, error) { return l, nil } +// reads the password file in urquell func getPasswordLDAP(path string) (string, error) { passwd, err := os.ReadFile(path) if err != nil { @@ -86,7 +87,7 @@ func DelFromLDAP(UID string) error { // search for all groups the user is a member of searchReq := ldap.NewSearchRequest( - "ou=grupos,dc=c3local,dc=com", + "ou=grupos,dc=c3local", ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(memberUid="+UID+")", // Filter by UID membership @@ -108,7 +109,7 @@ func DelFromLDAP(UID string) error { } // removing user entry - userDN := "uid=" + UID + "ou=usuarios,dc=c3local" + userDN := "uid=" + UID + ",ou=usuarios,dc=c3local" delReq := ldap.NewDelRequest(userDN, nil) if err := l.Del(delReq); err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { diff --git a/model/ltype.go b/model/ltype.go index 6c3eb107b2b3f5a65be6773affb9ac5b31d93cd0..4fbbc0f528490e309bdcce911c498f1b840d5010 100644 --- a/model/ltype.go +++ b/model/ltype.go @@ -28,11 +28,11 @@ func (lt LoginType) String() string { // converts to Ltype func (lt *LoginType) Parse(s string) { switch strings.ToLower(s) { - case "initials": + case "initials", "ini": *lt = Initials - case "firstname": + case "firstname", "first": *lt = FirstName - case "lastname": + case "lastname", "last": *lt = LastName default: *lt = LoginTypeUnknown diff --git a/model/user.go b/model/user.go index ade5235564d6bfae737f845e7d2c8aade7031ab9..45490f60a60f689d47720c57ed0e70b5034652b4 100644 --- a/model/user.go +++ b/model/user.go @@ -90,6 +90,7 @@ func (u *User) Create(l *ldap.Conn) error { defer func() { if !success { + fmt.Printf("Error found, deleting user...\n") _ = DelKRBPrincipal(u.UID) _ = DelFromLDAP(u.UID) } @@ -150,7 +151,7 @@ func Locate(l *ldap.Conn, login string) (User, error) { } if !LoginExists(users, login) { - return u, fmt.Errorf("Failed to find login in LDAP database: %v", err) + return u, fmt.Errorf("No such user!") } filter := Search(users, false, true, login, "", "", "", "", "") @@ -165,7 +166,7 @@ search made: "useradm user show -l %v -e"`, login) // DN formatting func (u *User) SetDN(UID string) { - u.DN = "uid=" + UID + "ou=usuarios,dc=c3local" + u.DN = "uid=" + UID + ",ou=usuarios,dc=c3local" } // finds next available uidNumber (MEX from uid group) diff --git a/utils/utils.go b/utils/utils.go index 464987aa0cf383c93c50dc5adbe4765437058a5d..81e340ddd04af987b6f447baff6c52fb84e60458 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -14,7 +14,6 @@ import ( const ( MAIL_ALIAS_FILE = "/etc/aliases" - PASSWD_PATH = "/etc/ldapscripts/ldapscripts.passwd" ) // generic function for filtering data types, in this case the model User @@ -78,7 +77,7 @@ func GenDirPath(base, group, login, input string) (string, error) { p := filepath.Join(base, group, login) if PathExists(p) { - return p, fmt.Errorf("Path already exists") + return p, fmt.Errorf("Path %v already exists: %v, %v, %v, %v", p, base, group, login, input) } return p, nil @@ -123,7 +122,7 @@ func MoveAndChown(orig, dest, owner, group string) error { destPath := filepath.Join(dest, filepath.Base(orig)) if _, err := os.Stat(destPath); err == nil { - return fmt.Errorf("Directory %v already exists, can't move\n", destPath) + return fmt.Errorf("Destiny directiory %v already exists, won't overwrite!\n", destPath) } // move directory @@ -156,7 +155,7 @@ func ConfirmationPrompt(confirm bool, operation string) { } } -// LDAP has a cache... +// LDAP has a cache, so this should reload it func ClearCache() error { cmd := exec.Command("nscd", "-i", "passwd") @@ -181,6 +180,7 @@ func IsValidDate(arr []string) bool { mth, err2 := strconv.Atoi(arr[1]) year, err3 := strconv.Atoi(arr[2]) + // can't convert --> something's wrong lol if err1 != nil || err2 != nil || err3 != nil { return false }